googlefonts / googlefonts/fontc
Compile DS+UFO fonts with non-identical (but compatible) per-master feature files
- Dominant language
- Rust
- Stars
- 193
- Forks
- 21
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 65
Description
Split out of #1829. That issue was about the naive "are the per-master features.fea identical" check; #2027 fixed that (canonicalize includes, compare significant token streams) and now correctly errors on any real divergence instead of silently using only the first master's FEA.
This issue tracks the harder half: actually compiling fonts whose masters have different-but-compatible feature files, which fontc currently rejects with "non-identical feature files".
Today fontc parses one root FEA (the first master's) and assumes every master's hand-written FEA is identical; variable kern/mark/feature-variations are built programmatically from the IR. fontmake instead can compile each master's FEA independently and merge the resulting GPOS/GSUB tables with varLib, so it handles masters that share a structure but differ in values, and some that differ structurally.
Three ways out, not mutually exclusive:
1. Zip compatible FEA in fea-rs. Give fea-rs the ability to combine N compatible non-variable feature files into one variable feature file (variable scalars where values differ), then error if they're genuinely incompatible. I belive this was Colin's suggestion in the thread (https://github.com/googlefonts/fontc/issues/1829#issuecomment-4014520396) and the most self-contained. It keeps a single-compile pipeline and reuses the variable-scalar machinery fea-rs already has.
2. Compile-per-master then table-merge. Mirror what fontmake/varLib does: compile each master's FEA to OT tables, merge at the table level. Handles the widest set of sources (including some structural divergence) but pulls a varLib-style GPOS/GDEF merge into fontc, which is a large new surface.
3. Source migration to variable FEA. Push sources onto a single shared variable features.fea so no merge is needed. See @rsheeter's preference in the thread ("I really really don't want to merge", https://github.com/googlefonts/fontc/issues/1829#issuecomment-3647489179). I recently contributed a python script in GS repo to do just that, which we could potentially move to fonttools. However, we don't control third-party sources, so fontc still needs a defined behavior for the ones that don't migrate.
Scope, from the 2026-05-29 crater run (26 repos with divergent FEA):
Fonts shipped as variable on Google Fonts where fontmake succeeds and fontc bails (what this issue needs to fix):
- Same structure, differ only in values (the zip case): EkType/Anek (Bangla, Gujarati, Gurmukhi, Latin, Odia, Telugu, 9 masters each), Linefont (12 masters), Tektur (4), Noto Sans Hanifi Rohingya (2). NotoSansThai is a related case that used to pass the old length-only check and shipped subtly wrong (#2027 now errors on it).
- Structurally divergent (only the table-merge path helps): Source Serif 4 (Roman + Italic), Arimo Italic, Jost.
Out of scope: many divergent-FEA fonts (SIL Harmattan/Padauk/Scheherazade/Andika/Charis, Spectral, some IBM Plex) are static-only on Google Fonts and fail in fontmake too. Their FEA is divergent enough that varLib can't merge the compiled tables either. Not fontc-specific.
I would not mark this as a fontc 1.0 blocker, since the affected sources are a minority. fontc now fails loudly rather than silently corrupting, and the remedy is either upstream source migration or a substantial new merge capability.
But I hear @cmyr already has some WIP towards this...
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with fontc's current rejection of non-identical feature files and read the fea-rs variable-scalar machinery described in the issue. Compare the zip-compatible-FEA and compile-per-master table-merge approaches, using the listed Google Fonts cases as examples. Done means compatible masters compile without rejection and genuinely incompatible feature files still produce a defined error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100