googlefonts / oxidize

Notes on moving tools and libraries to Rust.
Apache License 2.0
173 stars 7 forks source link

font graph structure & packing #17

Closed cmyr closed 2 years ago

cmyr commented 2 years ago

So I've been digging into compilation, and I'm trying to get a better understanding of the structure of the graph in various tables.

In particular, I'm trying to figure out where we can define independent sub-graphs; that is, where in the font file we can define barriers across which sharing does not occur.

What are some of the more complicated/densely connected graphs? In hb-repacker, is it common for subtables to share a child? (for instance a coverage table?) Are there other examples of sibling tables sharing a child, in such a way that they refer to it with different relative offsets? (As opposed to, say, cmap, where multiple records might point to the same subtable, but with the same relative offset.)

cc @garretrieger, who is probably best equipped to answer this question?

behdad commented 2 years ago

In hb-repacker, is it common for subtables to share a child? (for instance a coverage table?) Are there other examples of sibling tables sharing a child, in such a way that they refer to it with different relative offsets?

Yes, in tables that are designed in the style of GSUB/GPOS/GDEF/BASE/JSTF, any byte-sharing is allowed by way of relative offsets as long as the compiler can figure it out. Note that the objects of different types can be shared as well, if they refer to the same bytes. For example, an empty ScriptList, FeatureList, and LookupList, all can be represented by just '\00\00' and as such can be shared.

garretrieger commented 2 years ago

Harfbuzz and fonttools both attempt to maximize sharing and will by default share any subtable's it's able too. During overflow resolution we may disable some sharing (on a per subtable basis) if needed to fix overflows.

In harfbuzz sharing is resolved every time a subtable is added to the serialization buffer. If the buffer already contains a duplicate subtable the one being added will instead re-use the existing subtable.

garretrieger commented 2 years ago

More details are here: https://github.com/harfbuzz/harfbuzz/blob/main/docs/serializer.md#object-de-duplication

cmyr commented 2 years ago

Okay thanks, I will treat actual top-level table boundaries as the only hard barrier for sharing.

garretrieger commented 2 years ago

There is one case to be aware of that needs special handling: in Ligature subtables the coverage table must always be packed last. The reason for this is that some old versions of windows 7 will fail to render the font if it's not. See: https://github.com/harfbuzz/harfbuzz/commit/49c9392412ccf31cf948310acda057b96eb6afc6 in harfbuzz we handle this by adding virtual links that enforce the desired ordering. These virtual links are treated as offsets by the repacker and thus it will make sure and children of a virtual link are placed after the parent of that link.

FontTools also implements this fix: https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/ttLib/tables/otBase.py#L380

rsheeter commented 2 years ago

Is this question adequately answered? Should we summarize the rules to an md then close?

cmyr commented 2 years ago

This is answered, yes. Not sure where this should live, I could imagine mentioning it in https://github.com/harfbuzz/harfbuzz/blob/main/docs/repacker.md?