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.3.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.3.0-py3-none-win_amd64.whl (6.1 MB view details)

Uploaded Python 3Windows x86-64

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

Uploaded Python 3Windows x86

fontc-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl (6.9 MB view details)

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

fontc-0.3.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl (7.3 MB view details)

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

fontc-0.3.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (13.4 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.3.0.tar.gz.

File metadata

  • Download URL: fontc-0.3.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.3.0.tar.gz
Algorithm Hash digest
SHA256 bd4f1fe942c108998a420e5deb0e150228ed5a5a5a2173b2b642634e623c9f65
MD5 1cc3faf992c8fbe35acd066128fb2882
BLAKE2b-256 c637be4af8c1a633669797996116b2c8f0688d735112b2c77a74ffeebccd5f7e

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: fontc-0.3.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 6.1 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.3.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 9dc91526547a25fca62c5262861164da00d99c8f1d29fce90a83c4ecb57bd62e
MD5 cd743a760d788dcf46337eeec5119c63
BLAKE2b-256 f7b0e37352413c9a721a487971e0ef1941a6fa23ed726d82cc6c578bfacc690a

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: fontc-0.3.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.3.0-py3-none-win32.whl
Algorithm Hash digest
SHA256 cb675b3287f4980755fe2dd2703365559849e547e25fad6eb8891c919a3bf99c
MD5 437e3e8fe4942c9b424aea55e5ccca5b
BLAKE2b-256 06674e6b3e4847d55487d9390ab3a83630c054834b41c5d7c93d59520b46abdc

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for fontc-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl
Algorithm Hash digest
SHA256 7ad9425cd558e78ae5ab56966d93e93e5022d2c9ec0f41315aac7e949e32d7c9
MD5 d7eda7ba3cb17174cf391b4edebb3c8f
BLAKE2b-256 45d471cdad500ea565e0c85344921f9ede686b89716e28a22789b2cd862a6509

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.3.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.3.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.3.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl
Algorithm Hash digest
SHA256 7ce6092bf98d13acf38f94656fd6a8b94ab50765d6495ac9fad27d49b59f9a1d
MD5 41cabba65678a75dd474fb02975d4a50
BLAKE2b-256 751530c159f0d4900276453f57421e1803ad847cf25043a5c5f22a113ee26356

See more details on using hashes here.

Provenance

The following attestation bundles were made for fontc-0.3.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.3.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.3.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 8ef1e9b77a063f7b78cfd465050ef956f510e85fb5933e8e133bddbdc6ff0121
MD5 6e295b2a51219bad5e276385b06e37b9
BLAKE2b-256 09dbac0d3c9942a7778797185cd95bf69628cdbd2f48ee84d56beb4b35b2b393

See more details on using hashes here.

Provenance

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