head, hhea, post: three small tables

Last updated 2026-11-02

On this page

head, hhea and post hold a dozen fields each, and their names do not look as tidy as OS/2. But they decide how fine the grid is, how tall a line is, how far italic leans and where the underline sits. They are covered together because they are usually read together.

Field Meaning
unitsPerEm The scale of font units. Usually 1000 (CFF) or 2048 (TrueType); valid range 16–16384
xMin / yMin / xMax / yMax Overall bounding box of all outlines
macStyle Bits: 0 bold, 1 italic, 2 underline, 3 outline, 4 shadow, 5 condensed, 6 extended
fontRevision Numeric revision (unrelated to the version string in name)
indexToLocFormat Whether loca uses short or long offsets
magicNumber Fixed 0x5F0F3CF5, used to confirm this really is a font
created / modified Timestamps, seconds since 1904

unitsPerEm is the first key to reading any metrics drawing: every coordinate in the font is counted in these units, not pixels. At 16px, one unit in a font with unitsPerEm = 2048 is 16 / 2048 of a pixel.

macStyle and OS/2's fsSelection say the same things in two dialects: the old Mac way and the Windows way. Read fsSelection first and keep macStyle as a fallback.

hhea: the horizontal baseline trio

Field Meaning
ascender Typographic ascent (positive)
descender Typographic descent (usually negative)
lineGap Suggested extra gap between lines
advanceWidthMax Widest advance in the font
numberOfHMetrics Entries in hmtx; the rest repeat the last one
caretSlopeRise / Run Caret angle, used to draw a slanted insertion bar in italics

Many renderers treat ascender + |descender| + lineGap as "the font's own line height". But OS/2 carries two more sets (sTypo* and usWin*) — which one counts depends on bit 7 of fsSelection. Different line heights on macOS and Windows usually come from exactly that choice.

post: italics and underlines

Field Meaning
italicAngle Slope in degrees, counter-clockwise positive; a forward-leaning italic is negative (often −12)
underlinePosition Distance from baseline to the top of the underline; negative means below the baseline
underlineThickness How thick the underline is
isFixedPitch Whether the font is monospaced
Glyph names Only in version 2.0 — names such as A or uni4E2D

italicAngle is often used as circumstantial evidence for "is this a real italic": true italics almost always write a non-zero angle, while a slanted upright might write 0 or something arbitrary. It cannot be the only test, but combined with fsSelection it beats reading the name.

Three sign conventions not to flip

This is where metrics drawings most often go wrong:

Value Actual direction
hhea.descender Negative (below baseline)
OS/2.sTypoDescender Negative
OS/2.usWinDescent Positive — it is "how far down", not a coordinate
post.underlinePosition Negative (below baseline)
post.italicAngle Negative = leaning right

This site normalises all of these into one "y up is negative" font coordinate system in shared/src/fonts/metrics.ts; components never flip them again. Mixing the two conventions flips or mirrors the whole drawing.

What these three do not cover

  • Whether a character exists: cmap;
  • How many glyphs: maxp;
  • Weight and x-height: OS/2.

Further reading

  • OpenType spec: the head, hhea and post sections
  • Apple TrueType Reference Manual: the original head / post definitions