After the table directory, and the collection directory if there is one, comes the font data: every table, one after the other, compressed together as a single Brotli stream. Optional extended metadata and private data may follow.
0Header48 bytes48Table directoryvaries…Collection directory only whenflavoristtcfvaries…Compressed font data one Brotli streamtotalCompressedSize…Extended metadata Brotli-compressed XML, on a 4-byte boundaryoptional…Private data on a 4-byte boundary, to the end of the fileoptional
The compressed stream
The stream is compressed with Brotli (RFC 7932). Uncompressed, it is the data of each
table in the order of the table directory, end to end, with nothing between them: no
padding and no alignment. Each table takes its transformLength if it is
transformed, and its origLength if not; a transformed
loca takes nothing.
The font must not be loaded if the stream does not decompress, or if it decompresses
to a length other than the sum of those lengths. The stream is
totalCompressedSize bytes long. The data after it, if any, starts on the
next four-byte boundary, with up to three zero bytes of padding.
Because every table is in one stream, Brotli can use repetitions across tables. A decoder that needs only one table still has to decompress everything before it.
Rebuilding the font
A WOFF2 file is not meant to give back the original font byte for byte, only a font that works the same. The decoder:
- Reverses the glyf and loca and hmtx transformations; other tables are copied as they are.
- Writes the offset table (
flavor,numTables, andsearchRange,entrySelector,rangeShiftworked out as OpenType defines them) and the table records, sorted by tag. For a collection it writes a TTC header first. - Computes a checksum for every table record, since WOFF2 stores none, and then the
checkSumAdjustmentinheadfor the whole font. - Should lay the tables out in the order the OpenType and OFF specifications recommend for performance.
The rebuilt glyf table may come out a different size from its
origLength, and the whole font a different size from
totalSfntSize; neither is a reason to reject the font.
An encoder must remove any DSIG table, since a signature cannot match the
rebuilt font, and must set bit 11 of the flags field in
head. OpenType defines that bit as meaning the font has been through a
lossless, optimising transformation: it works as before, but is not byte for byte the
same file.
Extended metadata
The metadata block is interpreted exactly as in WOFF 1.0: an XML document in UTF-8
about the font, its makers and its licence, with the elements listed on the
WOFF metadata page. The difference is the
compression: WOFF2 compresses it with Brotli, as its own stream, separate from the font
data. metaLength is the compressed length and
metaOrigLength the length of the XML.
The block starts on a four-byte boundary straight after the compressed font data. If it is the last block in the file, nothing should follow it. It must not affect how the font loads or renders.
Private data
The private data block holds anything the font’s maker wants, in a format WOFF2 does
not define. A user agent must make no assumptions about it and must not let it change
how the font is used. It is always the last block: it starts on a four-byte boundary
at privOffset and ends at the end of the file.
Example
In IBM Plex Mono Regular (see the header example) the stream starts at offset 88, the end of the table directory, and runs 9,964 bytes to the end of the file. It must decompress to 19,489 bytes, the sum the directory gives, and the rebuilt font is 24,156 bytes.
00000058 1B 20 4C 25 E3 98 95 78 1C 00 5E 7A 39 44 51 2A
The first byte, 1B, is the start of the Brotli stream header. Read from
its lowest bit, as Brotli reads bits: bit 0 is 1, and bits 1 to 3 give 5, so the
window size bits are 17 + 5 = 22: a sliding window of 222 − 16 bytes,
about 4 MiB.