Skip to main content

A compiler for fonts.

Project description

CI Status Crates.io

fontc

Where in 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.2.0.tar.gz (1.6 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.2.0-py3-none-win_amd64.whl (6.0 MB view details)

Uploaded Python 3Windows x86-64

fontc-0.2.0-py3-none-win32.whl (5.7 MB view details)

Uploaded Python 3Windows x86

fontc-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl (6.8 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64musllinux: musl 1.1+ ARM64

fontc-0.2.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl (7.2 MB view details)

Uploaded Python 3manylinux: glibc 2.12+ x86-64musllinux: musl 1.1+ x86-64

fontc-0.2.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (13.3 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.2.0.tar.gz.

File metadata

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

File hashes

Hashes for fontc-0.2.0.tar.gz
Algorithm Hash digest
SHA256 fc12b9efa443594a464dacc0172070b80a293335a70ce53b0631a29f49488f06
MD5 8713d8a7aded3be9ced1cf87ffd12130
BLAKE2b-256 73d3fbeadbbbd6f5370b0875b8efdba85e07389e5316b0998646f8256ae7b743

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.2.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.2.0-py3-none-win_amd64.whl.

File metadata

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

File hashes

Hashes for fontc-0.2.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 91d1d2ad866f389cf15a75db9161ee76d1bc5afd0505c07a27a1d26c587d1e47
MD5 489b8ddcaf4b3d40bc35e30cc58939e6
BLAKE2b-256 bc8fbb118e255fd107939e541687bede79e6a23c77d7504d11980d5458a3962c

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.2.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.2.0-py3-none-win32.whl.

File metadata

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

File hashes

Hashes for fontc-0.2.0-py3-none-win32.whl
Algorithm Hash digest
SHA256 c7157a87282146b5462444561fb5f34b19c37d388c8b9fc8a5ebd183f5dd7fcd
MD5 6c460a10a1aed1d37eb08fe88ab91669
BLAKE2b-256 8207709c1e8c9d972cbd73fc02a50a2b013e56f0b19d5f925785b4d4006a643d

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.2.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.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl.

File metadata

File hashes

Hashes for fontc-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl
Algorithm Hash digest
SHA256 fb2720660321968c7e1a57a37d6f63dda794aa5ebee439407e364be5df83e7df
MD5 aad1b3d1abfaaf74379efc8c7cf94f90
BLAKE2b-256 7ded5b5f88934e7544d8907b7e3b7493e419049b4e8fd63a8b2dec59b303bdac

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.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.2.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl.

File metadata

File hashes

Hashes for fontc-0.2.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl
Algorithm Hash digest
SHA256 ac55f355fb1c48bbcbc616e9f896a5ba588524fbda32c36abf34923357aef1a2
MD5 bbf1e153b57e24e9b3cd322df47aca6c
BLAKE2b-256 80deb786dd4f8a2203a926e2f60323527928872b7c67dee98c4d8655d41419b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.2.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.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.2.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.2.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 eb8a8d438286a4cf9414a8619b9b2b24872abb2137db4058f7da3532edd4839b
MD5 de6a43d2bbd3832e62817a52d6e11fb1
BLAKE2b-256 8ebb409be66f56d9b17830af4bda3366f4aff23693b0efadba9302bf1fcc67e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.2.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