NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #157 most downloaded on PyPI
Tools to manipulate font files
Last release 6 days ago
23 Sep 2026
Release timing varies
gaps range from 9 days to 4 months
Nearly every release is documented
notes for 59 of the last 60 stable releases
2 versions withdrawn
withdrawn after publishing
13 years old
237 releases · first in 2013
[avar2] Added experimental support for reading/writing avar version 2 as specified in this draft proposal: https://github.com/harfbuzz/boring-expansio
STAT table that was built with feaLib from FEA feature file. Also, added support for building multiple VFs defined in Designspace v5 from fonttools varLib script (https://github.com/fonttools/fonttools/pull/3024).Debg table with lookup names when FONTTOOLS_LOOKUP_DEBUGGING env variable is set (https://github.com/fonttools/fonttools/pull/3023).One column per quarter.
[avar2] Added experimental support for reading/writing avar version 2 as specified in this draft proposal: https://github.com/harfbuzz/boring-expansion-spec/blob/main/avar2.md
[glifLib] Wrap underlying XML library exceptions with GlifLibError when parsing GLIFs, and also print the name and path of the glyph that fails to be parsed (#3042).
[feaLib] Consult avar for normalizing user-space values in ConditionSets and in VariableScalars (#3042, #3043).
[ttProgram] Handle string input to Program.fromAssembly() (#3038).
[otlLib] Added a config option to emit GPOS 7 lookups, currently disabled by default because of a macOS bug (#3034).
[COLRv1] Added method to automatically compute ClipBoxes (#3027).
[ttFont] Fixed getGlyphID to raise KeyError on missing glyphs instead of returning None. The regression was introduced in v4.27.0 (#3032).
[sbix] Fixed UnboundLocalError: cannot access local variable 'rawdata' (#3031).
[varLib] When building VF, do not overwrite a pre-existing STAT table that was built with feaLib from FEA feature file. Also, added support for building multiple VFs defined in Designspace v5 from fonttools varLib script (#3024).
[mtiLib] Only add Debg table with lookup names when FONTTOOLS_LOOKUP_DEBUGGING env variable is set (#3023).
[mtiLib] Optionally add Debg debug info for MTI feature builds (#3018).
Debg debug info for MTI feature builds (#3018).- character, similar to existing -o - option to write output to standard output (#3020).cython.compiled raise AttributeError if cython not installed properly (#3017).debug parameter to addOpenTypeFeatures method to add source debugging information to the font in the Debg private table (#3008).__lt__ comparison not fail on Unicode encoding errors (#3006).overlayBox (#3003, #3005).addVarComponent method to pen protocols' base classes, which pens can implement to handle varcomponents (by default they get decomposed).__getstate__ for BaseTable so that a copied/pickled 'lazy' object gets its own OTTableReader to read from; incidentally fixes a bug while subsetting COLRv1 table containing ClipBoxes on python 3.11 (#2965, #2968).endPointsOfContours field should be unsigned! Kudos to behdad for spotting one of the oldest bugs in FT. Probably nobody has ever dared to make glyphs with more than 32767 points... (#2957).ignore statements with unmarked glyphs to match makeotf behavior, which assumes the first glyph is marked (#2950).black and enforce new code style via CI check (#2925).Nothing published for this version
[varLib.instancer] Added support for L4 instancing, i.e. moving the default value of an axis while keeping it variable. Thanks Behdad! (#2728, #2861).
[subset] Keep nameIDs used by CPAL palette entry labels (#2837).
Fix arguments in calls to (glyf) glyph.draw() and drawPoints(), whereby offset wasn't correctly passed down; this fix also exposed a second bug, where
[subset] Keep CPAL table and don't attempt to prune unused color indices if OT-SVG table is present even if COLR table was subsetted away; OT-SVG may
--no-lazy to optionally load fonts eagerly (mostly to ease debugging of table lazy loading, no practical effects) (https://github.com/fonttools/fonttools/pull/2807).VarScalar by caching models for recurring master locations (https://github.com/fonttools/fonttools/pull/2798).feaLib.lexer, speeds up parsing FEA a bit (https://github.com/fonttools/fonttools/pull/2799).post legacy format 1 is malformed/improperly used (https://github.com/fonttools/fonttools/pull/2786)normalizeLocation method to normalize a location dict from the font's defined axes space (also known as "user space") into the normalized (-1..+1) space. It applies avar mapping if the font contains an avar table (https://github.com/fonttools/fonttools/pull/2789).statShape.py script to draw statistical shape of a glyph as an ellips (requires pycairo) (https://github.com/fonttools/fonttools/commit/baecd8850b878b1cc5ccf140d884754c4b8d2e9b).[subset] Keep CPAL table and don't attempt to prune unused color indices if OT-SVG table is present even if COLR table was subsetted away; OT-SVG may be referencing the CPAL table; for now we assume that's the case (#2814, #2815).
[varLib.instancer] Downgrade GPOS/GSUB version if there are no more FeatureVariations after instancing (#2812).
[subset] Added --no-lazy to optionally load fonts eagerly (mostly to ease debugging of table lazy loading, no practical effects) (#2807).
[varLib] Avoid building empty COLR.DeltaSetIndexMap with only identity mappings (#2803).
[feaLib] Allow multiple value record types (by promoting to the most general format) within the same PairPos subtable; e.g. this allows variable and non variable kerning rules to share the same subtable. This also fixes a bug whereby some kerning pairs would become unreachable while shapiong because of premature subtable splitting (#2772, #2776).
[feaLib] Speed up VarScalar by caching models for recurring master locations (#2798).
[feaLib] Optionally cythonize feaLib.lexer, speeds up parsing FEA a bit (#2799).
[designspaceLib] Avoid crash when handling unbounded rule conditions (#2797).
[post] Don't crash if post legacy format 1 is malformed/improperly used (#2786)
[gvar] Don't be "lazy" (load all glyph variations up front) when TTFont.lazy=False (#2771).
[TTFont] Added normalizeLocation method to normalize a location dict from the font's defined axes space (also known as "user space") into the normalized (-1..+1) space. It applies avar mapping if the font contains an avar table (#2789).
[TTVarGlyphSet] Support drawing glyph instances from CFF2 variable glyph set (#2784).
[fontBuilder] Do not error when building cmap if there are zero code points (#2785).
[varLib.plot] Added ability to plot a variation model and set of accompaning master values corresponding to the model's master locations into a pyplot figure (#2767).
[Snippets] Added statShape.py script to draw statistical shape of a glyph as an ellips (requires pycairo) (baecd88).
[TTVarGlyphSet] implement drawPoints natively, avoiding going through SegmentToPointPen (#2778).
[TTVarGlyphSet] Fixed bug whereby drawing a composite glyph multiple times, its components would shif; needed an extra copy (#2774).
[subset] Fixed regression introduced with v4.37.0 while subsetting the VarStore of HVAR and VVAR tables, whereby an AttributeError: subset_varidxes wa
HVAR and VVAR tables, whereby an AttributeError: subset_varidxes was thrown because an apparently unused import statement (with the side-effect of dynamically binding that subset_varidxes method to the VarStore class) had been accidentally deleted in an unrelated PR (#2769, #2773).cairoPen (#2768).gvar more lazily by not parsing all of the glyf table (#2771).drawPoints(pointPen) method work for CFF fonts as well via adapter pen (#2770).[varLib.models] Reverted PR https://github.com/fonttools/fonttools/pull/2717 which added support for "narrow tents" in v4.36.0, as it introduced a reg
blend operator (https://github.com/fonttools/fonttools/pull/2750, https://github.com/fonttools/fonttools/issues/1975)._TTVarGlyphSet inherits from _TTGlyphSet to keep backward compatibility with existing API (https://github.com/fonttools/fonttools/pull/2762).fontTools.misc.visitor and fontTools.ttLib.ttVisitor. Added fontTools.ttLib.scaleUpem module that uses the latter to change a font's units-per-em and scale all the related fields accordingly (https://github.com/fonttools/fonttools/pull/2718, https://github.com/fonttools/fonttools/pull/2755).[varLib.models] Reverted PR #2717 which added support for "narrow tents" in v4.36.0, as it introduced a regression (#2764, #2765). It will be restored in upcoming release once we found a solution to the bug.
[cff.specializer] Fixed issue in charstring generalizer with the blend operator (#2750, #1975).
[varLib.models] Added support for extrapolation (#2757).
[ttGlyphSet] Ensure the newly added _TTVarGlyphSet inherits from _TTGlyphSet to keep backward compatibility with existing API (#2762).
[kern] Allow compiling legacy kern tables with more than 64k entries (d21cfdede).
[visitor] Added new visitor API to traverse tree of objects and dispatch based on the attribute type: cf. fontTools.misc.visitor and fontTools.ttLib.ttVisitor. Added fontTools.ttLib.scaleUpem module that uses the latter to change a font's units-per-em and scale all the related fields accordingly (#2718, #2755).
[varLib.models] Use a simpler model that generates narrower "tents" (regions, master supports) whenever possible: specifically when any two axes that
GDEF not being built when explicitly not requested; don't build one unconditonally even when not requested (#2744, also works around #2747).TTFont.getGlyphSet method now supports selecting a location that represents an instance of a variable font (supports both user-scale and normalized axes coordinates via the normalized=False parameter). Currently this only works for TrueType-flavored variable fonts (#2738).[otData/otConverters] Added support for 'biased' PaintSweepGradient start/end angles to match latest COLRv1 spec (#2743).
_instantiateFeatureVariations when at the same time pinning one axis and restricting the range of a subsequent axis; the wrong axis tag was being used in the latter step (as the records' axisIdx was updated in the preceding step but looked up using the old axes order in the following step) (#2733, #2734).'.' instead of '#' in duplicate glyph names (#2742).build_many, we forgot to pass on colr_layer_reuse parameter to the build method (#2730).glyphsLib).varLib.iup (35% faster), pens.momentsPen (makes varLib.interpolatable 3x faster).AttributeError when uharfbuzz < 0.23.0 and repack method is missing (32aa8eaf).uharfbuzz.repack_with_tag when available (since uharfbuzz>=0.30.0), enables table-specific optimizations to be performed during repacking (#2724).NotANone variable (#2714).Fixed typo in varLib/merger.py that causes NameError merging COLR glyphs containing more than 255 layers (#2685).
[designspaceLib] Don't make up bad PS names when no STAT data
[varStore/subset] fixed KeyError exception to do with NO_VARIATION_INDEX while subsetting varidxes in GPOS/GDEF (a08140d).
[instancer] When optimizing HVAR/VVAR VarStore, use_NO_VARIATION_INDEX=False to avoid including NO_VARIATION_INDEX in AdvWidthMap, RsbMap, LsbMap mapp
[instancer] Set RIBBI bits in head and OS/2 table when cutting instances and the subfamily nameID=2 contains strings like 'Italic' or 'Bold' (#2673).
compressed field, and no longer a bare 3-tuple (#2645).[designspaceLib] Fixed typo in deepcopyExceptFonts method, preventing font references to be transferred (#2600). Fixed another typo in the name of Ran
deepcopyExceptFonts method, preventing font references to be transferred (#2600).Range dataclass's __post_init__ magic method (#2597).[otBase] Make logging less verbose when harfbuzz fails to serialize (#2593). Do not exit at the first failure but continue attempting to fix offset ov
USE_HARFBUZZ_REPACKER option was explicitly set to True. This is normal with fonts with relatively large tables, at least until hb.repack implements proper table splitting.…introduced with v4.33.0 (#2592, #2593). This is deprecated and will be removed once ufo2ft gets updated to use the new config setup.
FONTTOOLS_GPOS_COMPACT_MODE environment variable to fix regression in ufo2ft (and thus fontmake) introduced with v4.33.0 (#2592, #2593).Allow instances to have the same location as a previously defined STAT label. Deprecated some attributes: SourceDescriptor: copyLib, copyInfo, copyGro…
OS/2.xAvgCharWidth after merging fonts with fontTools.merge (#2591, #2538).fontTools.misc.configTools module, a generic configuration system (#2416, #2349).fontTools.config module, a fontTools-specific configuration system using configTools above.Config object to TTFont.fontTools.designspaceLib.split module to split a designspace into sub-spaces that interpolate and that represent the variable fonts listed in the document.fontTools.designspaceLib.statNames module.SourceDescriptor: copyLib, copyInfo, copyGroups, copyFeatures.InstanceDescriptor: kerning, info; glyphs: use rules or sparse sources.location: use the more explicit designLocation.build_many to build several variable fonts from a single designspace document (#2436).fontTools.varLib.stat module to build STAT tables from a designspace document.uharfbuzz python bindings are available (#2552). Disable it by setting the "fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER" config option to False. If the option is set explicitly to True but uharfbuzz can't be imported or fails to serialize for any reasons, an error will be raised (ImportError or uharfbuzz errors).pen.closePath() gets called for CFF2 charstrings (#2577).
Handle implicit CFF2 closePath within T2OutlineExtractor (#2580).[otlLib] Disable GPOS7 optimization to work around bug in Apple CoreText. Always force Chaining GPOS8 for now (#2540).
outputImpliedClosingLine=False parameter to Glyph.draw(), to control behaviour of PointToSegmentPen (6b4e2e7).GlobalState class and fix calls to TopDictIndex() (#2569, #2570).AxisValueArray if it is empty after instantiating (#2563).[varLib] fix instantiation of GPOS SinglePos values (#2555).
[subset] fix subsetting OT-SVG when glyph id attribute is on the root element (#2553).
<svg> element (#2553).[ttCollection] Fixed 'ResourceWarning: unclosed file' warning (#2549).
ensureDecompiled method to load all tables irrespective of the lazy attribute (#2551).iterSubTable method to iterate over BaseTable's children of type BaseTable; useful for traversing a tree of otTables (#2551).[varLib] Added debug logger showing the glyph name for which gvar is built (#2542).
gvar is built (#2542).FoundANone and UnsupportedFormat exceptions (ac4d5611).windowsNames and macNames (bool) parameters to the buildStatTable function, so that one can select whether to only add one or both of the two sets (#2528).getFirstDebugName, getBest{Family,SubFamily,Full}Name methods (#2526).[colorLib] Fixed rounding issue with radial gradient's start/end circles inside one another (#2521).
[ufoLib] Fixed illegal characters and expanded reserved filenames (#2506).
waitForKeyPress method on Windows (#2509).freetype-py and the pen protocol for rasterizating outline paths (#2494).fontName in suckfont (#2496).[svgPathPen] Continuation of #2471: make sure all occurrences of str() are now replaced with user-defined ntos callable.
str() are now replaced with user-defined ntos callable.[merge] Merge GDEF marksets in Lookups properly (#2474).
fontTools feaLib script exit with error code when build fails (#2459)ntos option to customize number formatting (e.g. rounding) (#2471).[subset] Fixed bug while subsetting COLR table, whereby incomplete layer records pointing to missing glyphs were being retained leading to struct.erro
COLR table, whereby incomplete layer records pointing to missing glyphs were being retained leading to struct.error upon compiling. Make it so that glyf glyph closure, which follows the COLR glyph closure, does not influence the COLR table subsetting (#2461, #2462).cmap and glyf tables (#2454, #2457).[otlLib] Remove duplicates when building coverage (#2433).
calcChecksum (#2448).--pretty-svg option to pretty print SVG table contents (#2452).CFF tables (CID-keyed CFF is still not supported) (#2447).--output-file (#2447).fontTools.ttLib.ttFont (#2442).[subset] Fixed AttributeError while traversing a color glyph's Paint graph when there is no LayerList, which is optional (#2441).
AttributeError while traversing a color glyph's Paint graph when there is no LayerList, which is optional (#2441).[OT-SVG] Removed support for old deprecated version 1 and embedded color palettes, which were never officially part of the OpenType SVG spec. Upon com…
None (empty) (#2424, 2439).[otlLib] Fixed error when chained contextual lookup builder overflows (#2404, #2411).
t for a point lying on an almost horizontal/vertical line; another when computing the intersection point between a curve and a line (#2413).[py23] Dropped internal use of fontTools.py23 module to fix deprecation warnings in client code that imports from fontTools (#2232, #2399, #2400).
Coverage and ClassDef readers; removed unused allowVID argument from TTFont constructor, and requireReal argument in TTFont.getGlyphID method.TTFont.setGlyphOrder clear reverse glyphOrder map, and assume glyphOrder internal attribute is never modified outside setGlyphOrder; added TTFont.getGlyphNameMany and getGlyphIDMany (#1536, #1654, #2334, #2398).fontTools.py23 module to fix deprecation warnings in client code that imports from fontTools (#2232, #2399, #2400).[otTables] Added missing CompositeMode.PLUS operator (#2390).
CompositeMode.PLUS operator (#2390).[transform] Added transformVector and transformVectors methods to the Transform class. Similar to transformPoint but ignore the translation part (#238
transformVector and transformVectors methods to the Transform class. Similar to transformPoint but ignore the translation part (#2386).[xmlWriter] Default to "\n" for newlinestr instead of platform-specific os.linesep (#2384).
"\n" for newlinestr instead of platform-specific os.linesep (#2384).[COLRv1] Various changes to sync with the latest CORLv1 draft spec. In particular: define COLR.VarIndexMap, remove/inline ColorIndex struct, add VarIn
PaintVar* tables (#2372);
add reduced-precicion specialized transform Paints;
define Angle as fraction of half circle encoded as F2Dot14;
use FWORD (int16) for all Paint center coordinates;
change PaintTransform to have an offset to Affine2x3 (#2359).[ttGlyphPen] Fixed bug in TTGlyphPointPen, whereby open contours (i.e. starting with segmentType "move") would throw NotImplementedError. They are now
TTGlyphPointPen, whereby open contours (i.e. starting with segmentType "move") would throw NotImplementedError. They are now treated as if they are closed, like with the TTGlyphPen (#2365, #2366).PaintRotate, PaintSkew and PaintComposite formats were re-numbered. NOTE: these are breaking changes; clients using the experimental COLRv1 API will h…
PaintScale* tables: with/without centers, uniform vs non-uniform.
Added *AroundCenter variants to PaintRotate and PaintSkew: the default versions no longer have centerX/Y, but default to origin.
PaintRotate, PaintSkew and PaintComposite formats were re-numbered.GuessSmoothPointPen to accept a tolerance. Fixed call to math.atan2 with x/y parameters inverted. Sync the code with fontPens (#2344).post table format 2.0 when it contains extra garbage at the end of the stringData array (#2314).otlLib.optimize module; added GPOS compaction algorithm. The compaction can be run on existing fonts with fonttools otlLib.optimize or using the snippet compact_gpos.py. There's experimental support for compacting fonts at compilation time using an environment variable, but that might be removed later (#2326).[subset/instancer] Fixed AttributeError when instantiating a VF that contains GPOS ValueRecords with Device tables but without the respective non-Devi
AttributeError when instantiating a VF that contains GPOS ValueRecords with Device tables but without the respective non-Device values (e.g. XAdvDevice without XAdvance). When not explicitly set, the latter are assumed to be 0 (#2323).[otTables] Fixed AttributeError in methods that split LigatureSubst, MultipleSubst and AlternateSubst subtables when an offset overflow occurs. The Fo
AttributeError in methods that split LigatureSubst, MultipleSubst and AlternateSubst subtables when an offset overflow occurs. The Format attribute was removed in v4.22.0 (#2319).Fixed two instances of DeprecationWarning: invalid escape sequence (#2311).
[subset] Fixed AttributeError when SinglePos subtable has None Value (ValueFormat 0) (#2312, #2313).
[pens] Add ttGlyphPen.TTGlyphPointPen similar to TTGlyphPen (#2205).
ttGlyphPen.TTGlyphPointPen similar to TTGlyphPen (#2205).[subset] Fix KeyError after subsetting COLR table that initially contains both v0 and v1 color glyphs when the subset only requested v1 glyphs; we wer
KeyError after subsetting COLR table that initially contains both v0 and v1 color glyphs when the subset only requested v1 glyphs; we were not pruning the v0 portion of the table (#2308).LayerV1List attribute to None when empty, it's optional in CORLv1 (#2308).[designspaceLib] Allow to use \\UNC absolute paths on Windows (#2299, #2306).
\\UNC absolute paths on Windows (#2299, #2306).VarLibMergeError was raised with incorrect parameters (#2300).NULL to delete multiple glyphs (#2303).NameError exception in getPhantomPoints (#2295, #2305).ItemVariationStore as used in COLRv1 table (#2285).PenError exception (#2284).[feaLib] Skip references to named lookups if the lookup block definition is empty, similarly to makeotf. This also fixes an AttributeError while gener
AttributeError while generating aalt feature (#2276, #2277).--no-hinting implementation for Device tables (#2272, #2275). The previous code was alwyas dropping Device tables if no-hinting was requested, but some Device tables (DeltaFormat=0x8000) are also used to encode variation indices and need to be retained.MVAR table with lazy=True (#2273, #2274).GlyphCoordinates and TupleVariation classes, use bytearray where possible, refactored phantom-points calculations. We measured about 30% speedup in total time of loading master ttfs, building gvar, and saving (#2261, #2266).AssertionError while pruning unused CPAL palettes when 0xFFFF is present (#2257, #2259).[ttLib] Remove .Format from Coverage, ClassDef, SingleSubst, LigatureSubst, AlternateSubst, MultipleSubst (#2238). ATTENTION: This will change your TT
[pens] Reverted breaking change that turned AbstractPen and AbstractPointPen into abstract base classes (#2164, #2198).
AbstractPen and AbstractPointPen into abstract base classes (#2164, #2198).[feaLib] Indent anchor statements in asFea() to make them more legible and diff-able (#2193).
asFea() to make them more legible and diff-able (#2193).AbstractPen and AbstractPointPen into abstract base classes (#2164).STAT table from AFDKO feature files (#2039).STAT table's axis values (#2189).rectArea function to compute area of rectangle (#2192).[COLRv1] Added unbuildColrV1 to deconstruct COLRv1 otTables to raw json-able data structure; it does the reverse of buildColrV1 (#2171).
unbuildColrV1 to deconstruct COLRv1 otTables to raw json-able data structure; it does the reverse of buildColrV1 (#2171).sub X by NULL sequence to delete a glyph (#2170).Vector division (#2173).PaintSweepGradient (#2172).Paint.Format enum class outside of Paint class definition, now named PaintFormat. It was clashing with paint instance Format attribute and thus was breaking lazy load of COLR table which relies on magic __getattr__ (#2175).setupFvar to optionally take designspaceLib.AxisDescriptor objects. Added new setupAvar method. Support localised names for axes and named instances (#2185).[woff2] An initial off-curve point with an overlap flag now stays an off-curve point after compression (https://github.com/fonttools/fonttools/pull/21
[codecs] Handle errors parameter different from 'strict' for the custom extended mac encodings (#2137, #2132).
errors parameter different from 'strict' for the custom extended mac encodings (#2137, #2132).PaintRadialGradient.c0 when the start circle almost touches the end circle's perimeter (#2148).PaintColrLayers tables (#2153).MissingComponentError instead of bare KeyError when a referenced component is missing (#2145).[COLRv1] Implemented PaintTranslate paint format (#2129).
PaintTranslate paint format (#2129).[colorLib] Speed optimization for LayerV1ListBuilder (#2119).
LayerV1ListBuilder (#2119).interpolate_cff2_metrics (0957dc7).[COLRv1] Update to latest draft: added PaintRotate and PaintSkew (#2118).
PaintRotate and PaintSkew (#2118).brotlicffi bindings for PyPy (#2117).expectContentsFile parameter to GlyphSet, for use when reading existing UFOs, to comply with the specification stating that a contents.plist file must exist in a glyph set (#2114).LangSys tags in --layout-scripts option (#2112). For example: --layout-scripts=arab.dflt,arab.URD,latn; this will keep DefaultLangSys and URD language for arab script, and all languages for latn script.--json option to produce a machine-readable list of incompatibilitiesQuartzPen to create CGPath from glyph outlines on macOS. Requires pyobjc (#2107).FONTTOOLS_LOOKUP_DEBUGGING=1 to enable feature file debugging info stored in Debg table (#2106).[colorLib] Fixed regression in 4.17.0 when building COLR v0 table; when color layers are stored in UFO lib plist, we can't distinguish tuples from lis
[colorLib/otData] Updated to latest draft COLR v1 spec (#2092).
COLR v1 spec (#2092).Debg if Builder.buildLookups_ has cooperated (#2065, #2067).Your coding agent can read these notes before it upgrades. Set up the MCP server →