fontmake turns UFO and Glyphs sources into OpenType and TrueType binaries
Compile fonts from sources (UFO, Glyphs) to binary (OpenType, TrueType).
At a glance
- What is it?
- fontmake is a Python command line tool that compiles font sources into binaries, including static instances and variable fonts, and it is the build step most Google Fonts repositories already use. It suits type designers who need one command instead of a per-format pipeline, and it assumes your sources are already structured for compilation.
- Who is it for?
- fontmake suits a type designer or a font repository maintainer who keeps sources in UFO, Glyphs or Designspace format and wants a single documented command for static masters, static instances and variable fonts. It is the wrong tool for a designer who wants to draw glyphs, since it compiles rather than edits, and the wrong tool for a project with one hand-written source file and no build to speak of.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 14 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One command in front of several source formats
fontmake compiles fonts from various sources into binaries. The README names the source formats as .glyphs, .ufo and .designspace, and the binaries as .otf and .ttf, and it states plainly that you can use it to create static instances and variable fonts.
That description is the whole product. fontmake is not an editor and not a design tool. It is the step between a source directory and the files a browser or an operating system will actually load, and its job is to make that step reproducible rather than a sequence of manual conversions in a GUI.
The input can be specified in two ways. You can pass a flag that names the format, with -g for a Glyphs file, -u for one or more UFO or UFOZ files, and -m for a Designspace file. The README explains the odd choice of letter for the last one, noting that the -m is for mutatormath, an old Python library for handling designspaces.
Or you can leave the format out and pass the input as a positional argument, letting fontmake infer the source format from the file extension. So the same file can be named two ways, and the flag form exists for the cases where inference is not enough, such as several UFOs at once.
There is a parsing subtlety worth knowing before you write a build script. If positional arguments are preceded by an option that takes one or more arguments, you need the -- separator to mark everything after it as positional, otherwise the parser gets confused and thinks no input was provided. The example the README gives is the -i option, which takes zero or one argument:
fontmake -i -- MyFont.designspaceExactly one type of input must be specified, through either route.
Output formats decide the directory layout
The -o flag selects the output format, and it can be given more than once. The documented set splits into real outputs and intermediates.
The five real outputs are otf for per-master OTF binaries with CFF outlines, placed in the master_otf directory, and ttf for per-master TrueType binaries in master_ttf. Then otf-cff2 gives per-master OTF binaries with CFF2 outlines in master_otf. The variable format produces a TrueType variable font in variable_ttf, and variable-cff2 produces a variable font with CFF2 outlines in variable_otf.
The three intermediate formats are generally used internally by fontmake as a step toward one of those: otf-interpolatable, OTF binaries suitable for merging into a variable font, where the outlines are unoptimized, and ttf-interpolatable, TTF binaries suitable for merging, where the outlines are converted to quadratic curves in an interpolation-compatible way. The third is ufo, which converts Glyphs sources to UFO or, with the --save-ufo-as-zip flag, to the zipped UFOZ format.
If no format option is given, the default is -o otf ttf, so a bare invocation produces per-master binaries in both flavours and nothing else. That default is worth knowing, because a build that suddenly starts producing a directory of masters instead of a variable font is usually the result of a dropped -o flag rather than a broken source.
Two more options shape the output tree. --output-dir puts everything in one directory instead of the per-format directories above, and --output-path writes a single binary to a given filename, which is only valid when the output is one file.
Static instances are opt-in, and that surprises people
The -i flag, for interpolating instances, exists because of a design decision in the source formats that catches out almost everyone. The README states that having per-master binaries is not always what you expect, and that if you have defined instances, or exports, in your Glyphs file, they will not be generated by default.
So a source that defines ten static instances and a variable font will, by default, produce only the masters. The instances appear in the instance_ttf or instance_otf directory once -i is passed.
This is a real source of confusion in build pipelines, and it is a design decision rather than a bug. A variable font is meant to be the primary artefact, with the static instances derived from it at release time, so the default avoids producing a pile of binaries that nobody asked for. It does mean the flag has to be in the script.
The other two options in this group are about correctness of the outlines rather than about what gets built. The -f flag flattens components, which ensures that glyphs containing components which themselves contain components are decomposed to a single level. The README recommends this because certain rendering environments do not handle nested components correctly, and it points at a fontbakery issue for the details. A font that renders correctly in one tool and incorrectly in another is often a nested component, and -f is the fix.
Beyond these, the README points to USAGE.md for advanced usage and TROUBLESHOOTING.md for problems, which is where the rest of the option surface lives.
The dependency set is the real specification
fontmake is a thin layer over a specific stack, and the pinned requirements in the repository are more informative about the tool than the option list.
The declared dependencies are fontMath at 0.9.4 or later, fonttools at 4.59.2 or later, glyphsLib at 6.11.6 or later, ufo2ft at 3.6.1 or later and ufoLib2 at 0.18.1 or later. ufo2ft is the component that does the actual compilation from UFO to binary, glyphsLib reads and writes the Glyphs format, ufoLib2 handles the UFO format, fontMath does the interpolation mathematics, and fonttools provides the low-level font handling and the extras.
The requirements file pins those to exact versions, and adds booleanOperations, cffsubr, compreffor and ttfautohint-py, along with cattrs and orjson for the experimental reading and writing of UFO as JSON. The fonttools extras differ by interpreter, with the CPython build getting ufo, lxml and unicode and other implementations getting ufo and unicode.
The setup.py also shows two extras that are now vestigial and are kept only for compatibility. The lxml extra is empty with a comment that it is now the default, and the mutatormath extra is a dummy because MutatorMath is no longer supported, kept so that an install requesting it does not fail. There is an autohint extra pulling ttfautohint-py, and a pathops extra pulling skia-pathops.
The practical consequence is that fontmake is a moving target by version. Whether your build produces the same binary from the same sources depends on the versions of these libraries, which is why a repository that cares about reproducibility pins them, and why a design team should pin them too.
Installing it, and the version trap in the setup script
The requirement is Python 3.10 or later, and releases are on PyPI, so the install is one command:
pip3 install fontmakeand updating is the same command with the upgrade option:
pip3 install -U fontmakeThe README recommends installing fontmake inside a virtual environment to prevent conflicts between its dependencies and other modules installed globally, and it points at pipx as a way to automate that isolation for Python applications like fontmake.
Developers can work from a clone instead:
git clone https://github.com/googlefonts/fontmake
cd fontmake
pip install .and for quick iteration without reinstalling, the editable install:
pip install -e .The packaging is a plain setuptools build, with a pyproject.toml naming setuptools, wheel and setuptools_scm as build requirements, and a setup.py that reads the long description from README.md.
There is one thing in setup.py worth flagging, and it is a behaviour rather than a bug. The script sets site.ENABLE_USER_SITE to whether the --user flag was passed, with a comment pointing at a pip issue about user site installs. In other words, whether your install lands in the user site directory is decided by inspecting sys.argv before setuptools runs. It is the sort of detail that surprises people when a pip install behaves differently from what they expected.
A build script that installs fontmake into a fresh virtual environment per run, or uses pipx, sidesteps that class of surprise entirely.
Apache-2.0 terms and a version line that moves often
The licence is Apache-2.0, with a LICENSE file at the repository root. For a build tool, that is the uncomplicated case: no obligations beyond the notice, and no copyleft reaching into the fonts you build with it. The licence covers the tool, not the output.
Activity is high and the release line moves fast. The recent versions are v3.12.2 on 2026-10-09, v3.12.1 on 2026-06-02 and v3.12.0 on 2026-05-28, and the last push was on 2026-10-09. A patch release four months after the previous patch, within a minor line, is a normal cadence for a tool that sits in front of many font repositories.
The release process is documented in the README and is plain Git. Commit and push the final changes, create an annotated tag with a prepended v, as in git tag -a v3.1.1, write the release notes into the tag message so they appear on the GitHub release page, and push the tag to origin.
The repository layout shows a conventional Python project with a capital L Lib directory, setup.py alongside setup.cfg, a pyproject.toml, requirements.txt and test_requirements.txt, tox.ini, a tests directory, and USAGE.md, TROUBLESHOOTING.md and CONTRIBUTING.md at the top level. There is also a build_pyz.sh script, which is the route to a standalone executable rather than a pip install, and it matters for CI environments that cannot install Python packages.
The final point for an adopter is the one the pinned requirements already imply. fontmake does not produce the same binary forever from the same sources, because the libraries underneath it move. A font project that ships built artefacts should pin the whole stack, not just fontmake.
Editorial conclusion
fontmake suits a type designer or a font repository maintainer who keeps sources in UFO, Glyphs or Designspace format and wants a single documented command for static masters, static instances and variable fonts. It is the wrong tool for a designer who wants to draw glyphs, since it compiles rather than edits, and the wrong tool for a project with one hand-written source file and no build to speak of. Before you adopt it, read USAGE.md for the flags beyond the documented basics, check TROUBLESHOOTING.md when a compile fails, and pin the dependency versions in a requirements file, because the pinned requirements in the repository show that the exact versions of fonttools, glyphsLib, ufo2ft and ufoLib2 determine the output.
Frequently asked questions
How do I install fontmake and compile a font?
fontmake requires Python 3.10 or later and installs from PyPI with pip3 install fontmake. A variable font from a Glyphs source is then built with fontmake MyFont.glyphs -o variable, and the executable is called fontmake.
Why are my static instances missing from the build?
Instances defined in your Glyphs file are not generated by default. You need to pass the -i flag to interpolate static instances, which then places them in the instance_ttf or instance_otf directory as appropriate.
Which output formats does fontmake support?
The real outputs are otf, ttf, otf-cff2, variable and variable-cff2, each placed in its own directory. There are also intermediate formats, otf-interpolatable, ttf-interpolatable and ufo, which fontmake generally uses on the way to one of the main outputs. With no format option given, the default is otf and ttf.
What does the -f flag do in fontmake?
It flattens components, so that glyphs containing components which themselves contain components are decomposed to a single level. The README recommends it because certain rendering environments do not handle nested components correctly.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/googlefonts-fontmake)Was this useful?