NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #146 most downloaded on PyPI
Tools to manipulate font files
Last release 5 days ago
29 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
238 releases · first in 2013
[designspaceLib] When splitting a DesignSpace v5 document with makeNames=True (as varLib.build_many does), family and style names set explicitly on an
makeNames=True (as varLib.build_many does), family and style names set explicitly on an instance now take precedence over the ones computed from the STAT labels, in all languages, and a PostScript name is no longer made up from the labels for an instance that has its own style name (#3131, #4206, #4208).idRangeOffset points outside glyphIndexArray now raises TTLibError. A negative index used to silently map the code point to the wrong glyph, and one past the end raised a bare AssertionError (#4209).struct.error (#4210).One column per quarter.
fontTools.misc.enumTools now only re-exports enum.StrEnum and is deprecated. Explicitly test and declare support for Python 3.15 ( #4183 , #4196 ).
fontTools.misc.enumTools now only re-exports enum.StrEnum and is deprecated. Explicitly test and declare support for Python 3.15 (#4183, #4196).unicodedata2 18.0.0 when it is used (#4192, #4197).language statements listing multiple language tags, e.g. language AZE CRT;, as Glyphs does and as proposed for the spec (adobe-type-tools/feature_file_workshops#8): the following rules and lookup references are registered under every listed language. dflt cannot be combined with other tags. LanguageStatement.language is still the first tag; all of them are in the new languages attribute (#4201, #4202).script/language pair is repeated within a feature block: the repeated statement replaced the language system's lookups with a fresh copy of the default ones (#4189).FeatureLibError instead of UnboundLocalError when a STAT table block lacks ElidedFallbackName or ElidedFallbackNameID (#3834, #4179).VarStore when saving. Previously the bytes compiled by an earlier save were reused, so a CFF2 variable font that was saved and then modified in place, e.g. by the instancer, was written with a stale VarStore next to its updated charstrings (#4199).VARC fonts that omit fvar while retaining gvar or CFF2 variation data for component-internal axes: hidden axes are addressed by index and gvar can compile, decompile and round-trip through TTX without fvar, reading the axis count from a new axisCount element (#4187, #4188).VARC components whose condition is negated (format 5), which raised AttributeError (#4191).VARC axis references left stale when removing an unrelated axis, reject pinning or restricting axes referenced by VARC components, and stop culling avar2 ranges for component-internal variations, which can reach outside the font-level ranges (#4190, #4193).splitQuadraticAtT and splitCubicAtTC as well, like splitCubicAtT since 4.55.4 (#3742, #4194).ZeroDivisionError in lineLineIntersections for collinear vertical lines; they are now treated as parallel like horizontal ones (#3515, #4181).pyftsubset now preserves the input font's flavor (WOFF, WOFF2) when --flavor is omitted, instead of writing uncompressed sfnt data under the same extension; pass --flavor=none to force uncompressed output (#3630, #4182).unitsPerEm values by name, with the input values, instead of a bare assertion (#2844, #4184).DesignSpaceDocument.default, which holds a SourceDescriptor, not a source name (#2994, #4186).TTLibError instead of AssertionError for inconsistent WOFF table, metadata and private-data lengths, so the checks also hold under python -O (#4178).resolve_entities="internal" still fetched external parameter entities before lxml 6.1.3, so a crafted DTD could read local files into parsed XML content (#4195).varLib.avar.unbuild emits its designspace snippet, so a crafted font cannot inject markup (#4203).[glyf] Add __iter__ , items and values methods to the glyf table to make it more dict-like ( #4156 ).
__iter__, items and values methods to the glyf table to make it more dict-like (#4156).<variable-font name="..."/> when deriving the output filename in the varLib command line, so a designspace cannot write outside the output directory (#4168).script statements. Rules following a script statement that names the first declared language system no longer end up under the DFLT script, and a script statement naming the already-current script still narrows the language systems and terminates the current lookup while leaving the lookupflag alone, matching makeotf (#1824, #2522, #4169).OTLOffsetOverflowError instead of AttributeError so another contextual format can be tried (regression from #3439). When all formats overflow, split the ruleset in halves until it fits (#4171).[feaLib] Fix name-table parsing for multibyte Mac encodings ( #1196 , #4092 ).
IDEF[ ], like function definitions (#4093).palt by default (#4094).__setitem__ membership (#4103).fixLookupOverFlows() reporting success when it had not promoted any lookup to Extension, masking unresolvable overflows.TTCollection.save (#4111).bhed, bdat, bloc, variants of head, EBDT, EBLC used in legacy Apple bitmap-only fonts (#4115).OS/2 fsSelection/macStyle consistency against bhed as well as head (#4118, #4119).BASE table (#4137).vsindex handling in instantiateCFF2 (#4129, #4132).TTLibError instead of AssertionError or struct.error when reading a font truncated within the table directory or a table entry (#4147, #4149).]]> terminator inside CDATA sections, so an SVG document containing it can no longer smuggle markup past a TTX round trip (#4139).(100 wght=900:120) means (wght=400:100 wght=900:120) when the wght default is 400 (#4024).TTLibError for a truncated or out-of-bounds cmap subtable header (#4151).<lib> element as an empty lib instead of raising IndexError (#4142, #4144).splitSinglePos so GPOS lookup type 1 offset overflows can be recovered by splitting the subtable (#4091, #4108).TTLibError instead of RecursionError when recalcBounds() hits a composite-component reference cycle (#3899, #4116).Z Z) (#4122).DefaultTable type annotations (#4126).EBSC (Embedded Bitmap Scaling) table (#4113).TypeError in the Cython-compiled build when Qu2CuPen passes tuple splines (#4160)... components in contents.plist, and a crafted .ufoz could create files outside its temporary mirror (#4124).ttx -z extfile export, preventing arbitrary file writes from untrusted fonts (#4128).XMLParser when lxml is used, preventing XXE file disclosure on lxml < 5.0 (#4145).VARC auxiliary data: collect and remap variation indices referenced by condition tables when subsetting the MultiVarStore, and drop the AxisIndicesList, ConditionList, and MultiVarStore when they end up empty (#4162).[ttLib] Add support for Apple Color Emoji bgcl table ( #4065 ).
bgcl table (#4065).IFT and IFTX tables (Incremental Font Transfer, PatchMapFormat2) (#4070, #4072).FieldSpec dataclass for OpenType table schema definitions, replacing raw tuples in otData.py (#4076).name table strings as comments next to label IDs in TTX output, matching the convention used by fvar, STAT, trak (#4089).split_cubic_into_three that could cause ±1 off-curve coordinate shifts (#3928, #4083).map_backward for many-to-one (flat-segment) axis maps that silently dropped entries via dict comprehension (googlefonts/ufo2ft#978, #4085).setUnicodeRanges to accept reserved bits 123-127, restoring round-trip with getUnicodeRanges and fixing recalcUnicodeRanges crash in the subsetter (#4087, #4088).[feaLib] Extend contextual rule merging to all rule types: single subst, GSUB/GPOS named lookups, ignore rules, and chained alternate subst ( #4061 ).
[diff] Add new fonttools diff command for comparing font files, imported from the fdiff project and heavily reworked ( #1190 , #4007 , #4009 , #4011 ,
fonttools diff command for comparing font files, imported from the fdiff project and heavily reworked (#1190, #4007, #4009, #4011, #4013, #4019).VariableScalar interpolation bug with non-linear avar mappings. Also decouple VariableScalar from compiled fonts, allowing it to work with designspace data before compilation (#3938, #4054).VariableScalar axis ordering and iterative delta rounding to match fontc behavior (#4053).ConditionsetStatement to fix glyphsLib round-tripping (fontra/fontra-glyphs#130, #4057).0xFFFF instead of 0 for missing nameIDs in cv feature params (#4010, #4012).CmapSubtable.__lt__() TypeError on Python 3 when subtables share the same encoding record, and add compile-time validation for unique encoding records (#4035, #4055).glyphObject=None (#4030, #4031).SegmentToPointPen edge case: only remove a duplicate final point on closePath() if it is an on-curve point (#4014, #4015).eval() with safeEval() in parseBlendList() to prevent arbitrary code execution from crafted TTX files (#4039, #4040).META, SING, GMAP, GPKG) (#4044).getSubModel not forwarding extrapolate/axisRanges; check location uniqueness after stripping zeros (#4047).--variable-fonts filter in build_many; remove dead code and fix comments (#4048).unbuild return types consistent; validate map CLI coordinates (#4051).ColorLibError when base glyphs are missing from glyphMap, instead of a confusing KeyError (#4041).fvar table dependency (#4017).name table dependency (#4018).saveXML regression with empty table lists, clarify docstring (#4025, #4026, #4056).libm for Cython extensions using math functions (#4028, #4029).DSIG, DefaultTable, ttProgram (#4033).[otlLib] buildCoverage: return empty Coverage instead of None ( #4003 , #4004 ).
avar2 full instancing (#4002).setupOS2() default params globally polluted (#3996, #3997).…which invokes fonttools.varLib.main() . Fixes CVE-2025-66034 , see: GHSA-768j-98cg-p3fv .
fonttools varLib command-line script, or code which invokes fonttools.varLib.main(). Fixes CVE-2025-66034, see: GHSA-768j-98cg-p3fv.unicodedata2 >= 17.0.0 when installed with 'unicode' extra.…on Python 3.9 to avail of the security fix for CVE-2025-66034 ( #3994 , #3999 ).
[ufoLib] Reverted accidental method name change in UFOReader.getKerningGroupConversionRenameMaps that broke compatibility with downstream projects lik
UFOReader.getKerningGroupConversionRenameMapsgetKerningGroupConversionRenameMaps method (#3950).The bare fonttools varLib.avar script is deprecated, in favour of fonttools varLib.avar.build (or unbuild ).
[pointPen] Allow reverseFlipped parameter of DecomposingPointPen to take a ReverseFlipped enum value to control whether/how to reverse contour direction of flipped components, in addition to the existing True/False. This allows to set ReverseFlipped.ON_CURVE_FIRST to ensure that the decomposed outline starts with an on-curve point before being reversed, for better consistency with other segment-oriented contour transformations. The change is backward compatible, and the default behavior hasn't changed (#3934).
[filterPen] Added ContourFilterPointPen, base pen for buffered contour operations, and OnCurveStartPointPen filter to ensure contours start with an on-curve point (#3934).
[cu2qu] Fixed difference in cython vs pure-python complex division by real number (#3930).
[varLib.avar] Refactored and added some new sub-modules and scripts (#3926).
varLib.avar.build module to build avar (and a missing fvar) binaries into a possibly empty TTFont,varLib.avar.unbuild module to print a .designspace snippet that would generate the same avar binary,varLib.avar.map module to take TTFont and do the mapping, in user/normalized space,varLib.avar.plan module moved from varLib.avarPlanner.The bare fonttools varLib.avar script is deprecated, in favour of fonttools varLib.avar.build (or unbuild).
[interpolatable] Clarify linear_sum_assignment backend options and minimal dependency usage (#3927).
[post] Speed up build_psNameMapping (#3923).
[ufoLib] Added typing annotations to fontTools.ufoLib (#3875).
[varLib] Clear USE_MY_METRICS component flags when inconsistent across masters ( #3912 ).
USE_MY_METRICS component flags when inconsistent across masters (#3912).dot() product of perpendicular vectors not always returning exactly 0.0 in all Python implementations (#3911)avar2 fonts (#3909).VariableScalar's axis locations (#3906, #3907).calc_intersect for degenerate cubic curves where 3 to 4 control points are equal (#3904).[varLib] Deprecate varLib.mutator in favor of varLib.instancer . The latter provides equivalent full (static font) instancing in addition to partial V…
varLib.mutator in favor of varLib.instancer. The latter provides equivalent full (static font) instancing in addition to partial VF instancing.fonttools varLib.mutator with fonttools varLib.instancer. API users should migrate to fontTools.varLib.instancer.instantiateVariableFont (#2680).Removed hard-dependency on pyfilesystem2 (fs package) from fonttools[ufo] extra. This is replaced by the fontTools.misc.filesystem package, a stdlib-o
fs package) from fonttools[ufo] extra. This is replaced by the fontTools.misc.filesystem package, a stdlib-only, drop-in replacement for the subset of the pyfilesystem2's API used by fontTools.ufoLib. The latter should continue to work with the upstream fs (we even test with/without). However, clients who wish to continue using fs can do so by depending on it directly instead of via the fonttools[ufo] extra (#3885, #3620)..addComponent() implementations, in particular baseGlyphName vs glyphName (#3880).[feaLib] Don't try to combine ligature & multisub rules (#3874).
[feaLib] Fixed iterable check for Python 3.13.4 and newer (#3854, #3855).
[feaLib] Fixed iterable check for Python 3.13.4 and newer (#3854, #3855).
[ttLib.reorderGlyphs] Handle CFF2 when reordering glyphs
[ttLib.reorderGlyphs] Handle CFF2 when reordering glyphs (#3852)
[subset] Copy name IDs in use before scrapping or scrambling them for webfonts (#3853)
[varLib] Make sure that fvar named instances only reuse name ID 2 or 17 if they are at the default location across all axes, to match OT spec requirem
Timer._time a static method that doesn't take self, makes it easier to override (#3836).None for empty ConditionSet, which translates to a null offset in the compiled table (#3850).aalt feature (#3847).LangSys in a ScriptRecord if missing when adding feature variations to existing GSUB later in the build (#3838).main().[HVAR, VVAR] Prune unused regions when using a direct mapping
GVAR table (#3728)AttributeError when reporting table overflow (#3808)<NULL> in single pos lookups (#3803)fonttools voltLib command line tool to compile VOLT sources directly (doing an intermediate fea conversion internally) (#3818)[ttLib.__main__] Add --no-recalc-timestamp flag
ttLib.__main__] Add --no-recalc-timestamp flag (#3771)ttLib.__main__] Add -b (recalcBBoxes=False) flag (#3772)ttLib.__main__] Improvements around the -t flag (#3776)GPOS (#3794)[varStore] Sort the input todo list with the same sorting key used for the opimizer's output (#3767).
DeltaValue repeat value which caused a crash after importing from XML and then compiling a GPOS containing Device tables (#3758).FeatureLibError pickleable, so client can e.g. use feaLib to can compile features in parallel with multiprocessing (#3762).[MetaTools] Fixed bug in buildUCD.py script whereby the first non-header line of some UCD text file was being skipped. This affected in particular the
Shorten the changelog included in PyPI package description to accommodate maximum length limit imposed by Azure DevOps. No actual code changes since v
[glyf] Fixed regression introduced in 4.55.5 when computing bounds of nested composite glyphs with transformed components (#3752).
[glyf] Fixed recalcBounds of transformed components with unrounded coordinates (#3750).
[bezierTools] Fixed splitCubicAtT sometimes not returning identical start/end points as result of numerical precision (#3742, #3743).
splitCubicAtT sometimes not returning identical start/end points as result of numerical precision (#3742, #3743).AlternateSubstStatement (#3735).[Docs] fill out ttLib table section [#3716]
[designspaceLib] Allow axisOrdering to be set to zero
[ttGlyphSet] Support VARC CFF2 fonts
[cffLib.specializer] Adjust stack use calculation
[unicodedata] Update to Unicode 16
\ in doc string[Docs] Small docs cleanups by @n8willis
[Docs] Small docs cleanups by @n8willis (#3611)
[Docs] cleanup code blocks by @n8willis (#3627)
[Docs] fix Sphinx builds by @n8willis (#3625)
[merge] Minor fixes to documentation for merge by @drj11 (#3588)
[subset] Small tweaks to pyftsubset documentation by @RoelN (#3633)
[Tests] Do not require fonttools command to be available by @behdad (#3612)
[Tests] subset_test: add failing test to reproduce issue #3616 by @anthrotype (#3622)
[ttLib] NameRecordVisitor: include whole sequence of character variants' UI labels, not just the first by @anthrotype (#3617)
[varLib.avar] Reconstruct mappings from binary by @behdad (#3598)
[varLib.instancer] Fix visual artefacts with partial L2 instancing by @Hoolean (#3635)
[varLib.interpolatable] Support discrete axes in .designspace by @behdad (#3599)
[varLib.models] By default, assume OpenType-like normalized space by @behdad (#3601)
[feaLib] Improve the sharing of inline chained lookups
4.53.1
[ttLib.removeOverlaps] Support CFF table to aid in downconverting CFF2 fonts
[ttLib.removeOverlaps] Support CFF table to aid in downconverting CFF2 fonts (#3528)
[avar] Fix crash when accessing not-yet-existing attribute (#3550)
[docs] Add buildMathTable to otlLib.builder documentation (#3540)
[feaLib] Allow UTF-8 with BOM when reading features (#3495)
[SVGPathPen] Revert rounding coordinates to two decimal places by default (#3543)
[varLib.instancer] Refix output filename decision-making (#3545, #3544, #3548)
[varLib.cff] Restore and deprecate convertCFFtoCFF2 that was removed in 4.52.0 release as it is used by downstream projects (#3535).
Fixed a small syntax error in the reStructuredText-formatted NEWS.rst file which caused the upload to PyPI to fail for 4.52.2. No other code changes.
Fixed a small syntax error in the reStructuredText-formatted NEWS.rst file which caused the upload to PyPI to fail for 4.52.2. No other code changes.
Fixed a small syntax error in the reStructuredText-formatted NEWS.rst file which caused the upload to PyPI to fail for 4.52.0. No other code changes.
Fixed a small syntax error in the reStructuredText-formatted NEWS.rst file which caused the upload to PyPI to fail for 4.52.0.
No other code changes.
[ttLib] Optimization on loading aux fields (#3464).
[pens] Added decomposing filter pens that draw components as regular contours (https://github.com/fonttools/fonttools/pull/3460).
[pens] Added decomposing filter pens that draw components as regular contours (#3460).
[instancer] Drop explicit no-op axes from TupleVariations (#3457).
[cu2qu/ufo] Return set of modified glyph names from fonts_to_quadratic (#3456).
[otlLib] Add API for building MATH table
MATH table (#3446)Fixed uploading wheels to PyPI, no code changes since v4.48.0.
Minor release to fix uploading wheels to PyPI.
Minor release to fix uploading wheels to PyPI.
[varLib.models] New API for VariationModel: getMasterScalars and interpolateFromValuesAndScalars.
getMasterScalars and interpolateFromValuesAndScalars.public.fontInfo lib key, not used anywhere yet (#3358).[featureVars] Allow to register the same set of substitution rules to multiple features. The addFeatureVariations function can now take a list of feat
addFeatureVariations function can now take a list of featureTags; similarly, the lib key 'com.github.fonttools.varLib.featureVarsFeatureTag' can now take a comma-separateed string of feature tags (e.g. "salt,ss01") instead of a single tag (#3360).VarLibError if the feature tag already has feature variations associated with it (#3363).addGSUBFeatureVariations function to add GSUB Feature Variations to an existing variable font from rules defined in a DesignSpace document (#3362).[varLib.interpolatable] Various bugfixes and improvements, better reporting, reduced false positives.
[varLib.interpolator] Vastly improved algorithms. Also available now is --pdf and --html options to generate a PDF or HTML report of the interpolation
--pdf and --html options to generate a PDF or HTML report of the interpolation issues.[subset] Only prune codepage ranges for OS/2.version >= 1, ignore otherwise (#3334).
[feaLib] Ensure variable mark anchors are deep-copied while building since they get modified in-place and later reused (#3330).
recalcCodePageRanges to OS/2 table class; added --prune-codepage-ranges to fonttools subset command (#3328, #2607).NOTE: this release was "yanked" from PyPI because it introduced a regression: https://github.com/fonttools/fonttools/issues/3334
[instancer] Recalc OS/2 AvgCharWidth after instancing if default changes (#3317).
recalcBounds (#3301).[EBDT] Fixed TypeError exception in _reverseBytes method triggered when dumping some bitmap fonts with ttx -z bitwise option (#3162).
_reverseBytes method triggered when dumping some bitmap fonts with ttx -z bitwise option (#3162).recalc method when no vmtx or hmtx tables are present (#3290).calcQuadraticArcLength (#3288).[subset] Set up lxml XMLParser(resolve_entities=False) when parsing OT-SVG documents to prevent XML External Entity (XXE) attacks (9f61271dc): https:/
XMLParser(resolve_entities=False) when parsing OT-SVG documents to prevent XML External Entity (XXE) attacks (9f61271dc): https://codeql.github.com/codeql-query-help/python/py-xxe/iup_delta_optimize that was leading to IUP tolerance being incorrectly initialised, resulting in sub-optimal deltas (60126435d, cython/cython#5732).fonttools varLib.avar to add an avar table to an existing VF from axes mappings in a .designspace file (0a3360e52).[t1Lib] Fixed several Type 1 issues (#3238, #3240).
addMultilingualName and ttFont parameter was not passed on to findMultilingualName (#3253).[varLib] Use sentinel value 0xFFFF to mark a glyph advance in hmtx/vmtx as non participating, allowing sparse masters to contain glyphs for variation
[subset] Fixed perf regression in v4.41.0 by making NameRecordVisitor only visit tables that do contain nameID references (#3213, #3214).
NameRecordVisitor only visit tables that do contain nameID references (#3213, #3214).CursivePos masters (#3209).[fontBuilder] Fixed bug in setupOS2 with default panose attribute incorrectly being set to a dict instead of a Panose object (#3201).
removeUnusedNameRecords in the user range (#3185)._add_avar() call site (#3183).voltLib.voltToFea submodule (originally Tiro Typeworks' "Volto") for converting VOLT OpenType Layout sources to FEA format (#3164).Published native binary wheels to PyPI for all the python minor versions and platform and architectures currently supported that would benefit from th
pip install --no-binary=fonttools fonttools to expliclity request pip to install from the pure-python source.avar2 table from those (#3123).quantization option (#3126).[varLib.interpolatable] Allow for sparse masters
[varLib.interpolatable] Allow for sparse masters (#3075)
[merge] Handle differing default/nominalWidthX in CFF (#3070)
[ttLib] Add missing main.py file to ttLib package (#3088)
[ttx] Fix missing composite instructions in XML (#3092)
[ttx] Fix split tables option to work on filenames containing '%' (#3096)
[featureVars] Process lookups for features other than rvrn last (#3099)
[feaLib] support multiple substitution with classes (#3103)
[sbix] Fixed TypeError when compiling empty glyphs whose imageData is None, regression was introduced in v4.39 (https://github.com/fonttools/fonttools
seekable method was only added in python 3.11 (https://github.com/fonttools/fonttools/issues/3052).[sbix] Fixed TypeError when compiling empty glyphs whose imageData is None, regression was introduced in v4.39 (#3059).
[ttFont] Fixed AttributeError on python <= 3.10 when opening a TTFont from a tempfile SpooledTemporaryFile, seekable method only added on python 3.11 (#3052).
Your coding agent can read these notes before it upgrades. Set up the MCP server →