A WOFF2 file starts with a header of 48 bytes. It is the
WOFF 1.0 header with one more field,
totalCompressedSize, and a new signature. Fixed-size numbers are big-endian;
the table directory and the transformed tables also use two variable-length number
types, described below.
Fields
Offsets and sizes are in bytes. The names are the ones in the W3C specification. The examples are from the header shown at the end of this page.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | UInt32 | signature | Always 0x774F4632, the characters wOF2. Any other value makes the file invalid. |
4 | 4 | UInt32 | flavor | The sfntVersion of the font that was wrapped: 0x00010000 for TrueType outlines, OTTO for CFF outlines, or ttcf (0x74746366) for a font collection, which then has a collection directory. Example: 0x00010000. |
8 | 4 | UInt32 | length | The size of the whole WOFF2 file. Example: 10,052. |
12 | 2 | UInt16 | numTables | The number of entries in the table directory. For a collection, the number of distinct tables in all its fonts together. Example: 14. |
14 | 2 | UInt16 | reserved | Written as 0. Unlike WOFF 1.0, a decoder must not reject a file whose reserved is not 0. |
16 | 4 | UInt32 | totalSfntSize | The size of the font once decoded: its header, table records and padded tables. For reference only: a decoder must not reject a correctly decoded font whose size differs from it. Example: 24,156. |
20 | 4 | UInt32 | totalCompressedSize | The length of the compressed data, the Brotli stream that follows the directories. Example: 9,964. |
24 | 2 | UInt16 | majorVersion | The major version of the WOFF2 file, chosen by whoever made it. It does not affect loading. Example: 1. |
26 | 2 | UInt16 | minorVersion | The minor version of the WOFF2 file. Example: 0. |
28 | 4 | UInt32 | metaOffset | Where the extended metadata starts, from the start of the file; 0 when there is none. |
32 | 4 | UInt32 | metaLength | The length of the metadata block as stored, compressed with Brotli. |
36 | 4 | UInt32 | metaOrigLength | The length of the metadata once uncompressed. |
40 | 4 | UInt32 | privOffset | Where the private data starts; 0 when there is none. |
44 | 4 | UInt32 | privLength | The length of the private data block. |
The table directory follows directly, at offset 48. Its entries vary in length, so the only way to find the end of the directory, and the start of the compressed data, is to read every entry.
The file must be rejected if its signature is wrong, if the offsets and lengths of any blocks overlap or run past the end of the file, or if there are bytes between the blocks other than the padding the specification allows.
Number types
| Type | Size | Range | Used for |
|---|---|---|---|
| UInt8 | 1 | 0 to 255 | flag bytes |
| Int16 | 2 | −32,768 to 32,767, two’s complement, big-endian | coordinates in bounding boxes, side bearings |
| UInt16 | 2 | 0 to 65,535, big-endian | header fields, advance widths |
| UInt32 | 4 | 0 to 4,294,967,295, big-endian | header fields, tags |
| UIntBase128 | 1 to 5 | 0 to 4,294,967,295 | table lengths in the table directory |
| 255UInt16 | 1 to 3 | 0 to 65,535 | counts in the collection directory and in the transformed glyf table |
UIntBase128
A number written seven bits at a time, most significant group first. Each byte gives seven bits of the value in its low bits; its top bit is 1 if another byte follows and 0 on the last byte. To read one, start at 0 and for each byte multiply by 128 and add the byte’s low seven bits, stopping after the first byte whose top bit is clear.
A decoder must reject the file if a value:
- has a leading zero, that is, its first byte is
80; - takes more than five bytes;
- is larger than 232 − 1.
Encoders must use the shortest form, so 63 is 3F, never
80 3F.
| Bytes | Value |
|---|---|
4C | 76 |
86 52 | 6 × 128 + 82 = 850 |
81 8B 78 | (1 × 128 + 11) × 128 + 120 = 17,912 |
8F FF FF FF 7F | 4,294,967,295, the largest allowed |
255UInt16
A number up to 65,535 that takes one byte when it is below 253. The first byte decides the form:
| First byte | Name in the specification | Bytes | Value |
|---|---|---|---|
| 0 to 252 | – | 1 | the byte itself |
253 (FD) | wordCode | 3 | the next two bytes, as a big-endian UInt16 |
254 (FE) | oneMoreByteCode2 | 2 | the next byte + 506 (2 × 253) |
255 (FF) | oneMoreByteCode1 | 2 | the next byte + 253 (lowestUCode) |
So two bytes reach 761 (FE FF). One value can have several encodings: the
specification’s example is 506, which is FF FD, FE 00 or
FD 01 FA. Decoders must accept all of them. Encoders should choose the
shortest and must always use the same encoding for the same value.
Example
The header of IBM Plex Mono Regular as this site serves it, a TrueType font of 14 tables in a 10,052-byte WOFF2 file. It has no metadata and no private data.
00000000 77 4F 46 32 00 01 00 00 00 00 27 44 00 0E 00 00 wOF2......'D....
00000010 00 00 5E 5C 00 00 26 EC 00 01 00 00 00 00 00 00 ..^\..&.........
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
| Bytes | Field | Value |
|---|---|---|
77 4F 46 32 | signature | wOF2 |
00 01 00 00 | flavor | TrueType outlines |
00 00 27 44 | length | 10,052, the size of the file |
00 0E | numTables | 14 |
00 00 | reserved | 0 |
00 00 5E 5C | totalSfntSize | 24,156: the decoded font is 2.4 times the size of the WOFF2 |
00 00 26 EC | totalCompressedSize | 9,964 |
00 01, 00 00 | majorVersion, minorVersion | 1.0 |
| 20 zero bytes | metadata and private data fields | neither block is present |
The directory that follows takes 40 bytes (see the table directory example), so the compressed data runs from offset 88 for 9,964 bytes, to 10,052, the end of the file.