Skip to content

Repository files navigation

CI Status Crates.io

fontc

Wherein we pursue oxidizing fontmake. For context around where fontmake came from see Mr B goes to Vartown.

Converts source to IR, and then IR to font binary. Aims to be safe and fast.

References

  • Intermediate Representation (IR)
  • Editor perspective note from Just
  • Units
    • Fonts have all the best units; distinguishing between them turns out to matter.

But why?

Two main reasons:

  1. Speed
    • The python compiler is too slow and we don't think we can plausibly make it fast enough
  2. A key part of Google Fonts technical strategy is to get off both Python and C++, consolidating on Rust

So, Rust compiler time!

image

(https://xkcd.com/303/ remix)

Are we there yet?

https://googlefonts.github.io/fontc_crater/ tracks our progress in making the new compiler match the old one.

Getting started

Install the latest version of Rust, https://www.rust-lang.org/tools/install.

Build a simple test font

# Build a .designspace file
$ cargo run -p fontc -- resources/testdata/wght_var.designspace

# Build a .glyphs file
$ cargo run -p fontc -- resources/testdata/glyphs3/WghtVar.glyphs

# Build a .fontra file
$ cargo run -p fontc -- resources/testdata/fontra/minimal.fontra

Emit debug output

If you pass the --emit-debug option, additional files that can be helpful when troubleshooting are written to a debug directory inside the build working directory.

$ cargo run -p fontc -- --emit-debug resources/testdata/wght_var.designspace
$ ls build/debug/

Sources to play with

Google Fonts has lots, you could try https://github.com/rsheeter/google_fonts_sources to get some. Once you have them you could try building them:

cargo run -p fontc -- ../google_fonts_sources/sources/ofl/notosanskayahli/sources/NotoSansKayahLi.designspace

Building lots of fonts at once

There is an included fontc_crater tool that can download and compile multiple fonts at once; this is used for evaluating the compiler. For more information, see fontc_crater/README.md.

Plan

We focus on making the compiler better and more robust, ensuring it can handle the full variety of font sources in the Google Fonts ecosystem.

Our progress is tracked via fontc_crater, which compares fontc against fontmake for thousands of fonts.

Key milestones reached:

We are currently:

  • Ensuring ever more font families compile correctly and match fontmake output (or differ in well-understood ways).
  • Moving towards using fontc exclusively for all Google Fonts builds.

For context see https://github.com/googlefonts/oxidize/blob/main/text/2022-07-25-PROPOSAL-build-glyphs-in-rust.md and the discussion on googlefonts/oxidize#33.

Using a local copy of fontations

It is quite common to find we need changes in https://github.com/googlefonts/fontations to add a feature or fix a bug. Prior to a release being available modify the root Cargo.toml to point to either a local clone or a branch:

# Local copy
[patch.crates-io]
font-types =  { path = "../fontations/font-types" }
read-fonts =  { path = "../fontations/read-fonts" }
write-fonts = { path = "../fontations/write-fonts" }
skrifa =  { path = "../fontations/skrifa" }

# Branch
[patch.crates-io]
font-types = { git="https://github.com/googlefonts/fontations.git", branch="box" }
read-fonts = { git="https://github.com/googlefonts/fontations.git", branch="box" }
write-fonts = { git="https://github.com/googlefonts/fontations.git", branch="box" }
skrifa = { git="https://github.com/googlefonts/fontations.git", branch="box" }

Dependency map

Shows the non-dev dependency relationships among the crates in the repo.

%% This is a map of non-dev font-related dependencies.
%% See https://mermaid.live/edit for a lightweight editing environment for
%% mermaid diagrams.
graph
    %% First we define the nodes and give them short descriptions.
    %% We group them into subgraphs by repo so that the visual layout
    %% maps to the source layout, as this is intended for contributors.

   fontc{{fontc\nCLI font compiler}}
   fontra2fontir[fontra2fontir\nconverts .fontra files to our IR]
   glyphs2fontir[glyphs2fontir\nconverts .glyphs files to our IR]
   ufo2fontir[ufo2fontir\nconverts from a \n.designspace to our IR]
   fontir[fontir\nthe IR for fontc]
   fontbe[fontbe\nthe backend of font compilation\nIR -> binary font]
   fea-rs[fea-rs\nParses and compiles\nAdobe OpenType feature files]
   fontdrasil[fontdrasil\nCommon types and functionality\nshared between all layers of fontc]

    %% Now define the edges.
    %% Made by hand on March 20, 2024, probably not completely correct.
    %% Should be easy to automate if we want to, main thing is to
    %% define the crates of interest.
    fontbe --> fontir
    fontbe --> fea-rs
    fontc --> fontbe
    fontc --> fontir
    fontc --> glyphs2fontir
    fontc --> fontra2fontir
    fontc --> ufo2fontir
    fontra2fontir --> fontir
    glyphs2fontir --> fontir
    ufo2fontir --> fontir
Loading

Evaluation & Testing

We use several tools to evaluate the correctness of fontc compared to fontmake:

  • fontc_crater: A tool for compiling thousands of fonts and generating a compatibility report.
  • ttx_diff: A Python tool for comparing the XML representation of fonts (via ttx) produced by fontc and fontmake.
  • otl-normalizer: A tool for normalizing OpenType Layout subtables for easier comparison.

Profiling

For tips on profiling, see docs/performance.md.

Contributing

We have included a few git hooks that you may choose to use to ensure that patches will pass CI; these are in resources/githooks.

To run the pre-push step manually:

$ ./resources/githooks/pre-push

If you would like to have these run automatically when you commit or push changes, you can set this as your git hooksPath:

$ git config core.hooksPath "resources/githooks"

Cargo.lock

Cargo.lock is committed, so a given revision builds the same dependency graph everywhere, and CI builds with --locked. Dependency updates are deliberate: run cargo update and open a PR, most naturally at the start of a release cycle so crater validates the refreshed graph. A weekly CI job builds with the newest compatible dependencies to surface upstream breakage early, and audits the pinned graph against RustSec advisories and crates.io yank status (triaged findings: .cargo/audit.toml).

If Cargo.lock conflicts when merging or rebasing, don't resolve it by hand: take either side whole and let cargo re-sync it against the merged manifests (it only changes the entries it has to):

$ git checkout --theirs -- Cargo.lock  # either side works
$ cargo check
$ git add Cargo.lock

Releasing

See https://github.com/googlefonts/fontations#releasing

To update the Glyphs plugin see https://github.com/googlefonts/fontc-export-plugin.

About

Wherein we pursue oxidizing (context: https://github.com/googlefonts/oxidize) fontmake.

Resources

Stars

186 stars

Watchers

21 watching

Forks

Releases

Packages

Used by

Contributors

Languages