fonttools/fontbakery

Check for nested components

RosaWagner opened this issue · 17 comments

Observed behaviour

Component referring to another component (nested) are not exported correctly for ttf variable format. They take back the coordinates of the original glyph.

Capture d’écran 2020-07-08 à 13 57 36

Capture d’écran 2020-07-08 à 14 24 57

Expected behaviour

There should be a check to find any nested components.

Resources and exact process needed to replicate

I used Public Sans at commit uswds/public-sans@0a09945

But you can reproduce it with any font which have nested components (typically ohorn/uhorn with another accent)

This may be a more severe issue than just something messing up Samsa.

At arrowtype/recursive#412, I have described issues that occur when printing static TTFs with many printers. In summary: PostScript-based printers really mess up nested components in TTF fonts. This includes even high-end setups used for offset printing. One example of where things go wrong in current Recursive TTFs:

image

(There are more examples in the issue.)

There are two possible culprits:

  • A problem in ttfautohint, or in how PostScript handles the hints from it
  • A fundamental problem with PostScript’s handling of TTF nested components

Worse, printers that have PostScript installed apparently never update that software.

So, this check plus a script that un-nests components are probably relatively high-priority, if Google Fonts wants to give people any confidence in using the library for more than just web design. Not all of the alignment problems with TTFs & PostScript would be solved by this – it also messes up the positioning of glyph substituted via stylistic sets – but it seems that it would solve the majority of them.

madig commented

There's a ufo2ft filter to unnest composites automatically. Insert the following into your UFO's lib.plist:

    <key>com.github.googlei18n.ufo2ft.filters</key>
    <array>
      <dict>
        <key>name</key>
        <string>flattenComponents</string>
        <key>pre</key>
        <integer>1</integer>
      </dict>
    </array>

@madig THANK YOU, this is amazing news!

madig commented

It's actually been in there for decades, but we don't have time to write docs so all the filters are secret.

quite depressing that in 2020 we can't still have nested glyf components

It's actually been in there for decades, but we don't have time to write docs so all the filters are secret.

I could be a good start to just drop some general lines about the existence of this mechanism and where to find the available filters and how to generally access and configure them.

madig commented

I started something at googlefonts/ufo2ft#286 last century but died before I could finish the job.

The stems of all .py files in https://github.com/googlefonts/ufo2ft/tree/master/Lib/ufo2ft/filters can be used as filters.

@madig: Would it be worth making FlattenComponents the default instead of DecomposeComponents?

madig commented

Where?

ufo2ft

madig commented

Where in ufo2ft? No idea what you're talking about.

He means as default filter in the ufo2ft.preProcessor. I would rather not do that. We can make it easier for users to specify that, if a custom filter in the lib.plist is too obscure. E.g. we can add a new option to ufo2ft.compileTTF function and even add a CLI flag to fontmake. But changing the default is not a good idea. There may be legitimate use-cases that may prefer to keep the current beaviour that allows for nested components.

Should we add a --filters argument to fontmake instead or additionally?

Can’t we just have a single option which does this?

If we aren’t prepared to make creating non-broken fonts the default, we should at least make it as easy as possible.

madig commented

Can’t we just have a single option which does this?

What I’m suggesting as an alternative or as an additional option is to have a --filter argument to fontmake that works like --feature-writer does.
So one could do:

fontmake -m DS.designspace --filter flattenComponents

This would also work for propagateAnchors, decomposeTransformedComponents, or any custom filter.

Note to others here (and maybe this should be included in the check rationale, etc):

At least for Recursive (and another project, Name Sans) it is essential to not only flatten components but to first also decompose transformed components. Otherwise, accents like the caron (which is a 180° rotated circumflex) get really misaligned.

In a lib.plist, that looks like this:

    <key>com.github.googlei18n.ufo2ft.filters</key>
    <array>
      <dict>
        <key>name</key>
        <string>decomposeTransformedComponents</string>
        <key>pre</key>
        <integer>1</integer>
      </dict>
      <dict>
        <key>name</key>
        <string>flattenComponents</string>
        <key>pre</key>
        <integer>1</integer>
      </dict>
    </array>

See arrowtype/recursive#412 for details.