How to truetype hinting tables: complete reference
- Step 1fpgm — font program — Global hint bytecode: reusable functions defined once and executed at font load. `prep` and per-glyph hint programs call these functions. Deleting it removes the function library; with the callers also gone, nothing references it.
- Step 2prep — control-value (pre) program — Bytecode that runs every time the font is set at a new pixel size, initialising the rasteriser state and the CVT for that size. Once removed, modern engines simply rasterise outlines directly with no setup program to run.
- Step 3cvt — control value table — An array of reference coordinate values (stem widths, heights) that hint bytecode reads via `RCVT`. Note the trailing space in the tag. With the bytecode gone, these values have nothing to reference, so deleting the table is consistent and safe.
- Step 4gasp — grid-fitting / scan-conversion — A small table of pixel-size ranges and flags telling the renderer when to apply grid-fitting, grayscale, or subpixel rendering. Legacy GDI leaned on it heavily; DirectWrite and CoreText set their own policy and ignore it.
- Step 5hdmx / VDMX — device metrics — `hdmx` caches per-pixel-size glyph advance widths; `VDMX` caches vertical extents/line metrics. Both let old renderers skip layout maths. Modern engines compute these on the fly, so the caches are dead weight.
- Step 6LTSH — linear threshold — Records the pixel size above which each glyph's advance width scales linearly (so renderers can stop consulting `hdmx`). Useful only to very old GDI; ignored everywhere else. Removed with the rest.
The seven hinting tables at a glance
Every tag the Hinting Stripper deletes. The control-value table's tag includes a trailing space (cvt ) to make four bytes.
| Tag | Full name | Contains | Primary consumer |
|---|---|---|---|
fpgm | Font Program | Global hint bytecode functions (run once) | GDI / FreeType bytecode interpreter |
prep | Control Value Program | Bytecode run at each new size to set up state | GDI / FreeType bytecode interpreter |
cvt | Control Value Table | Reference coordinate values read by hints | Hint bytecode (fpgm/prep/glyph) |
gasp | Grid-fitting & Scan-conversion | Per-size grid-fit / smoothing policy flags | Legacy GDI; modern engines ignore |
hdmx | Horizontal Device Metrics | Cached advance widths at set pixel sizes | Legacy renderers |
VDMX | Vertical Device Metrics | Cached vertical extents at set pixel sizes | Legacy renderers |
LTSH | Linear Threshold | Per-glyph size where widths scale linearly | Very old GDI only |
What the tool does — and doesn't — to each table
The output is a brand-new sfnt with the seven tags absent and the directory rebuilt. There is no 'emptied but present' state.
| Behaviour | Reality |
|---|---|
| Tables removed | All seven, by tag, in one pass |
| Removal style | Deleted entirely (directory rebuilt) — not zero-filled |
Outlines (glyf/loca, CFF ) | Untouched, copied verbatim |
| Directory header | numTables, searchRange, entrySelector, rangeShift recomputed |
head.checkSumAdjustment | Left stale — not recomputed |
| Output type | Always raw font/ttf, named *.unhinted.ttf |
Who reads hints today
Consumer behaviour for the hint tables as a group. CFF/OTF fonts don't carry these at all.
| Engine | Reads these tables? |
|---|---|
| Windows 7/8 GDI + ClearType | Yes — fpgm/prep/cvt /gasp |
| Windows 10+ DirectWrite | Largely ignores |
| macOS / iOS CoreText | No |
| FreeType (bytecode interpreter on) | Yes, if compiled in |
| FreeType (autohint) / Android | Ignores embedded hints |
Cookbook
How these tables show up in real font dumps, and what their absence looks like after stripping. Use a ttx dump or the font-metadata-extractor to inspect your own files.
A heavily-hinted font's table list (before)
ExampleA GDI-era face carries all the hint tables, often with a large fpgm. This is the inventory the strip targets.
Tables in verdana.ttf: cmap glyf loca head hhea hmtx maxp name post OS/2 fpgm (4,812 B) prep (1,260 B) cvt (824 B) gasp (16 B) hdmx (38,420 B) VDMX (1,904 B) LTSH (1,096 B) → ~48 KB of the file is hint/device-metric tables.
The same font after stripping (after)
ExampleAll seven tags are gone; everything load-bearing remains.
Tables in verdana.unhinted.ttf: cmap glyf loca head hhea hmtx maxp name post OS/2 (no fpgm / prep / cvt / gasp / hdmx / VDMX / LTSH) Reduction reflects the removed tables + 4-byte padding savings.
Mind the trailing space in 'cvt '
ExampleThe control-value table tag is four bytes: c, v, t, space. Searching for 'cvt' without the space can miss it in tooling.
$ ttx -t 'cvt ' font.ttf # correct — note the quoted trailing space $ ttx -t cvt font.ttf # may not match; the tag is 'cvt ' (4 bytes) In a hex dump the directory entry reads: 63 76 74 20 (= 'c','v','t',' ')
A CFF/OTF font has none of these
ExampleOpenType/CFF fonts hint inside the CFF charstrings, so the seven tables simply aren't present — stripping finds nothing.
Tables in SourceSerif.otf (OTTO): CFF cmap head hhea hmtx maxp name OS/2 post (no fpgm / prep / cvt / gasp / hdmx / VDMX / LTSH) → Hinting Stripper removes nothing; bytes unchanged (renamed .ttf).
Reading the savings
ExampleThe size drop equals the removed tables' bytes plus their 4-byte padding and the freed directory slots.
Original: 738.0 KB
Removed: fpgm+prep+cvt+gasp+hdmx+VDMX+LTSH (~48 KB incl. padding)
+ 7 directory entries (7 x 16 B = 112 B)
Output: ~601 KB (Saved ~19%)Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
Searching for 'cvt' and finding nothing
By designThe control-value table's tag is cvt with a trailing space — four bytes, 63 76 74 20. Tooling that searches for the three-character cvt can miss it. The Hinting Stripper handles the spaced tag internally; just remember the space when grepping a dump.
Tables are deleted, not emptied
By designSome descriptions say hinting tables are 'emptied' — this tool deletes them. The output sfnt's directory no longer lists fpgm/prep/cvt /etc. There's no zero-length placeholder; the rebuilt directory simply has fewer entries.
OTF/CFF font has none of the seven tables
PreservedCFF fonts store hints in the CFF table's charstrings, not in these seven. The strip finds nothing to remove, so a CFF font comes back byte-identical (renamed .unhinted.ttf). The reference here applies only to TrueType-outline fonts.
Removing cvt without removing fpgm/prep
Not supportedThe tool removes all seven together — you can't keep cvt while dropping the bytecode. That's actually the consistent choice: with fpgm/prep gone, a lone cvt would have no reader. Selective removal needs a desktop tool like fonttools.
Font has gasp but no fpgm/prep
SupportedSome autohinted webfonts ship only a gasp table (a smoothing policy) without bytecode. The strip removes the gasp too; modern engines apply their own smoothing policy, so rendering is unaffected on DirectWrite/CoreText.
hdmx is the biggest table in the file
Expectedhdmx caches advance widths for many pixel sizes and can dwarf the actual hint programs — tens of KB in older fonts. Removing it is often the single largest contributor to the size drop, even though it's a cache, not 'hinting' per se.
Stale head checksum after removal
Checksum warningRebuilding the directory changes offsets, but head.checkSumAdjustment is left as-is. Renderers ignore it; strict validators may flag it. It's a metadata field, not a rendering one — fix with fonttools if a QA gate requires it.
Variable font's variation tables are untouched
Preservedfvar, gvar, STAT, HVAR, etc. are not in the strip list, so a variable font keeps them and stays variable. The hint tables (fpgm/prep/cvt /gasp) are still removed if present. Use variable-font-freezer if you instead want to flatten the axes.
Expecting fewer than seven tables to be listed as removed
ExpectedThe result metric always reads Removed tables: fpgm, prep, cvt, gasp, hdmx, VDMX, LTSH — that's the fixed target list, not necessarily the tables that were actually present. A font missing some of them still shows the full list; only the present ones contributed to the size drop.
post table or kern confused for hinting
Preservedpost (glyph names / italic angle) and kern/GPOS (spacing) are not hinting tables and are never removed. If your concern is metadata or kerning bloat, those are separate tools — name/post scrubbing belongs to name-table-cleaner.
Frequently asked questions
How many hinting tables does the tool remove?
Seven: fpgm, prep, cvt (trailing space), gasp, hdmx, VDMX, and LTSH. It's a fixed list with no per-table options.
What's actually in fpgm, prep, and cvt?
fpgm holds global hint bytecode functions run once at load; prep is bytecode run at every new pixel size to set up the rasteriser; cvt is an array of reference coordinate values (stem widths, heights) that the bytecode reads.
Why does cvt have a trailing space?
OpenType table tags are exactly four bytes. 'cvt' is three characters, so it's padded to cvt with a space. Watch for it when grepping a directory or using ttx -t.
What is gasp?
The grid-fitting and scan-conversion table: pixel-size ranges with flags for when to grid-fit, grayscale, or subpixel-render. Legacy GDI used it heavily; DirectWrite and CoreText set their own policy and ignore it. The tool removes it with the rest.
What are hdmx, VDMX, and LTSH?
Device-metric caches. hdmx stores advance widths at set pixel sizes, VDMX stores vertical extents, and LTSH marks the size above which widths scale linearly. They let old renderers skip maths; modern engines compute on the fly and ignore them.
Are the tables emptied or deleted?
Deleted. The tool rebuilds the sfnt directory without them — there's no zero-length placeholder. That's why the directory entry count drops and the file shrinks by the tables' bytes plus padding.
Can I keep just one of these tables?
Not with this tool — it removes all seven together. Keeping cvt without fpgm/prep would leave it unread anyway. For selective removal, use a desktop tool like fonttools.
Do OTF/CFF fonts have these tables?
No. CFF fonts hint inside their CFF charstrings, so none of the seven are present. The tool finds nothing to remove and returns the font unchanged (renamed .ttf).
Why does the result list all seven even if my font lacked some?
The Removed tables metric reports the fixed target list, not an audit of what was present. A font missing, say, LTSH still shows the full list; only the tables that existed contributed to the size reduction.
Does removing these change glyph shapes?
No. Outlines live in glyf/loca (or CFF ) and are copied verbatim. Hinting only affects how outlines snap to pixels at small sizes on engines that read it — never the underlying shape.
Will deleting them break the font's checksum?
The per-table checksums in the directory are preserved, but the whole-file head.checkSumAdjustment is not recomputed. Renderers ignore it; strict validators may warn. Run fonttools once if you need a clean checksum.
Where can I see these tables in my own font?
Dump them with ttx, or inspect the table inventory with font-metadata-extractor or font-format-identifier before deciding whether stripping is worth it.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.