Skip to content

Support variations of fonts in WOFF and WOFF2 containers - #411

Merged
blikblum merged 2 commits into
foliojs:masterfrom
lineto:fix/woff-variations
Oct 22, 2026
Merged

blikblum merged 2 commits into
foliojs:masterfrom
lineto:fix/woff-variations

Conversation

@lehni

@lehni lehni commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Supersedes #389 by @sidorares, who hasn't replied to the review in three weeks: this keeps their commit as it was, rebased on master, so the fix can be merged. A second commit addresses @blikblum's review:

  • Decode the points and deltas of glyph variations in functions at module scope
  • numberOfContours > 0 isn't always true there: empty glyphs return earlier, and composite glyphs have a negative count. The else is now else if (glyph.numberOfContours < 0), as in TTFGlyph

getVariation() rebuilt the font as a TTFFont, from the stream at the
directory's offset. For a WOFF that read a WOFF directory as an sfnt's, and
for a WOFF2 the stream is the compressed file, or once anything was decoded
the decompressed tables with no directory at all: the variation had no
tables, and threw from wherever its first table was asked for.

A variation is now made by the font's own class (_createVariation), so a
container reads its directory and its tables the way it holds them.

WOFF: the glyph variation data was read from the font's stream at offsets
that are positions in the inflated gvar table. GlyphVariationProcessor reads
it from the stream the table was decoded from. A table is inflated once and
kept, where it was inflated again for every glyph that asked for it.

WOFF2: a variation reads the directory from the file again and shares the
font's decompressed tables and decoded glyphs, so nothing is decompressed or
decoded twice. The transformed glyf table is decoded without a variation
applied, and WOFF2Glyph moves a copy of a glyph's points, or of its
components' offsets, where TTFGlyph moves what it decoded. The phantom
points need the bounding box a glyf header has, which the transformed table
stores for some glyphs and leaves to be computed from the points for the
rest, so the bounding box stream is read.

The tests compare every glyph of a variation (path, bounding box, advances)
with the same variation of the sfnt the container was made from: TrueType
outlines with and without HVAR, composite glyphs, CFF2, and a WOFF2 whose
glyf table is not transformed. The containers were made with fontTools from
the fonts already in test/data.
- Decode the points and deltas of glyph variations in functions at module scope
- Move the components of composite glyphs only for composite glyphs, as TTFGlyph does
@lehni
lehni force-pushed the fix/woff-variations branch from e917a06 to 1c600dd Compare October 22, 2026 08:45
@blikblum
blikblum merged commit 66c60b2 into foliojs:master Oct 22, 2026
3 checks passed
@sidorares

Copy link
Copy Markdown
Contributor

thanks @lehni !

@lehni
lehni deleted the fix/woff-variations branch October 25, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants