googlefonts / googlefonts/fontc

fontc ignores declared filter ordering from source, runs propagateAnchors before decomposeTransformedComponents

Open
#2,020 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
193
Forks
21
Avg merge
1d 20h
Merged PRs (30d)
60

Description

fontc hardcodes the order of operations for ufo2ft filters rather than respecting the ordering declared in the source's `com.github.googlei18n.ufo2ft.filters` lib key. In particular, `propagateAnchors` always runs before `decomposeTransformedComponents`, regardless of what the source says.

GoogleSans declares `[decomposeTransformedComponents, propagateAnchors, flattenComponents]` and fontmake runs them in that order, but fontc runs `propagateAnchors` first. This produces different GPOS output for composite glyphs that contain transformed components.

In `fontir/src/glyph.rs`, `propagate_all_anchors` is called at line 799

https://github.com/googlefonts/fontc/blob/1a0a7526afaed77638f0d3d3d4f1a69940b06c81/fontir/src/glyph.rs#L795-L800

while `apply_optional_transformations` (which handles `DECOMPOSE_TRANSFORMED_COMPONENTS` and `FLATTEN_COMPONENTS`) runs later [at line 885](https://github.com/googlefonts/fontc/blob/1a0a7526afaed77638f0d3d3d4f1a69940b06c81/fontir/src/glyph.rs#L885). fontc reads which filters are enabled from the source, but ignores their relative ordering.

For example, GoogleSans-Italic has three Ethiopic composite glyphs (`hoa-ethiopic`/U+1207, `koa-ethiopic`/U+12AF, `oaGlottal-ethiopic`/U+2D8A) where this matters. Each is a composite of:
- One untransformed component with a `top` anchor (e.g. `ho-ethiopic`)
- One 180°-rotated component (e.g. `_geez-part-11` with transform `[-1, 0, 0, -1, dx, dy]`)

With the source's declared order (decompose first):
1. `decomposeTransformedComponents` fully decomposes the composite (mixed contour+component not allowed)
2. `propagateAnchors` sees no components → no `top` anchor propagated → no MarkToBase rule

With fontc's hardcoded order (propagate first):
1. `propagateAnchors` sees the original composite → propagates `top` from `ho-ethiopic` → MarkToBase rule emitted
2. `decomposeTransformedComponents` decomposes later (anchors already propagated)

This accounts for 3 extra MarkToBase base glyphs in fontc's GPOS that fontmake doesn't produce.

Swapping the filter order in the source to `[propagateAnchors, decomposeTransformedComponents, ...]` makes both compilers agree.

One may argue that in general it's more correct to propagate anchors for composite glyphs before decomposing them, so the anchors don't get lost in the decomposition (and the inline comment from the code referenced above says exactly that).
And for the particular glyphs affected fontc produces a better output (more mark attachments than fontmake), and the relative ordering might not have been set intentionally by the font developers. We just don't know.

At this stage, I'm just filing this for future reference in case we find similar issues in other fonts, but I don't think it should be a blocker for fontc 1.0.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.