googlefonts / ufo2ft

A bridge from UFOs to FontTools objects (and therefore, OTFs and TTFs).
MIT License
152 stars 43 forks source link

skipExportGlyphs should be later in the pipeline #692

Open bobh0303 opened 1 year ago

bobh0303 commented 1 year ago

I suggest that skipExportGlyphs should be processed later in the pipeline, at least later than flattenComponents (which is already after decomposeComponents). The current implementation results in unnecessary font bloat.

Consider this common situation: an encoded composite glyph references a component glyph that is itself a composite. Here is a concrete example (with non-relevant detail omitted) from Arabic, but it happens in Latin and other scripts as well:

<glyph name="qaf-ar" ...>
  <outline>
    <component base="qafDotless-ar"/>
    <component base="_dot2h" ... />
  </outline>
</glyph>

<glyph name="_dot2h" …>
  <outline>
    <component base="_dot1" .../>
    <component base="_dot1" .../>
  </outline>
</glyph>

<glyph name="_dot1" …>
  <outline>
    <contour>
     ...
    </contour>
  </outline>
</glyph>

We have _dot2h used as a component (of qaf-ar) but is itself a composite glyph consisting of two references to _dot1.

GoogleFonts team requires this to be flattened, so we include the flattenComponents filter which will then flatten the qaf-ar glyph. The resulting glyph, if represented in UFO-speak, would be:

<glyph name="qaf-ar" ...>
  <outline>
    <component base="behDotless-ar"/>
    <component base="_dot1" .../>
    <component base="_dot1" .../>
  </outline>
</glyph>

This was confirmed by building the TTF and dumping with ttx, which yields:

    <TTGlyph name="qaf-ar" .../>
      <component glyphName="qafDotless-ar" .../>
      <component glyphName="_dot1" ... />
      <component glyphName="_dot1" .../>
    </TTGlyph>

Now, of course, the _dot2h is no longer used — it is not encoded and all other composites referencing it would similarly have been flattened. As a result, fontBakery legitimately complains that the _dot2h glyph is unreachable.

It would be nice to now be able to utilize the skipExportGlyphs mechanism to remove the unreachable _dot2h from the resulting font and quiet the fontBakery warning.

However, since ufo2ft processes skipExportGlyphs first, and at this point qaf-ar still references _dot2h, adding _dot2h to public.skipExportGlyphs will cause the qaf-ar glyph to be [needlessly] fully decomposed into a contour glyph.

Multiplying this over all the glyphs in the font that get similarly decomposed results in a significant increase in the glyph table size. In the Lateef project, adding just _dot2h to public.skipExportGlyphs increased the glyph table by 14 kb.

If the skipExportGlyphs process were run later in the pipeline, at least after flattenComponents, then it could have the desired effect of making the font smaller.

bobh0303 commented 1 year ago

Any thoughts on this (from devs or users)?

anthrotype commented 1 year ago

https://unifiedfontobject.org/versions/ufo3/lib.plist/#publicskipexportglyphs

the ufo spec says about public.skipExportGlyphs that the UFO compiler is responsible to "decompose the listed glyphs everywhere they are used as components", and that's what ufo2ft is doing. This is done in the preprocessor before any other filter is processed, when collecting the glyphs from a layer. It'd be difficult to untangle at this stage.

Fontmake also supports running the fonttools subsetter at the end of the build, it does that automatically when some glyphsLib specific lib keys are present, but I believe can be activated regardless if you pass the CLI option --subset. Can you please try to see if that works for you?

https://github.com/googlefonts/fontmake/blob/eef7c40a83cbdbf1317980169fcdc36ebb5c9a12/Lib/fontmake/font_project.py#L667-L673 https://github.com/googlefonts/fontmake/blob/eef7c40a83cbdbf1317980169fcdc36ebb5c9a12/Lib/fontmake/font_project.py#L720-L791

anthrotype commented 1 year ago

the fonttools subsetter should get rid of the orphaned glyphs if they are no longer referenced as components and have not direct cmap mappings and are not reachable via GSUB rules

bobh0303 commented 1 year ago

Thanks for your replies, Cosimo,

At this point neither fontmake nor the subsetter are in our regular workflow.

the ufo spec says about public.skipExportGlyphs that the UFO compiler is responsible to "decompose the listed glyphs everywhere they are used as components", and that's what ufo2ft is doing.

I agree, but it doesn't say that has to be done first. Seems pretty clear that ufo2ft would still be in compliance with the UFO spec if it ran flattenComponents process first, then the process required by public.skipExportGlyps, and finally all other processes (including "compilation", i.e., conversion to ttf). And the result would be a smaller font.