TTF Byte Layout Reference
TrueType (TTF) Byte Layout Reference
A minimal, quick-reference for the byte offsets and field layouts of every core TTF table. Big-endian throughout. For parsing strategy and walkthroughs, see Parse a Font.
Contents
File header
Offset table (sfnt header) — 12 bytes at byte 0:
| offset | type | field | notes |
|---|---|---|---|
| 0 | u32 | sfntVersion | 0x00010000 (TrueType) or 0x4F54544F (OTTO) |
| 4 | u16 | numTables | count of tables that follow |
| 6 | u16 | searchRange | binary-search hint; recompute if modifying |
| 8 | u16 | entrySelector | binary-search hint |
| 10 | u16 | rangeShift | binary-search hint |
Table directory — numTables records of 16 bytes each, starting at byte 12:
| offset | type | field | notes |
|---|---|---|---|
| 0 | tag[4] | tableTag | e.g. head, glyf (ASCII) |
| 4 | u32 | checksum | table checksum |
| 8 | u32 | offset | from file start |
| 12 | u32 | length | real length; table padded to 4-byte bound |
Record i starts at byte 12 + i×16.
Tables
head — Font header (54 bytes, fixed)
Global font metrics and configuration.
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | majorVersion | 1 |
| 2 | u16 | minorVersion | 0 |
| 4 | i32 | fontRevision | 16.16 fixed-point |
| 8 | u32 | checksumAdjustment | whole-file sum |
| 12 | u32 | magicNumber | must be 0x5F0F3CF5 |
| 16 | u16 | flags | bit field |
| 18 | u16 | unitsPerEm | design grid (e.g. 1000, 2048) |
| 20 | i64 | created | seconds since 1904-01-01 00:00 UTC |
| 28 | i64 | modified | same epoch |
| 36 | i16 | xMin | font bbox |
| 38 | i16 | yMin | |
| 40 | i16 | xMax | |
| 42 | i16 | yMax | |
| 44 | u16 | macStyle | bit field |
| 46 | u16 | lowestRecPPEM | smallest readable size in pixels |
| 48 | i16 | fontDirectionHint | |
| 50 | i16 | indexToLocFormat | 0 = short loca, 1 = long loca |
| 52 | i16 | glyphDataFormat | 0 |
maxp — Maximum profile
Memory requirements. Version 0.5 (CFF, 6 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u32 | version | 0x00005000 |
| 4 | u16 | numGlyphs | critical |
Version 1.0 (TrueType, 32 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u32 | version | 0x00010000 |
| 4 | u16 | numGlyphs | glyph count |
| 6 | u16 | maxPoints | max in simple glyph |
| 8 | u16 | maxContours | max in simple glyph |
| 10 | u16 | maxCompositePoints | max in composite glyph |
| 12 | u16 | maxCompositeContours | max in composite glyph |
| 14 | u16 | maxZones | 1 or 2 |
| 16 | u16 | maxTwilightPoints | |
| 18 | u16 | maxStorage | |
| 20 | u16 | maxFunctionDefs | |
| 22 | u16 | maxInstructionDefs | |
| 24 | u16 | maxStackElements | |
| 26 | u16 | maxSizeOfInstructions | |
| 28 | u16 | maxComponentElements | |
| 30 | u16 | maxComponentDepth |
hhea — Horizontal header (36 bytes, fixed)
Metrics for horizontal layout.
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | majorVersion | 1 |
| 2 | u16 | minorVersion | 0 |
| 4 | i16 | ascender | |
| 6 | i16 | descender | usually negative |
| 8 | i16 | lineGap | |
| 10 | u16 | advanceWidthMax | |
| 12 | i16 | minLeftSideBearing | |
| 14 | i16 | minRightSideBearing | |
| 16 | i16 | xMaxExtent | |
| 18 | i16 | caretSlopeRise | 1 for vertical |
| 20 | i16 | caretSlopeRun | 0 for vertical |
| 22 | i16 | caretOffset | 0 for non-slanted |
| 24 | i16 | reserved[4] | zero |
| 32 | i16 | metricDataFormat | 0 |
| 34 | u16 | numberOfHMetrics | sizes hmtx table |
hmtx — Horizontal metrics (variable)
No header. Two back-to-back arrays:
Part 1: numberOfHMetrics (from hhea) entries of:
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | advanceWidth | |
| 2 | i16 | lsb | left side bearing |
Part 2: numGlyphs − numberOfHMetrics entries of:
| offset | type | field | notes |
|---|---|---|---|
| 0 | i16 | lsb | reuses last advance width |
loca — Location index (variable)
Maps glyph index → byte offset in glyf table. numGlyphs + 1 offsets (the +1 marks end of last glyph).
Short format (head.indexToLocFormat == 0): uint16 offsets (multiply by 2 to get real byte offset).
Long format (head.indexToLocFormat == 1): uint32 offsets (use directly).
If loca[i] == loca[i+1], glyph i is empty (e.g. space character).
glyf — Glyph data (variable)
No header. Glyph blocks back to back, located by loca.
Glyph header (10 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | i16 | numberOfContours | ≥ 0 = simple; < 0 = composite |
| 2 | i16 | xMin | glyph bbox |
| 4 | i16 | yMin | |
| 6 | i16 | xMax | |
| 8 | i16 | yMax |
Simple glyph (numberOfContours ≥ 0):
| offset | type | field | notes |
|---|---|---|---|
| 10 | u16[numberOfContours] | endPtsOfContours | last+1 = point count |
| … | u16 | instructionLength | bytes of hinting |
| … | u8[instructionLength] | instructions | TrueType bytecode |
| … | u8[…] | flags | one logical flag per point, compressed |
| … | — | xCoordinates | delta-encoded |
| … | — | yCoordinates | delta-encoded |
Flag bits (each point):
| bit | name | meaning |
|---|---|---|
| 0x01 | ON_CURVE_POINT | on curve (else off-curve control point) |
| 0x02 | X_SHORT_VECTOR | x delta is 1 byte |
| 0x04 | Y_SHORT_VECTOR | y delta is 1 byte |
| 0x08 | REPEAT_FLAG | next byte = repeat count for this flag |
| 0x10 | X_IS_SAME_OR_POSITIVE | (see spec) |
| 0x20 | Y_IS_SAME_OR_POSITIVE | (see spec) |
| 0x40 | OVERLAP_SIMPLE | contours may overlap |
| 0x80 | reserved | 0 |
Composite glyph (numberOfContours < 0):
Loop of components. Each:
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | flags | component flags (below) |
| 2 | u16 | glyphIndex | which glyph to reference |
| 4 | — | arg1, arg2 | int8/uint8 or int16/uint16 per ARG_1_AND_2_ARE_WORDS |
| … | — | transform | 0, 1, 2, or 4 × F2Dot14 per scale flags |
Component flag bits:
| bit | name | meaning |
|---|---|---|
| 0x0001 | ARG_1_AND_2_ARE_WORDS | args are 16-bit |
| 0x0002 | ARGS_ARE_XY_VALUES | args are offsets (else indices) |
| 0x0004 | ROUND_XY_TO_GRID | |
| 0x0008 | WE_HAVE_A_SCALE | one F2Dot14 follows |
| 0x0020 | MORE_COMPONENTS | another component after this |
| 0x0040 | WE_HAVE_AN_X_AND_Y_SCALE | two F2Dot14 follow |
| 0x0080 | WE_HAVE_A_TWO_BY_TWO | four F2Dot14 (matrix) |
| 0x0100 | WE_HAVE_INSTRUCTIONS | after last component: u16 length + bytes |
| 0x0200 | USE_MY_METRICS | |
| 0x0400 | OVERLAP_COMPOUND |
cmap — Character to glyph mapping (variable)
Maps Unicode → glyph index. Multiple subtables; pick the best for your case.
Header (4 bytes):
| offset | type | field |
|---|---|---|
| 0 | u16 | version |
| 2 | u16 | numTables |
Encoding records (numTables of 8 bytes each):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | platformID | 0=Unicode, 1=Mac, 3=Windows |
| 2 | u16 | encodingID | platform-specific |
| 4 | u32 | subtableOffset | from cmap table start |
Common: (3,1) → format 4 (BMP); (3,10) → format 12 (full Unicode).
Format 0 (byte encoding, 256 glyphs):
| offset | type | field |
|---|---|---|
| 0 | u16 | format |
| 2 | u16 | length |
| 4 | u16 | language |
| 6 | u8[256] | glyphIdArray |
Format 4 (segment mapping, BMP):
| offset | type | field |
|---|---|---|
| 0 | u16 | format |
| 2 | u16 | length |
| 4 | u16 | language |
| 6 | u16 | segCountX2 |
| 8 | u16 | searchRange |
| 10 | u16 | entrySelector |
| 12 | u16 | rangeShift |
| 14+ | u16[segCount] | endCode |
| … | u16 | reserved |
| … | u16[segCount] | startCode |
| … | i16[segCount] | idDelta |
| … | u16[segCount] | idRangeOffset |
| … | u16[…] | glyphIdArray |
Format 12 (full Unicode coverage):
| offset | type | field |
|---|---|---|
| 0 | u16 | format |
| 2 | u16 | reserved |
| 4 | u32 | length |
| 8 | u32 | language |
| 12 | u32 | numGroups |
| 16+ | — | groups (numGroups entries, 12 bytes each) |
Each group: { u32 startCharCode; u32 endCharCode; u32 startGlyphID }.
name — Font names (variable)
Human-readable strings (family, style, version, etc.). Stored once in a byte pool; records point in with metadata.
Header (6 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | version | 0 or 1 |
| 2 | u16 | count | number of name records |
| 4 | u16 | storageOffset | start of string pool from table start |
Name records (count of 12 bytes each):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | platformID | 0=Unicode, 3=Windows |
| 2 | u16 | encodingID | platform-specific |
| 4 | u16 | languageID | platform/language tag |
| 6 | u16 | nameID | 1=family, 2=subfamily, 4=full, 5=version |
| 8 | u16 | length | string length in bytes |
| 10 | u16 | stringOffset | from storageOffset |
Then raw string bytes per platform (typically UTF-16BE for Windows).
post — PostScript data (variable)
Italic angle, monospace flag, PostScript names (v2.0 only).
Fixed header (32 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u32 | version | 0x00010000, 0x00020000, or 0x00030000 |
| 4 | i32 | italicAngle | 16.16 fixed-point |
| 8 | i16 | underlinePosition | |
| 10 | i16 | underlineThickness | |
| 12 | u32 | isFixedPitch | 0 or nonzero |
| 16 | u32 | minMemType42 | |
| 20 | u32 | maxMemType42 | |
| 24 | u32 | minMemType1 | |
| 28 | u32 | maxMemType1 |
Version 2.0 only (after header):
| offset | type | field |
|---|---|---|
| 32 | u16 | numGlyphs |
| 34 | u16[numGlyphs] | glyphNameIndex |
| … | — | Pascal-string names for indices ≥ 258 |
OS/2 — OS/2 and Windows metrics (version-dependent)
Font classification, weight, width, coverage, PANOSE, embedding flags.
Version 0 (78 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | version | 0–5 |
| 2 | i16 | xAvgCharWidth | |
| 4 | u16 | usWeightClass | 100–900 |
| 6 | u16 | usWidthClass | 1–9 |
| 8 | u16 | fsType | embedding flags |
| 10 | i16 | ySubscriptXSize | |
| 12 | i16 | ySubscriptYSize | |
| 14 | i16 | ySubscriptXOffset | |
| 16 | i16 | ySubscriptYOffset | |
| 18 | i16 | ySuperscriptXSize | |
| 20 | i16 | ySuperscriptYSize | |
| 22 | i16 | ySuperscriptXOffset | |
| 24 | i16 | ySuperscriptYOffset | |
| 26 | i16 | yStrikeoutSize | |
| 28 | i16 | yStrikeoutPosition | |
| 30 | i16 | sFamilyClass | |
| 32 | u8[10] | panose | |
| 42 | u32 | ulUnicodeRange1 | bits 0–31 |
| 46 | u32 | ulUnicodeRange2 | bits 32–63 |
| 50 | u32 | ulUnicodeRange3 | bits 64–95 |
| 54 | u32 | ulUnicodeRange4 | bits 96–127 |
| 58 | tag[4] | achVendID | |
| 62 | u16 | fsSelection | style flags |
| 64 | u16 | usFirstCharIndex | |
| 66 | u16 | usLastCharIndex | |
| 68 | i16 | sTypoAscender | |
| 70 | i16 | sTypoDescender | |
| 72 | i16 | sTypoLineGap | |
| 74 | u16 | usWinAscent | |
| 76 | u16 | usWinDescent |
Version 1 adds (86 bytes):
| offset | type | field |
|---|---|---|
| 78 | u32 | ulCodePageRange1 |
| 82 | u32 | ulCodePageRange2 |
Version 2/3/4 add (96 bytes):
| offset | type | field |
|---|---|---|
| 86 | i16 | sxHeight |
| 88 | i16 | sCapHeight |
| 90 | u16 | usDefaultChar |
| 92 | u16 | usBreakChar |
| 94 | u16 | usMaxContext |
Version 5 adds (100 bytes):
| offset | type | field |
|---|---|---|
| 96 | u16 | usLowerOpticalPointSize |
| 98 | u16 | usUpperOpticalPointSize |
kern — Kerning pairs (optional, variable)
Spacing adjustments between glyph pairs. Optional; modern fonts use GPOS instead.
Header (4 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | version | 0 |
| 2 | u16 | nTables |
Per subtable header (6 bytes):
| offset | type | field | notes |
|---|---|---|---|
| 0 | u16 | version | subtable version |
| 2 | u16 | length | subtable length in bytes |
| 4 | u16 | coverage | high byte = format; low byte = flags |
Format 0 (sorted pairs):
| offset | type | field | notes |
|---|---|---|---|
| 6 | u16 | nPairs | |
| 8 | u16 | searchRange | binary-search hint |
| 10 | u16 | entrySelector | binary-search hint |
| 12 | u16 | rangeShift | binary-search hint |
| 14+ | — | pairs | nPairs entries, 6 bytes each |
Each pair: { u16 left; u16 right; i16 value }.
For parsing strategy and complete walkthroughs, see Parse a Font.