Repository navigation
Nested composites with a per-master-varying 2×2 are decomposed differently from ufo2ft #2154
Description
Activity
Both outputs are valid fonts. do we care?
I don't think we do. Honoring the pre/post ordering of ufo2ft custom filters feels to me a bit out of scope unless it turns out to solve some actual problem besides fontmake fidelity.
The pre/post distinction only comes into play when the source carries a
flattenComponentsentry as a pre-filter in its ufo2ft filters lib key, and that's not what gftools does: it passes--filter FlattenComponentsFilter --filter DecomposeTransformedComponentsFilterto fontmake with noprekwarg, soFlattenComponentsFiltertakes its class default (which ispre=False, i.e. a post-filter) whileDecomposeTransformedComponentsFilteris a pre-filter by default. The effective ufo2ft order is then:decompose transformed -> decompose varying 2x2s -> flattensame as fontc (that's what #929 fixed).
The crater rows you annotated in googlefonts/fontc_crater#97 are default-mode builds (crater skips gftools mode for any source whose file stem starts with "noto",
should_build_in_gftools_mode), so the only flattening in play is the one the source's lib entry asks for, ordered as you describe.Noto LGC used to have that pre-filter in the masters' userData, probably a leftover from a ufo2glyphs round trip. I asked for it to be removed back in 2024 (notofonts/latin-greek-cyrillic#489) and it was dropped in June 2025. The crater still sees it because four google/fonts families declare latin-greek-cyrillic as their source: notosans and notoserif at c4a321e1, notosansdisplay and notoserifdisplay at 41644bc7 (Feb 2023), so the repo is built at both commits. The c4a321e1 targets are identical in today's run (so don't show up on crater), and at the old commit only the two glyphs you list are affected.
FWIW flattening before the required decompositions is strictly worse.
(quick recap: the pre/post distinction comes from Glyphs.app's PreFilter/Filter export parameters, and the thing in the middle is the set of outline transformations required by the output format, e.g. decomposing mixed glyphs, with overflowing F2Dot14 or varying 2x2 across masters, cu2qu, overlap removal for statics)
Flattening only rewrites component references, it doesn't touch contours, so the only thing the filter order changes is what this middle stage gets to see. A pre-flattened outer composite inherits whatever would get its inner one decomposed, so it gets decomposed as well; whereas flattening afterwards keeps it as a composite of the now-simple inner glyph. Same geometry, bigger glyf, never the other way round.
for those who like github archeology: in 2018 Denis added this flattenComponents filter to ufo2ft to match the behavior of Glyphs 2.4.4, there was never a custom parameter to control this and already in Glyphs 2.5 it had been dropped (Georg's words)
The flattening was applied mostly to be able to open the .ttf in FLS. I removed it as is was causing some problems. I didn’t hear about any problems.
googlefonts/ufo2ft#214 (comment)
gftools has been turning it on by default since 2020 because PostScript printer RIPs were reported to mangle nested TTF composites (fonttools/fontbakery#2961, arrowtype/recursive#412). Nobody seems to have re-tested that claim since, but printers don't get firmware updates, so I wouldn't bet on it having gone away.
fontbakery and fontspector still give a hard FAIL for their 'universal' profiles so we can expect this cargo cult to outlive us all.
Opening this to track another place where operation order is producing different results than the python pipeline.
This affects certain composite glyphs whose component is itself a composite containing a component with a per-master-varying 2×2:
An example from NotoSans:
The source's filter list is
flattenComponentswithpre = 1. ufo2ft runs it beforecheck_for_nonmatching_components, so the outer glyph is first flattened to[O, period(varying), O, period(varying)]and then decomposed because its 2×2s vary.fontc does the opposite:
has_consistent_components(fontir/src/glyph.rs~878–905) runs on the unflattened glyph, whose two components have identity transforms, so it is kept; by the timeflatten_glyph(~636–676) runs, the inner glyph is already simple and there is nothing to flatten. The log showsCoalescing 'Omonocular-cy' into a simple glyphwith no corresponding line forOdoublemonocular-cy.Both outputs are valid fonts. do we care?