[glyf-1 var components] lsb vs. xMin for a glyph when used as a component
justvanrossum opened this issue · 6 comments
Introduction
When glyf
glyphs are drawn, they are horizontally positioned so that the lsb
(left sidebearing) value from the hmtx
table is leading. The coordinate system of the glyph is shifted horizontally by lsb - xMin
to make this happen.
It turns out that whether to do that or not when drawing a glyph as part of a composite is not defined in the OT spec. Behdad discovered that this behavior varies among implementations:
- macOS applies the
lsb - xMin
offset when drawing a glyph as part of a composite - Windows, FreeType and HarfBuzz do not apply the
lsb - xMin
offset when drawing a glyph as part of a composite
More discussion in fonttools/fonttools#2981 and the comment thread mentioned there.
The OpenType spec weakly recommends for lsb
and xMin
to always be the same for performance reasons, and in practice this is true for the vast majority of fonts. The most common font build tool chains are not even capable of building such fonts [citation needed].
That said, with the glyf-1
proposal on the table, I think we should include a definition for how variable components should behave, or explicitly state that the behavior is undefined. Variable components are new, not backwards compatible, and there is no strict need to make them behave exactly the same as regular components in this respect.
The problem of ignoring the lsb - xMin offset in a composite context
The lsb - xMin
offset is IMO a low level detail, perhaps even a regrettable legacy detail in OT. Higher level font representations, such as .ufo and .glyphs generally do not support it: the origin of the glyph's coordinate system is by definition the "left side" of the glyph. There simply is no place in these models for the offset, and when importing a binary font it needs to be abstracted away.
What generally happens is that upon reading glyphs from a TT-flavored OT file, the lsb - xMin
offset is simply applied to the coordinates, and all is good.
Now, if a glyph that originally had a non-zero lsb - xMin
offset is used in a composite, placement goes wrong if the offset is supposed to be ignored in that context: the outline data available (already imported) has the offset already applied to it, and the offset value is gone.
The use case illustrated here (extracting shapes from a binary font) exists in Fontra (where I encountered it) but presumably also exists in RoboFont and Glyphs: given their internal models, it is very difficult to support the "ignore lsb - xMin
offset when drawing composites" behavior correctly. I think it's even fair for these apps to ignore this obscure problem and say it is undefined behavior (which it is: it's not in the OT spec) and do just whatever.
(Reading a binary font by a font editor is my current perspective, but it applies to any situation where you want to model/render TTF with a format-neutral model that doesn't support the lsb - xMin
offset.)
Possible solutions
I think glyf-1
is an excellent opportuinity to a) clarify/define the behavior for variable components and perhaps b) fix the spec for regular components. Focussing on variable components, I see three options:
- Specify that
the lsb - xMin
offset MUST also be applied when drawing a glyph as part of a variable composite - Specify that
the lsb - xMin
offset MUST NOT be applied when drawing a glyph as part of a variable composite - Specify that
lsb != xMin
is undefined behavior, and that it is therefore required thatlsb == xMin
for all glyphs that are used as variable components
For my use case option 2 is terrible, so I'm strongly in favor of choosing options 1 or 3. Between 1 and 3 I have no strong preference I have a slight preference for option 3.
In the beyond-64k work I'm removing lsb from hmtx entries for glyphs beyond 64k.
In https://learn.microsoft.com/en-us/typography/opentype/spec/head it says:
Note that, in a variable font with TrueType outlines, the left side bearing for each glyph must equal xMin, and bit 1 in the flags field must be set. [ ... ]
Which makes this issue largely redundant, as it is equivalent to my "option 3".
Some more interesting insights and questions in the comments on my Mastodon post: https://typo.social/@justvanrossum/109805730176978858
URL doesn't work. Is there a typo there?
URL doesn't work. Is there a typo there?
Our poor typo.social instance seems to be down, sorry! Please check back later.
Hm, works now, maybe it's fixed.
Works. Thanks.