Slice

What happens on 775 real fonts

Slicing 775 real variable fonts from Google Fonts

The conformance corpus in tests/suite/ is 298 cases over 14 fixtures, thirteen of them synthetic. Passing all of it says the program does what we thought to ask for. This is the other question: pointed at fonts nobody designed a test around, does it crash, does it produce a readable font, and does that font agree with fontTools?

Reproduce with:

cargo build --release -p slice-cli
tools/corpus-sweep.py --corpus /path/to/google/fonts --compare --json sweep.json

The corpus

google/fonts at the checkout used here: 3869 font files across ofl, apache and ufl, of which 775 carry an fvar. Every one of them has glyf outlines — Google Fonts ships no CFF2 variable fonts at all, so this sweep says nothing about the CFF2 path, which remains exercised only by a four-glyph synthetic fixture.

Sizes run from 31 KB to 49 MB, median 235 KB. The large end is CJK: the Chiron and Noto Serif SC families.

Result

Two jobs per font — every axis pinned at its default, and the first axis restricted to its upper half — so 1481 jobs from 775 fonts.

   
jobs succeeded 1479 / 1481 (99.9%)
static (every axis pinned) 774 / 775 (99.9%)
partial (one axis narrowed) 705 / 706 (99.9%)
panics 0
outputs fontTools could not open 0
glyphs compared against fontTools 177,154
outline or advance disagreements 0
inputs modified 0 of 775, sha256-verified before and after

Both remaining failures are the same font, and neither is a wrong answer: fontTools cannot instance it, so there is no reference to compare against. See below.

Nothing produced a malformed font, and nothing crashed.

The one real defect this found

axisregistry/tests/data/OpenSansCondensed-Italic[wght].ttf has a two-axis STAT with an AxisValue whose AxisIndex is 2. fontTools 4.62.1 cannot instance it at all — instantiateVariableFont raises IndexError: list index out of range from designAxes[axisValueTable.AxisIndex].AxisTag.

This program did instance it, and produced a valid 3431-glyph font. That looked like a robustness win and was not: we survived because we were not looking. The out-of-range record was copied straight through. An AxisValue naming an axis the design-axis array does not have cannot describe a location, so it is now dropped — that font’s STAT goes from 9 axis values to 1, all well-formed. Fixed in 5acd6cb, with a regression test that asserts in both directions, since dropping every record would also satisfy “no dangling record survived”.

The tier-3 line still reports this font, because fontTools cannot produce a reference to compare against. That is a limitation of the comparison, not a disagreement.

The gap this quantified, and closed

The first run of this sweep found that a GDEF item variation store – variable kerning and anchors – blocked partial instancing on 365 of the 706 variable fonts that have an axis worth narrowing, 52%, of which 184 had more than one axis. That was the largest limitation in the program, and the corpus could never have shown it: the one fixture with a GDEF variation store was used only by cases that pinned every axis, and at the time this was written that looked harmless, on the belief that a static instance legitimately drops the store. It does not – each value has to be resolved at the pinned location first – and the static path was not even dropping it: it copied GDEF and GPOS through untouched, so every static instance kerned like its default master. That was found in October 2026 from a user’s report, and static.gdef-varstore.kerning-applied-at-the-pinned-location now holds it; see tools/kerning-compare.py in tools/README.md for what it measured.

It is fixed. varstore::rebuild, written for CFF2’s HVAR, re-tents a store that carries its own deltas, and that is the same operation GDEF needs; wiring it up took nine lines and a guard. Partial instancing went from 341/706 (48.3%) to 705/706 (99.9%) on the same corpus, and partial.variable-kerning-survives-a-restricted-range now holds it, with a check that samples an interior location because kerning frozen at the default is right at the default and wrong everywhere else.

This is the argument for sweeping a real corpus in one sentence: the gap was documented before the sweep, as one bullet among eight, weighted the same as “the progress bar does not animate”. The sweep is what said it was the whole ballgame.

Speed

Median slice 23 ms, p95 100 ms, slowest 2.67 s (a 49 MB CJK font). The whole corpus is 54 seconds of CPU in the Rust binary; the sweep’s twelve-minute wall time is almost entirely fontTools building reference instances to compare against.

What this does and does not license

It licenses a strong claim about both instancing paths: 1479 of 1481 jobs over 775 real fonts, 177,154 sampled glyphs compared against the reference implementation, zero disagreements, zero crashes, zero malformed outputs.

It licenses nothing about CFF2, because the corpus contains no CFF2 variable font. It licenses nothing about overlap removal, which the sweep does not exercise — that remains the least externally validated part of the program, because there is no reference implementation to diff it against.