{"id":19464414,"url":"https://github.com/arrowtype/fonttools-intro","last_synced_at":"2025-08-30T12:16:04.294Z","repository":{"id":99695414,"uuid":"273374580","full_name":"arrowtype/fonttools-intro","owner":"arrowtype","description":"An introduction to FontTools \u0026 font development","archived":false,"fork":false,"pushed_at":"2023-11-29T01:44:49.000Z","size":13156,"stargazers_count":89,"open_issues_count":3,"forks_count":3,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-02-25T13:36:41.542Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arrowtype.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-06-19T01:15:40.000Z","updated_at":"2025-01-28T07:55:37.000Z","dependencies_parsed_at":"2023-07-31T12:15:48.443Z","dependency_job_id":null,"html_url":"https://github.com/arrowtype/fonttools-intro","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arrowtype/fonttools-intro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arrowtype%2Ffonttools-intro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arrowtype%2Ffonttools-intro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arrowtype%2Ffonttools-intro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arrowtype%2Ffonttools-intro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arrowtype","download_url":"https://codeload.github.com/arrowtype/fonttools-intro/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arrowtype%2Ffonttools-intro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272847349,"owners_count":25003177,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-30T02:00:09.474Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T18:14:57.916Z","updated_at":"2025-08-30T12:16:04.273Z","avatar_url":"https://github.com/arrowtype.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# An Intro to FontTools\n\nThis post is an introduction to FontTools and modern font development more generally.\n\nIt is written from the perspective of a beginner/intermediate font designer \u0026 developer (me, Stephen Nixon / [@ArrowType](https://instagram.com/arrowtype)), intended as an approachable introduction to font development for designers or developers hoping to better understand some common processes \u0026 tools of font development.\n\nBasically, this is an attempt to write \u0026 present the guide I wish I had three years ago, when I first encountered font development as a student at KABK TypeMedia. Some of it requires knowledge of navigating in a terminal and using Python, but a lot of it is written in hopes of being accessible to almost anyone.\n\nIt is opinionated, limited to my current perspective, and possibly not 100% accurate. For this reason, I am presenting this at the [Typographics 2020 TypeLab](https://2020.typographics.com/typelab/) in hopes that more-experienced font developers might participate in conversation that can help me to correct any inaccuracies and improve this guide. Additionally, if you spot something here that is inaccurate or if you have questions, please file an [issue](https://github.com/arrowtype/fonttools-intro/issues) or [pull request](https://github.com/arrowtype/fonttools-intro/pulls) in this repo to help myself \u0026 others learn!\n\n## Basic things to know about fonts \u0026 font development\n\nDigital fonts, like any other digital media, are made up of data. Each glyph in a font is drawn from a collection of points on a coordinate grid, in scaleable curves called *Bézier* curves. Fonts have to map these drawings to [Unicode](https://en.wikipedia.org/wiki/List_of_Unicode_characters), so that the right glyph can display for each character in text on any computer. Fonts include a lot of other data: kerning, layout logic (e.g. for the place of accents, for stylistic sets, contextual alternates, etc), metrics for layout, family \u0026 style names for various software, license information, version numbers, and much more.\n\nWhile some fonts are standalone, single-style things, many fonts are members of large font families (AKA typefaces) which have different but related styles.\n\nModern fonts follow the [OpenType specification](https://docs.microsoft.com/en-us/typography/opentype/spec/), which describes the data *tables* that all of this information is stored in. The OpenType spec is somewhat similar to the HTML, CSS, and JS specs that describe the “ground rules” that make up the modern web.\n\nFor end-users, the most common font file types are `TTF` and `OTF` (typically the latter are more specifically `CFF` or `CFF2` files). The biggest technical difference between `TTF` and `CFF` files is how they store glyph shapes. `CFF` fonts store glyphs as *Cubic* Bézier curves, which are similar to curves you may be familiar with from the pen tools of apps like Adobe Illustrator, Sketch, \u0026 Figma – these curves have two “offcurve points” between all main “oncurve” points. `TTF` fonts use *Quadratic* Bézier curves, which can have one to many offcurve points between oncurve points. The two formats also take different approaches to data compression and hinting, and have pros \u0026 cons in different software. However, both types are OpenType fonts, and both share many of the same data tables.\n\n`TTF` and `OTF` files are called *binaries* because they are made up of code that is not human-readable. Font designers don’t work (typically) work directly in binaries. Instead, they work in source files which are often `.glyphs` (for [GlyphsApp](https://glyphsapp.com/)) or `.ufo` (the open-source format Unified Font Object, which is most commonly edited in [RoboFont](https://robofont.com/)). While `.glyphs` files can include multiple font sources to make up a single font family, `.ufo` sources are individual files, and `.designspace` files describe how multiple UFOs make up a given font family.\n\nFrom these sources, binary files like OTF \u0026 TTF can be exported/built. For fonts on the web, these OTF/TTF files can then be further compressed into web-specific formats (the standard of which is now `woff2`).\n\nAt core, a lot of what FontTools does is to allow people to read \u0026 manipulate these different font filetypes with Python. In turn, this allows scripts \u0026 software that can create, modify, and build fonts.\n\n## What is FontTools?\n\nFrom the [FontTools repo](https://github.com/fonttools/fonttools/):\n\n\u003e fontTools is a library for manipulating fonts, written in Python.\n\nFor my purposes, FontTools is a set of code-based tools to read \u0026 manipulate:\n- OTF \u0026 TTF font binaries\n- UFO \u0026 Designspace font sources\n- More font formats that I haven’t yet needed to work with\n\nRather than being one big thing, FontTools is a *collection* of [many tools](https://github.com/fonttools/fonttools/tree/master/Lib/fontTools), including:\n\n- **cu2qu** for conversion between cubic \u0026 quadratic Bézier curves\n- **designspaceLib** for reading \u0026 writing Designspace files\n- **feaLib** for reading \u0026 writing OpenType features (e.g. accent placement, contextual glyph selection)\n- **merge** to merge multiple font files into together\n- **pens** that can read \u0026 draw different kinds of Bézier paths\n- **subset** which can make font files smaller by eliminating (or subsetting) certain glyphs\n- **ttLib** converts TrueType fonts to/from Python objects, and Python objects to/from TTX. This includes **TTFont** which can be used to read/edit data in font binaries\n- **ttx** is a command-line tool to convert font binaries to/from human-readable [XML](https://developer.mozilla.org/en-US/docs/Web/XML/XML_introduction), allowing the data to be inspected or easily changed\n- **ufoLib** allows the reading \u0026 writing of UFO files \u0026 the data within\n- **varLib** allows the building of [variable fonts](https://variablefonts.io/), as well as the  instancing of variable fonts (trimming the amount of stylistic range in a variable font, either to make small variable fonts or single static fonts).\n\nThere are more tools than these, but I am still learning about them.\n\nTo learn more about FontTools, you can [read the docs](https://fonttools.readthedocs.io/), or browse the [source code](https://github.com/fonttools/fonttools) (which includes documentation inside of many functions).\n\n### How is FontTools different from / related to FontParts? FontMake? FontBakery? AFDKO?\n\nParts of FontTools act as a major component of many other font-related tools.\n\nAs I was starting into font development, it was confusing to me that there were several tools with similar names and (sometimes) interdependencies. To be honest, I’m still learning things about how various Python libraries relate \u0026 interact, and others could speak in much more depth about how these things use FontTools. Here are four major tools that use FontTools in various ways:\n\n**[FontParts](https://fontparts.robotools.dev/en/stable/)**\n\n- Gives a logical structure to fonts, font info, glyphs, and contours (and [more](https://fontparts.robotools.dev/en/stable/objectref/objects/index.html)), and includes methods to manipulate these different objects to allow scripting workflows in type design.\n- Can be used as a part of script tools in RoboFont (but is intended to be usable in any font editor)\n- Can also be used in external Python scripts or via terminal commands to edit UFO files without requiring RoboFont.\n- Uses FontTools\n\n**[FontMake](https://github.com/googlefonts/fontmake/)**\n\n- A command-line tool to build binary fonts from sources\n- Can build `UFOs → OTF/TTF` or `Glyphs → UFOs → OTF/TTF`\n  - Uses [glyphsLib](https://github.com/googlefonts/glyphsLib) to go from Glyphs files to UFOs + Designspace files\n  - If building from Cubic (CFF) curves to Quadratic (TTF) curves, uses cu2qu for conversion\n  - Uses FontTools to build output binaries\n- Can also be used as a module in Python scripts for more-custom build setups\n- Primarily sponsored \u0026 maintained by Google Fonts\n- Uses FontTools to build prepared UFOs into OTFs \u0026 TTFs\n\n**[FontBakery](https://github.com/googlefonts/fontbakery/)**\n\n- A command-line tool to run quality-assurance checks on fonts in various formats: UFO, TTF, OTF\n- The checks that run test for many things that can cause fonts to not work as expected. Examples:\n  - Data tables that are missing or misformed\n  - Font name entries that don’t match expectations (e.g. they are too long, they are different within the same font family, etc)\n  - Font metrics that mismatch within a family\n  - Variable axes with ranges that don’t conform to the OpenType spec\n- There are some “universal” checks useful for almost any font, and there are checks specific to the expectations of individual font libraries such as Google Fonts or Adobe Fonts\n- Uses FontTools to “inspect” font data in order to run checks\n\n**[AFDKO](https://github.com/adobe-type-tools/afdko/) (Adobe Font Development Kit for OpenType)**\n\n- Somewhat similar functionality to FontMake, but primarily sponsored \u0026 maintained by Adobe\n- My favorite part: `otf2otc`, which makes OTC files out of a collection of OTF files ([example](https://github.com/arrowtype/recursive/blob/2c1c9d58e2130851cd6cee804d71ccce73b75805/src/build-scripts/make-release/make-otc-files.sh))\n- Includes tools for proofing and validating font files\n- Has some advantages for building CFF fonts, like better CFF hinting \u0026 compression\n- Uses FontTools to build prepared UFOs into OTFs \u0026 TTFs\n- (Note: I don’t have as much experience with the AFDKO, so it is something I still need to learn more deeply. My apologies if the summary above isn’t perfect, but I welcome contributions.)\n\n## Using FontTools for common font development tasks\n\n**Cloning the repo**\n\nTo work directly with these examples, you should have [Git set up on your computer](https://help.github.com/en/github/getting-started-with-github/set-up-git).\n\nTo try these examples, clone this repo onto your computer!\n\n```bash\n# first, use `cd` to navigate the parent directory of where you want to work\ngit clone https://github.com/arrowtype/fonttools-intro.git\ncd fonttools-intro # change directory into the cloned repo\n```\n\n**Setup**\n\n1. To use FontTools, you first have to [Download Python](http://python.org/download/) and install it if you haven’t already.\n\n2. To try the examples in this repo, set up a virtual environment. Navigate to this directory in a command line, then make a venv and activate it:\n\n```bash\npython3 -m venv venv      # creates a virtual environment in directory `venv`\nsource venv/bin/activate  # activates virtual environment\n```\n\nIf you close and come back to the folder later, use `source venv/bin/activate` again.\n\n3. Install FontTools with Pip:\n\n```bash\npip install fonttools\n```\n\n\n### TTX\n\nJust like web browsers have Developer Tools which allow you to “Inspect” the HTML, CSS, JS, and files that make up webpages, TTX allows you to convert binary fonts into human-readable XML so that you can inspect data.\n\nA common way to use TTX is to simply build a XML from an OTF or TTF file (replacing the all-caps placeholders with an actual font filepath):\n\n```bash\nttx examples/Recursive_VF_1.077.ttf # makes XML file examples/Recursive_VF_1.077.ttx\n```\n\nHowever, often, you only want to look at a single table of a font such as the `name` table. You also usually don’t need to save a new ttx file, but just want to check values. So, my favorite recipe is this:\n\n```bash\nttx -t name -o- examples/Recursive_VF_1.077.ttf # just shows the name table\n```\n\nIn that command, `-t name` specifies that only the name table should be converted to XML, and `-o-` specifies that the “output” should be nothing (and so, rather than a file, the XML is simply printed directly in the terminal). I remember this because `-o-` looks a bit like an emoticon owl.\n\nTTX will also compile a TTX file back into a binary font file. So, it is possible to TTX a font, make changes in the `.ttx` file, and then run TTX to save those edits into a working font.\n\n```bash\nttx examples/Recursive_VF_1.077.ttf # outputs examples/Recursive_VF_1.077.ttx\n# you can make some manual edits, e.g. changing name table entries\nttx examples/Recursive_VF_1.077.ttx # outputs examples/Recursive_VF_1.077#1.ttf\n```\n\nHowever, editing font files by editing the TTX is relatively slow and hard to repeat. For example, if you are processing a folder containing many static fonts within a type family, it will take time to covert from OTF/TTF to TTX, time to make the changes (even if you write a script to do so), and time to convert back to OTF/TTF. It will probably take a few minutes, and chances are, you may have to run the process many times while developing it, making it painfully slow. So, TTX is really best used as a font inspector. \n\nTo efficiently edit font data, it is much more useful to reach for `ttFont`.\n\n## TTFont\n\n`ttFont` makes it possible to manipulate font binaries “directly” with Python (some conversion is involved to/from font data \u0026 Python objects, but it is very fast). The basic use ttFont is in a Python script:\n\n1. Import ttFont with `from fontTools.ttLib import TTFont`\n2. Use it to open a font path `font = TTFont(fontPath)`\n3. Access font tables in the format of `font['head']`, and entries in the format of `font['head'].fontRevision` – refer to [the OpenType spec](https://docs.microsoft.com/en-us/typography/opentype/spec/otff#font-tables) for details on each table\n4. After updating data, save with `font.save(fontPath)`\n\n### A basic example/template using TTFont\n\nA basic example of a Python script using TTFont is in `examples/00-ttfont-template.py`. To run it, make sure you navigate to this folder in a terminal, have your venv activated, and have FontTools installed, then run:\n\n```bash\npython examples/00-ttfont-template.py examples/Recursive_VF_1.077.ttf\n```\n\nIt should output `→ name1 is 'Recursive Sans Linear Light'`.\n\n### Editing Font Names with TTFont\n\nIt’s worth noting that font files have many different entries for names for various purposes, listed under [“Name IDs”](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids). This may be surprising at first, but there are a lot of names needed for a usable font!\n\nA common need might be updating the family name of a font.\n\nOne method is provided at `examples/01-ttfont-name-updates.py`. This includes:\n\n- code comments to detail each line\n- helper functions to make it simpler to get \u0026 set names\n\nRun it from the directory above with the following command, include 1) a font path, and 2) a new family name:\n\n```bash\npython examples/01-ttfont-name-updates.py examples/Recursive_VF_1.077.ttf \"New Name\"\n```\n\n(The above command will output `NewName_VF_1.053.ttf`)\n\nNotice how *stupidly fast* it is to run that change. It takes a little longer to write the script, but with it, you can process many files, many times, in minimal time.\n\n## Subsetting a Font\n\nRunning the following in the command line will subset Recursive to have a basic Latin character set:\n\n```bash\nfontPath=\"examples/Recursive_VF_1.077.ttf\"\npyftsubset $fontPath --flavor=\"woff2\" --output-file=\"${fontPath/'.ttf'/--subset-GF_latin_basic.woff2}\" --unicodes=\"U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD\"\n```\n\nThis can be further configured with [other unicodes \u0026 unicode ranges](https://en.wikipedia.org/wiki/List_of_Unicode_characters).\n\nAs with many command line tools, you can learn how more about using it by running `pyftsubset --help`.\n\n## Instancing a Variable Font\n\nYou can use the Instancer to make smaller instances of a variable font, “pinning” axes to a single value or limiting them to given ranges. You can run it as a python module or from the command line. (One current limitation: axis ranges must include their default position, such as Recursive’s `wght` default of `300`.) For example:\n\n```bash\nfonttools varLib.instancer examples/Recursive_VF_1.077.ttf CASL=1 wght=300:850 # outputs examples/Recursive_VF_1.077-partial.ttf\n```\n\nThe docs in [instancer.py](https://github.com/fonttools/fonttools/blob/master/Lib/fontTools/varLib/instancer.py) are very good.\n\n## Putting several techniques together\n\nThe script `examples/02-make-trial-font.py` puts several of these techniques together to make a “trial” font, keeping characters for specified unicodes while hiding the rest with a “replacer” glyph.\n\n```bash\npython examples/02-make-trial-font.py examples/Recursive_VF_1.077.ttf -r \"asterisk\"\n```\n\nThis script also makes use of Python `argparse` to allow multiple arguments to be passed in from the command line. Run `python examples/02-make-trial-font.py --help` to see more options for the script (or look at the code itself).\n\nThe result of the command above is a font that subsets out non-basic Latin characters, and updates their previous unicodes to point to the asterisk glyph. In text, the result looks like this:\n\n![French Wikipedia with a Recursive “Trial” font](assets/2020-06-18-23-16-22.png)\n\n(If you wish to run this script on a folder of fonts, one method is shown in [this GitHub Gist](https://gist.github.com/arrowtype/db9562858a56ab45010e390c3f788b84).)\n\n### Other useful things to know about TTFont\n\nYou can set data into almost any table \u0026 entry. For example, if the TTX for the `head` table includes `\u003cfontRevision value=\"1.053\"/\u003e`, you can edit that with:\n\n```python\nfont['head'].fontRevision = 1.054\n```\n\nYou can delete a whole table like this:\n\n```python\ndel font[\"mvar\"]\n```\n\nThere’s a lot more to it, much of which I still need to learn. As with many things in Python, [the `help()` function](https://docs.python.org/3/library/functions.html#help) is your best friend! A good start: one the command line, run `python3`, import TTFont, and then run `help(TTFont)`. (You can exit the help view by hitting **q**, and exist out of the Python mode by hitting **control d**.)\n\n```bash\n$ python3\n\u003e\u003e\u003e from fontTools.ttLib import TTFont\n\u003e\u003e\u003e help(TTFont)\nHelp on class TTFont in module fontTools.ttLib.ttFont # etc etc\n```\n\n------------------------------------------------------------------\n\n## Also useful\n\n- https://github.com/google/woff2 to compress/decompress woff2 files\n\n## Other learning resources for FontTools\n\n- [The FontTools Docs](https://fonttools.readthedocs.io/en/latest/index.html)\n- [A very good TTFont tutorial by Lynne Yun](https://github.com/lynneyun/Tutorials/blob/6cabd407054431559b30d66d9b664462bb1d58b7/FontTools%20%26%20DrawBot/Navigating%20TTFs%20with%20fontTools.ipynb)\n\n## Fonts used\n\nThis uses Recursive VF 1.053, available as an OFL on GitHub ([permalink](https://github.com/arrowtype/recursive/blob/006828dd941878bf0819a97c9d6286f24972bb16/fonts_1.031/Variable_TTF/Recursive_VF_1.031.ttf)).\n\nThe slide presentation also uses Name Sans, [available from Future Fonts](https://www.futurefonts.xyz/arrowtype/name-sans).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farrowtype%2Ffonttools-intro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farrowtype%2Ffonttools-intro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farrowtype%2Ffonttools-intro/lists"}