# fontmake turns UFO and Glyphs sources into OpenType and TrueType binaries

> 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.

**googlefonts/fontmake** — Compile fonts from sources (UFO, Glyphs) to binary (OpenType, TrueType).

- Repository: https://github.com/googlefonts/fontmake
- Stars: 896 · Forks: 98
- Language: Python
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/googlefonts-fontmake

## 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:

```bash
fontmake -i -- MyFont.designspace
```

Exactly 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:

```bash
pip3 install fontmake
```

and updating is the same command with the upgrade option:

```bash
pip3 install -U fontmake
```

The 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:

```bash
git clone https://github.com/googlefonts/fontmake
cd fontmake
pip install .
```

and for quick iteration without reinstalling, the editable install:

```bash
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.

## 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.

## FAQ

### 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.

## Sources

- [googlefonts/fontmake on GitHub](https://github.com/googlefonts/fontmake)
- [Issues](https://github.com/googlefonts/fontmake/issues)
- [License: Apache-2.0](https://github.com/googlefonts/fontmake/blob/main/LICENSE)
- [README](https://github.com/googlefonts/fontmake/blob/main/README.md)
- [Releases](https://github.com/googlefonts/fontmake/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/googlefonts-fontmake
