In a TrueType font the glyf table holds each glyph’s outline as one record,
and loca gives where each record starts. WOFF2’s transformation, version 0
in the table directory,
splits the glyph records into separate streams of like data (contour counts, point
counts, flags, coordinates, instructions) that compress far better, and drops
loca altogether, since it can be worked out again. It applies only to
TrueType outlines; a CFF font has no glyf.
Header
The transformed table starts with a 36-byte header. All values are big-endian.
| Offset | Type | Name | Description |
|---|---|---|---|
0 | UInt16 | reserved | 0. |
2 | UInt16 | optionFlags | Bit 0: the overlapSimpleBitmap is present at the end. Bits 1 to 15 are reserved. |
4 | UInt16 | numGlyphs | The number of glyphs. |
6 | UInt16 | indexFormat | The loca format to rebuild: 0 for 16-bit offsets, 1 for 32-bit. It must equal indexToLocFormat in the font’s head table. |
8 | UInt32 | nContourStreamSize | The size of the nContour stream in bytes. |
12 | UInt32 | nPointsStreamSize | The size of the nPoints stream. |
16 | UInt32 | flagStreamSize | The size of the flag stream. |
20 | UInt32 | glyphStreamSize | The size of the glyph stream. |
24 | UInt32 | compositeStreamSize | The size of the composite stream. |
28 | UInt32 | bboxStreamSize | The size of the bounding box bitmap and the bounding box stream together. |
32 | UInt32 | instructionStreamSize | The size of the instruction stream. |
Streams
The streams follow the header in this order, each straight after the one before. Each is read from its start as the glyphs are decoded, glyph 0 first.
| Stream | Contents |
|---|---|
nContourStream | One Int16 per glyph: its number of contours. 0 is an empty glyph, 1 or more a simple glyph, −1 a composite glyph. |
nPointsStream | For each contour of each simple glyph, its number of points, as a 255UInt16. |
flagStream | One UInt8 per point of each simple glyph: bit 7 set means the point is off the curve, clear means on it; bits 0 to 6 are the triplet code of its coordinates. |
glyphStream | For simple glyphs, the coordinate bytes of every point, then the length of the glyph’s instructions as a 255UInt16. For composite glyphs with instructions, just that length. |
compositeStream | For composite glyphs, the components exactly as in a TrueType composite glyph record: a UInt16 of flags, then 4 to 14 bytes of glyph index, offsets and transformation, for each component. |
bboxBitmap | One bit per glyph, 4 × floor((numGlyphs + 31) / 32) bytes, so a whole number of 32-bit words. Glyph 0 is the top bit of byte 0, glyph 7 its bottom bit, glyph 8 the top bit of byte 1. A set bit means the glyph’s bounding box is given explicitly. |
bboxStream | For each glyph whose bit is set, four Int16: xMin, yMin, xMax, yMax. |
instructionStream | The TrueType instructions of every glyph that has any, one after another. |
overlapSimpleBitmap | Only when bit 0 of optionFlags is set: one bit per glyph, the same size and bit order as bboxBitmap. |
Decoding a glyph
For each glyph in turn, read its contour count from nContourStream.
Simple glyph (1 or more contours)
- Read one 255UInt16 per contour from
nPointsStream. Their running totals, less 1, are the glyph’sendPtsOfContours; their sum is its number of points. - Read one flag byte per point from
flagStream. - For each point, read 1 to 4 bytes from
glyphStream, as the point’s triplet code says, and decode them to a change in x and in y from the previous point (the first point’s is from 0, 0). - Read the instruction length, a 255UInt16, from
glyphStream, and that many bytes frominstructionStream. - Take the bounding box from
bboxStreamif the glyph’s bit is set; otherwise compute it from the smallest and largest x and y of the points. - Write a TrueType simple glyph record: the contour count, the bounding box,
endPtsOfContours, the instructions, then the points in TrueType’s own encoding of flags and coordinates, which the decoder chooses.
Composite glyph (−1)
- Read the components from
compositeStream. Each starts with a UInt16 of TrueType component flags; the bytes after it are the glyph index (2 bytes), two offsets or point numbers (4 bytes ifARG_1_AND_2_ARE_WORDS,0x0001, is set, otherwise 2), then a scale of 2 bytes (WE_HAVE_A_SCALE,0x0008), 4 bytes (WE_HAVE_AN_X_AND_Y_SCALE,0x0040) or 8 bytes (WE_HAVE_A_TWO_BY_TWO,0x0080), or none. Another component follows whileMORE_COMPONENTS(0x0020, bit 5) is set. - If any component has
WE_HAVE_INSTRUCTIONS(0x0100, bit 8), read the instruction length fromglyphStreamand the instructions frominstructionStream. - The bounding box must be explicit: a composite glyph whose bit in
bboxBitmapis clear makes the font invalid. - Write a TrueType composite glyph record: −1, the bounding box, the components, and the instruction length and instructions if any.
Empty glyph (0)
No data is read and no glyph record is written: its loca entry equals
the next one. An empty glyph whose bit in bboxBitmap is set makes the
font invalid.
Triplet codes
Bits 0 to 6 of a point’s flag, 0 to 127, choose how its x and y changes are packed in the glyph stream: how many bytes they take, how many bits each coordinate has, a fixed amount added to each, and the signs. Small moves along one axis take one byte, small moves on both axes take one byte split into two four-bit halves, and the largest take four bytes. The specification calls the flag and its coordinate bytes a triplet, and gives a table of all 128 codes; this is the same table with each group of codes on one row.
Bytes is the number of bytes read from the glyph stream (the specification’s byte count also counts the flag, which is in the flag stream). X and Y give each coordinate’s bits and the amount added to them. Signs is the order of signs within the group: within each group of two codes, the second is positive; within each group of four, the codes run (x −, y −), (x +, y −), (x −, y +), (x +, y +).
| Codes | Bytes | X bits | Y bits | X added | Y added | Signs |
|---|---|---|---|---|---|---|
| 0, 1 | 1 | 0 | 8 | – | 0 | y −, y + |
| 2, 3 | 1 | 0 | 8 | – | 256 | y −, y + |
| 4, 5 | 1 | 0 | 8 | – | 512 | y −, y + |
| 6, 7 | 1 | 0 | 8 | – | 768 | y −, y + |
| 8, 9 | 1 | 0 | 8 | – | 1024 | y −, y + |
| 10, 11 | 1 | 8 | 0 | 0 | – | x −, x + |
| 12, 13 | 1 | 8 | 0 | 256 | – | x −, x + |
| 14, 15 | 1 | 8 | 0 | 512 | – | x −, x + |
| 16, 17 | 1 | 8 | 0 | 768 | – | x −, x + |
| 18, 19 | 1 | 8 | 0 | 1024 | – | x −, x + |
| 20 to 23 | 1 | 4 | 4 | 1 | 1 | four combinations |
| 24 to 27 | 1 | 4 | 4 | 1 | 17 | four combinations |
| 28 to 31 | 1 | 4 | 4 | 1 | 33 | four combinations |
| 32 to 35 | 1 | 4 | 4 | 1 | 49 | four combinations |
| 36 to 39 | 1 | 4 | 4 | 17 | 1 | four combinations |
| 40 to 43 | 1 | 4 | 4 | 17 | 17 | four combinations |
| 44 to 47 | 1 | 4 | 4 | 17 | 33 | four combinations |
| 48 to 51 | 1 | 4 | 4 | 17 | 49 | four combinations |
| 52 to 55 | 1 | 4 | 4 | 33 | 1 | four combinations |
| 56 to 59 | 1 | 4 | 4 | 33 | 17 | four combinations |
| 60 to 63 | 1 | 4 | 4 | 33 | 33 | four combinations |
| 64 to 67 | 1 | 4 | 4 | 33 | 49 | four combinations |
| 68 to 71 | 1 | 4 | 4 | 49 | 1 | four combinations |
| 72 to 75 | 1 | 4 | 4 | 49 | 17 | four combinations |
| 76 to 79 | 1 | 4 | 4 | 49 | 33 | four combinations |
| 80 to 83 | 1 | 4 | 4 | 49 | 49 | four combinations |
| 84 to 87 | 2 | 8 | 8 | 1 | 1 | four combinations |
| 88 to 91 | 2 | 8 | 8 | 1 | 257 | four combinations |
| 92 to 95 | 2 | 8 | 8 | 1 | 513 | four combinations |
| 96 to 99 | 2 | 8 | 8 | 257 | 1 | four combinations |
| 100 to 103 | 2 | 8 | 8 | 257 | 257 | four combinations |
| 104 to 107 | 2 | 8 | 8 | 257 | 513 | four combinations |
| 108 to 111 | 2 | 8 | 8 | 513 | 1 | four combinations |
| 112 to 115 | 2 | 8 | 8 | 513 | 257 | four combinations |
| 116 to 119 | 2 | 8 | 8 | 513 | 513 | four combinations |
| 120 to 123 | 3 | 12 | 12 | 0 | 0 | four combinations |
| 124 to 127 | 4 | 16 | 16 | 0 | 0 | four combinations |
So a code’s x change is positive when the code is odd, and for codes 20 and up its y change is positive when bit 1 of the code is set; for codes 0 to 9 the y change is positive when the code is odd. Where four-bit or twelve-bit values share a byte, the x value comes first, in the high bits. As formulas, with c the code and b0, b1… the bytes read, before the sign is applied (this is how Google’s reference decoder writes them; they give exactly the table above):
| Codes | x change | y change |
|---|---|---|
| 0 to 9 | 0 | ((c & 14) << 7) + b0 |
| 10 to 19 | (((c − 10) & 14) << 7) + b0 | 0 |
| 20 to 83 | 1 + ((c − 20) & 0x30) + (b0 >> 4) | 1 + (((c − 20) & 0x0C) << 2) + (b0 & 0x0F) |
| 84 to 119 | 1 + (floor((c − 84) / 12) << 8) + b0 | 1 + ((((c − 84) mod 12) >> 2) << 8) + b1 |
| 120 to 123 | (b0 << 4) + (b1 >> 4) | ((b1 & 0x0F) << 8) + b2 |
| 124 to 127 | (b0 << 8) + b1 | (b2 << 8) + b3 |
For example, flag 0F followed by the byte 20 is an on-curve
point (bit 7 clear) with code 15: x changes by +(512 + 32) = +544, y by 0. Flag
A9 is an off-curve point with code 41 (x +, y −, both with 17 added);
with the byte 3C it moves x by +(17 + 3) = +20 and y by −(17 + 12) = −29.
Overlap bitmap
TrueType’s simple glyph flags have a bit, OVERLAP_SIMPLE (bit 6 of the
first flag of a glyph), that says the glyph’s contours overlap. The triplet flags do not
carry it, so it has its own bitmap. An encoder includes the bitmap, and sets bit 0 of
optionFlags, only when some simple glyph has the bit set; it then holds
one bit per glyph. A decoder sets bit 6 of each simple glyph’s first flag from the
bitmap, or leaves it clear everywhere when there is no bitmap. The bitmap is not in the
first Recommendation of 2018: it came in as a correction to it, made normative in the
2024 edition.
Rebuilding loca
A transformed loca table has version 0 in the directory and a
transformLength of 0: it takes no bytes in the stream. The decoder builds
it while it writes the glyph records, one offset per glyph and one for the end of the
last, in the format indexFormat gives. Its origLength must
be (numGlyphs + 1) × 2 for short offsets or × 4 for long ones; any other
value makes the font invalid. Short offsets store the offset divided by 2, so with
indexFormat 0 every glyph record must start at an even offset.
If glyf is transformed, loca must be too, and the reverse.
When the font must be rejected
- A composite glyph has no explicit bounding box, or an empty glyph has one.
- The transformed
locahas atransformLengthother than 0, or anorigLengththat does not matchnumGlyphsandindexFormat. - Only one of
glyfandlocais transformed. - A stream runs out before every glyph is decoded.
The rebuilt glyf table need not be the same size as its
origLength, because the decoder chooses its own encoding of flags and
coordinates; that is not an error.