Skip to main content

A font compiler written in Rust.

Project description

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

$ cargo run -p fontc -- resources/testdata/wght_var.designspace

Emit IR

If you pass the --emit-ir option, the IR will be written to disk inside the build working directory. This can be helpful when troubleshooting.

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

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

As of 6/4/2023 we intend to:

  • Get to the point where Oswald compilation matches fontmake
  • Get to the point where ever more of the families for which we have source compile to a form that matches fontmake, or differs only in well understood ways
  • Provide a Glyphs plugin to allow push-button use of the new compiler
  • Once there are no known issues, switch Google Fonts to exclusively use fontc

We are discarding our prior plan to make fontmake (Python) call into Rust as it appears to be more complex than initially anticipated and higher risk than migrating on a per-family basis.

For context see https://github.com/googlefonts/oxidize/blob/main/text/2022-07-25-PROPOSAL-build-glyphs-in-rust.md and the discussion on https://github.com/googlefonts/oxidize/pull/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

Comparing branch performance with hyperfine

Only relatively large changes are effectively detected this way:

To compare current branch to main when building Oswald:

$ hyperfine \
  --parameter-list branch main,$(git rev-parse --abbrev-ref HEAD) \
  --setup "git checkout {branch} && cargo build --release" \
   --warmup 5 --runs 250 \
   --prepare 'rm -rf build/' \
   "target/release/fontc --source ../OswaldFont/sources/Oswald.glyphs"

...noise...

Summary
  target/release/fontc --source ../OswaldFont/sources/Oswald.glyphs (branch = nocs) ran
    1.09 ± 0.09 times faster than target/release/fontc --source ../OswaldFont/sources/Oswald.glyphs (branch = main)

# Yay, it seems to be faster!

Running samply

https://github.com/mstange/samply gives a nice call tree, flame graph, etc. Sample usage:

# Assuming current directory is the root of fontc
$ (cd .. && git clone https://github.com/mstange/samply)
$ (cd ../samply && cargo build --release)
$ ../samply/target/release/samply record target/release/fontc ../OswaldFont/sources/Oswald.glyphs

Running flamegraph

flamegraphs of fontc are very handy. They are most easily created using cargo flamegraph:

# Minimize the impact of logging
$ export RUST_LOG=error
# Symbols are nice, https://github.com/flamegraph-rs/flamegraph#improving-output-when-running-with---release
$ export CARGO_PROFILE_RELEASE_DEBUG=true

# Build something and capture a flamegraph of it
$ rm -rf build/ perf.data flamegraph.svg && cargo flamegraph -p fontc -- ../OswaldFont/sources/Oswald.glyphs

# TIPS

# On macOS you might have to pass `--root` to cargo flamegraph, e.g. cargo flamegraph --root ...as above...

# If you are losing samples you might want to dial down the rayon threadcount
# You'll see a perf error similar to:
Warning:
Processed 5114 events and lost 159 chunks!

Check IO/CPU overload!

Warning:
Processed 5116 samples and lost 35.01%!

# Fix is to lower the threadcount:
$ export RAYON_NUM_THREADS=16

Focused flames

https://blog.anp.lol/rust/2016/07/24/profiling-rust-perf-flamegraph/ offers examples of filtering flamegraphs. This is very useful when you want to zoom in on a specific operation. For example, to dig into fea-rs:

# Generate a perf.data
# You can also use perf record but cargo flamegraph seems to have nice capture settings for Rust rigged
$ rm -rf build/ perf.data flamegraph.svg && cargo flamegraph -p fontc -- ../OswaldFont/sources/Oswald.glyphs

# ^ produced flamegraph.svg but it's very noisy, lets narrow our focus
# Example assumes https://github.com/brendangregg/FlameGraph is cloned in a sibling directory to fontc
$ perf script | ../FlameGraph/stackcollapse-perf.pl | grep fea_rs | ../FlameGraph/flamegraph.pl > fea-flame.svg

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"

Releasing

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

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fontc-0.5.0.tar.gz (1.7 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

fontc-0.5.0-py3-none-win_amd64.whl (6.5 MB view details)

Uploaded Python 3Windows x86-64

fontc-0.5.0-py3-none-win32.whl (6.0 MB view details)

Uploaded Python 3Windows x86

fontc-0.5.0-py3-none-musllinux_1_1_x86_64.whl (7.6 MB view details)

Uploaded Python 3musllinux: musl 1.1+ x86-64

fontc-0.5.0-py3-none-musllinux_1_1_aarch64.whl (7.2 MB view details)

Uploaded Python 3musllinux: musl 1.1+ ARM64

fontc-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

fontc-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (7.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

fontc-0.5.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (14.1 MB view details)

Uploaded Python 3macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file fontc-0.5.0.tar.gz.

File metadata

  • Download URL: fontc-0.5.0.tar.gz
  • Upload date:
  • Size: 1.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fontc-0.5.0.tar.gz
Algorithm Hash digest
SHA256 995d176a967f9b8f9d6de9f502eaf40492d71ce98a34cc75ae12fdedfd53d95d
MD5 3f8e9db040ed631a2b9ad4b5ce3943e2
BLAKE2b-256 c387e7ad29f71c677e978581b4e909212951c9cb4bf74d84d3bead1a88c3d4e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0.tar.gz:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: fontc-0.5.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 6.5 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fontc-0.5.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 33fd54e7a53d76b2e8e0d0f6c31bbac0b6e5b70dc4557eb45b3424cd34e937a7
MD5 1e1450181a335123cf0f639db3c96795
BLAKE2b-256 6f3653472c04d8b452ba64b0e81e7554eaa848863428e76614e84423a2412901

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-win_amd64.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-win32.whl.

File metadata

  • Download URL: fontc-0.5.0-py3-none-win32.whl
  • Upload date:
  • Size: 6.0 MB
  • Tags: Python 3, Windows x86
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fontc-0.5.0-py3-none-win32.whl
Algorithm Hash digest
SHA256 e23f191e95c87dc1b2dbed29f125cd3c40b36c32fd3cd77cb7dd9069c3125017
MD5 7cecd5ba61f7d14b7920110d05f94077
BLAKE2b-256 c0a8ba4abdd3ef8bd59357b333f191cea874da2da23d98c07bfa768416a49553

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-win32.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-musllinux_1_1_x86_64.whl.

File metadata

File hashes

Hashes for fontc-0.5.0-py3-none-musllinux_1_1_x86_64.whl
Algorithm Hash digest
SHA256 8dae0dbf7793cb1b54b8d7931f3d562f78576d1f000b5ec6a1adc5db423813c3
MD5 aed399031b7b580b6b6173a9c4f8ef1b
BLAKE2b-256 7062940c6c59a211c4504382762ced2afbb65abcecfc5dedd95d0fd1ea2c8cf1

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-musllinux_1_1_x86_64.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-musllinux_1_1_aarch64.whl.

File metadata

File hashes

Hashes for fontc-0.5.0-py3-none-musllinux_1_1_aarch64.whl
Algorithm Hash digest
SHA256 3bcadcd3eb986f6e9ee1a8dcf11e1a28ec95a56ccb336025f181594473a336cd
MD5 fe8c69ead61eef2b7ced1c24cc018c74
BLAKE2b-256 7c04582590f52d64cc69e49f3a0ab351e5328cadec4132cc2b2351fd7dabefd4

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-musllinux_1_1_aarch64.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for fontc-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 aa52ac539c17ea228d6b86cdefb8ffa0747f2ae3cac6ea0ecfbd9cc0a764f4bb
MD5 99bb4e3984d8fad581f93af74ed6d6b8
BLAKE2b-256 d3498faa3b55164e5b3db1d48c8b1dc83d0cc1c56ff9974309bf4d7d78aed80b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for fontc-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 aee72b2efd142fde35f26b5e6f4bd7de59667fc0560fd749cf7e28f4ba372ac8
MD5 d3b62700c6adc0b905059f187229875c
BLAKE2b-256 e4bae9b2ed8ad228e3ed9563016ec862420870a03a57f3a42bf33ca54ceb10f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fontc-0.5.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for fontc-0.5.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 06dc78b5a2324313a0a26876247af049becd66823fe2513755c2b39a1f33cad2
MD5 5149f68e05b37f5be90de23955721b1e
BLAKE2b-256 6bdaecbd34f783774b8f2a139cf08b2d228f734f0c1aee96e71f838b6ad0ea35

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.5.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on googlefonts/fontc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page