How to opentype name table: platforms, encodings, languages
- Step 1Read the platformID first — Each record starts with a platformID: 0 (Unicode), 1 (Macintosh), 2 (ISO, deprecated), 3 (Windows). For web fonts, platformID 3 is the one consumers read. The JAD cleaner keeps only platformID 3 and drops 0, 1, and 2.
- Step 2Then read the encodingID, which depends on the platform — encodingID is interpreted relative to the platform. Windows (3): 0 = Symbol, 1 = Unicode BMP, 10 = Unicode full. Mac (1): 0 = Roman, plus a long table of script codes. The cleaner keeps Windows encodingID 1 (Unicode BMP) and drops the rest, including Windows-Symbol (0) and Windows-Unicode-full (10).
- Step 3Then the languageID — Under Windows the languageID is a Microsoft LCID: `0x409` English-US, `0x809` English-UK, `0x40C` French-France, `0x407` German, `0x411` Japanese, and so on. Under Mac it's a small index (0 = English). Browsers read `0x409`. The cleaner keeps `0x409` only — even English-UK (`0x809`) is dropped.
- Step 4Finally the nameID — what the string actually is — The nameID identifies the field: 1 Family, 2 Subfamily, 4 Full name, 5 Version, 6 PostScript name, 13 Licence, 14 Licence URL (the ones the cleaner keeps), plus 0 Copyright, 3 Unique ID, 7 Trademark, 16/17 Typographic family/subfamily, and 256+ variable-font axis names (the ones it drops).
- Step 5Spot the canonical browser record — Combine the coordinates: platformID 3, encodingID 1, languageID `0x409`, nameID 1 is the family name a browser matches in `@font-face`. nameID 2 (subfamily) and 4 (full name) at the same address complete the matching set.
- Step 6Identify removable redundancy — Anything that is not (3, 1, 0x409) for a kept nameID is removable for web delivery: Mac duplicates, localised strings, Windows-Symbol/Unicode-full encodings, and decorative nameIDs. Run [name-table-cleaner](/font-tools/name-table-cleaner) to remove them automatically, [font-metadata-extractor](/font-tools/font-metadata-extractor) to inspect them first, or [font-fingerprinter](/font-tools/font-fingerprinter) to see how name-table contents factor into a font's identity hash.
platformID and encodingID combinations
encodingID is only meaningful relative to its platformID. The cleaner keeps exactly one combination: platform 3 / encoding 1.
| platformID | Meaning | Common encodingID values | Kept by cleaner? |
|---|---|---|---|
| 0 | Unicode (platform-agnostic) | 3 = Unicode BMP, 4 = Unicode full, 6 = Unicode full (format 13 cmap) | No |
| 1 | Macintosh (legacy) | 0 = Roman (plus a large script table) | No — all Mac records dropped |
| 2 | ISO (deprecated, do not use) | 0 = ASCII, 1 = ISO 10646, 2 = ISO 8859-1 | No |
| 3 | Windows | 0 = Symbol, 1 = Unicode BMP (UTF-16BE), 10 = Unicode full | Only encodingID 1 is kept |
Common Windows languageIDs (LCIDs)
Under platformID 3, languageID is a Microsoft LCID. Browsers and the cleaner read English-US (0x409) only.
| LCID (hex) | Locale | Kept by cleaner? |
|---|---|---|
| 0x409 | English (United States) | Yes — the only language kept |
| 0x809 | English (United Kingdom) | No — dropped |
| 0x40C | French (France) | No — dropped |
| 0x407 | German (Germany) | No — dropped |
| 0x410 | Italian (Italy) | No — dropped |
| 0x411 | Japanese | No — dropped |
| 0x804 | Chinese (Simplified, PRC) | No — dropped |
Who reads which record
Different consumers prefer different coordinates. Modern browsers and OS font engines all read the Windows-English record.
| Consumer | Record it reads | Notes |
|---|---|---|
Browsers (@font-face) | (3, 1, 0x409) | Reads English-US regardless of system locale; uses nameID 1/2/4 for matching |
| Modern macOS / Font Book | (3, 1, 0x409) preferred; (1, 0, 0) fallback | Reads Windows records since 10.6 (2009); Mac records are legacy |
| Pre-2010 macOS | (1, 0, *) Mac-platform | The historical reason Mac records exist at all |
| Windows GDI / DirectWrite | (3, 1, *) localised to user | Falls back to 0x409 when the user's locale record is absent |
Cookbook
Real record breakdowns showing how the same string repeats across coordinates, and how the canonical (3, 1, 0x409) record is identified.
The family name stored four ways
ExampleA typical commercial font stores nameID 1 (family) once per platform and locale. All four resolve to the same human-readable string — only the address differs.
nameID 1 (Family) appears as: (1, 0, 0) Mac, Roman, English -> "Brand Sans" (3, 1, 0x409) Windows, Unicode, EN-US -> "Brand Sans" <- browsers read this (3, 1, 0x40C) Windows, Unicode, FR -> "Brand Sans" (3, 1, 0x411) Windows, Unicode, JA -> "Brand Sans" Cleaner keeps row 2 only.
Decoding a raw record header
ExampleEach 12-byte record entry is platformID, encodingID, languageID, nameID, length, offset (all uint16, big-endian). Here's how to read one.
Bytes: 00 03 00 01 04 09 00 01 00 12 00 00
|---| |---| |---| |---| |---| |---|
pid eid lid nid len off
3 1 0x409 1 18 0
-> Windows / Unicode / English-US / Family,
18 bytes long at string-storage offset 0
-> this is the canonical browser record.Why UTF-16BE matters for the length
ExampleWindows-Unicode (3, 1) records store strings as UTF-16 big-endian, so a 9-character ASCII name is 18 bytes, not 9. Mac-Roman (1, 0) records store one byte per character. Same text, different byte length.
Family name "Brand Sans" (10 chars): (3, 1, 0x409) Windows-Unicode: 20 bytes (UTF-16BE) (1, 0, 0) Mac-Roman: 10 bytes (1 byte/char) The cleaner keeps the 20-byte Windows-Unicode version because that's what every modern consumer reads.
Counting the redundancy
ExampleMultiply combinations to see why tables balloon. Eight locales, two platforms, seven strings is the difference between a 7-record table and a 100+ record one.
7 strings x 1 platform (Win) x 1 lang (EN-US) = 7 records <- cleaned 7 strings x 2 platforms x 8 languages = 112 records <- typical source The cleaner reduces 112 -> 7 by keeping only (platform 3, encoding 1, language 0x409).
What 'browsers ignore locale' means in practice
ExampleA German user on a German OS still gets the English-US name record because the OpenType spec ties web font matching to 0x409, not the user's locale.
User locale: de-DE Font carries: (3, 1, 0x407) German + (3, 1, 0x409) English Browser reads: (3, 1, 0x409) English-US -> dropping the German record (as the cleaner does) changes nothing for the browser.
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.
A font with only Mac-platform records
Error on cleanSome very old fonts carry only platformID 1 (Mac) records and no Windows ones. Modern consumers may still find a name through fallback, but the JAD cleaner filters to platformID 3 / encodingID 1 / 0x409 — if zero records match, it errors with No Windows English name records to keep. The font isn't broken; it just predates the Windows-record convention. Add a Windows-Unicode-English record before cleaning.
Windows-Symbol encoding (3, 0) mistaken for Unicode
Dropped by designSymbol fonts often carry name records under encodingID 0 (Symbol) as well as or instead of 1 (Unicode). The cleaner keeps only encodingID 1, so a record stored solely under (3, 0) is dropped. For a genuine symbol font this is usually fine because the name strings are normally also present under (3, 1) — but verify with font-metadata-extractor first.
Windows Unicode-full (3, 10) records
Dropped by designencodingID 10 (Unicode full repertoire, beyond the BMP) appears in cmap subtables, and occasionally a font mirrors name records under (3, 10). The cleaner keeps only (3, 1); a (3, 10) name record is dropped. Name strings rarely need characters beyond the BMP, so the kept (3, 1) record carries the same text.
Mac-Roman strings with non-ASCII characters
Removed (legacy)Mac-Roman (1, 0) is a single-byte encoding that maps bytes 128–255 to a Mac-specific character set, so an accented designer name encodes differently from its Windows-Unicode counterpart. Tools that misread Mac-Roman as Latin-1 show mojibake. The cleaner sidesteps the issue entirely by dropping all Mac records and keeping the unambiguous UTF-16BE Windows record.
languageID above 0x8000 (language-tag records)
Removed (rare)Name-table version 1 allows languageIDs ≥ 0x8000 that index into a separate language-tag array (for locales without an LCID). These are rare and never English-US, so the cleaner drops them. If a font relies on language-tag records for an exotic locale, cleaning loses those strings — expected for web delivery, but check first if the font is for a regional desktop deployment.
Pre-2010 macOS rendering after Mac records removed
Expected (irrelevant today)The only consumer that ever preferred Mac-platform records was macOS before 10.6 (2009). Removing them has no effect on any OS or browser still receiving security updates. If you are targeting genuinely ancient Macs (you almost certainly are not on the web), keep the Mac records — otherwise they're dead weight.
English-UK users and the 0x809 record
Removed by designA font may carry both English-US (0x409) and English-UK (0x809) name records, differing only in spelling of words like 'colour'. The cleaner keeps 0x409 only. Browsers read 0x409 regardless, so UK users see the US-spelled name string in any font-picker UI that surfaces it — a cosmetic difference at most.
Confusing nameID with platformID
By design (clarification)A frequent misread: the keep-list {1, 2, 4, 5, 6, 13, 14} is a set of nameIDs, not platformIDs. The platform/encoding/language filter (3, 1, 0x409) is separate and applies on top. A record survives only if it matches both the (3, 1, 0x409) address and has a kept nameID — both conditions, not either.
Frequently asked questions
Why does a font store the same name 50 times?
Because each name record is addressed by platformID × encodingID × languageID, and foundries license fonts across operating systems and locales. The family name gets stored once per (platform, encoding, language) combination — Windows and Mac, then English, French, German, Japanese, and so on. Multiply by the number of distinct strings (family, subfamily, full name, version, etc.) and you reach 50–80 records for maybe seven actual pieces of information.
Which exact record do browsers read?
platformID 3 (Windows), encodingID 1 (Unicode BMP), languageID 0x409 (English-US). Browsers match @font-face families against nameID 1 (family), 2 (subfamily), and 4 (full name) at that address, regardless of the user's system locale. That's the combination the name-table cleaner keeps.
What is encodingID and why does it matter?
encodingID tells you how the string bytes are encoded, interpreted relative to the platformID. Under Windows (3): 0 = Symbol, 1 = Unicode BMP (UTF-16 big-endian), 10 = Unicode full. Under Mac (1): 0 = Roman (single-byte). It matters because the same text occupies a different number of bytes and decodes with a different algorithm depending on the encoding — UTF-16BE for Windows-Unicode, one byte per character for Mac-Roman.
What's the difference between 0x409 and 0x809?
Both are English LCIDs: 0x409 is English (United States), 0x809 is English (United Kingdom). A font may carry both, differing only in spelling. Browsers and the JAD cleaner read/keep 0x409 only; 0x809 is dropped. The practical effect is cosmetic — a UK user sees the US-spelled name in any UI that surfaces the family name string.
Are Mac-platform records ever still needed?
Only for macOS versions before 10.6 Snow Leopard (2009), which preferred Mac-platform records. Every macOS since then, plus Windows, Linux, and all browsers, read the Windows-platform records. On the web, Mac records are pure legacy — which is why the cleaner removes them with no functional impact.
Why is platformID 2 (ISO) never used?
platformID 2 (ISO) is deprecated in the OpenType spec and should not appear in modern fonts. If you see it, the font was built by an old tool. The cleaner drops it along with all non-Windows platforms. There is no consumer that prefers ISO-platform name records.
Does the system locale change which name record I get?
For web font matching, no. The OpenType convention ties browser matching to English-US (0x409). A French user on a French OS still gets the 0x409 record. Desktop applications may prefer the user's locale record when present and fall back to 0x409 when absent — which is why keeping only 0x409 is safe for browsers but can change a localised desktop font menu label.
How are name strings actually encoded in the bytes?
For Windows-Unicode (3, 1) records, strings are UTF-16 big-endian, so a 10-character ASCII name takes 20 bytes. For Mac-Roman (1, 0) records, it's one byte per character with bytes 128–255 mapped to the Mac character set. The name table stores all string data in one pool; each record points at it via an offset and length.
What's a languageID above 0x8000?
Name-table version 1 adds 'language-tag records': languageIDs ≥ 0x8000 index into a separate array of BCP-47 language tags, used for locales that have no Microsoft LCID. They're rare. The cleaner drops them since they're never English-US — fine for web delivery, but relevant if a font targets an exotic regional desktop deployment.
How do I see all the records in my font?
Use font-metadata-extractor — it decodes the name table read-only and shows the nameIDs present (copyright, family, subfamily, full name, version, PostScript, licence, sample text, and more). Run it before cleaning to know exactly what will be kept versus dropped, then run name-table-cleaner to remove the redundancy.
Will dropping localised records ever cause a visible problem?
Not in any browser — they read 0x409. The only place it can matter is a desktop font menu in a locale-aware application that preferred a localised family name, or a design tool reading variable-font axis names. For pure web delivery, dropping every non-(3, 1, 0x409) record is invisible to users.
Why keep nameID 5 (version) and 6 (PostScript name) if browsers ignore them?
Browsers match on 1/2/4, but the version string (5) is useful for cache-busting and debugging which build of a font shipped, and the PostScript name (6) is required by PDF embedding and some print/PostScript workflows. They're cheap to keep and avoid breaking non-browser consumers, so the cleaner retains them even though @font-face matching doesn't use them.
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.