How to visualise a font's unicode coverage as a block map
- Step 1Drop your font — Click the drop zone or drag in a `.ttf`, `.otf`, `.woff`, or `.woff2`. WOFF2 is decompressed with wawoff2 and WOFF with pako before parsing, so all four formats are read natively — you don't convert first.
- Step 2Press Process — There are no options to configure (the tool's schema lists zero settings). opentype.js parses the font and the tool iterates every glyph, collecting `glyph.unicode` and every entry in `glyph.unicodes`.
- Step 3Read the headline — The top line — `N of 346 Unicode blocks covered` — plus `unique codepoints covered` tells you the font's breadth immediately. The metric chips repeat Unicode blocks, total codepoints, and total glyphs.
- Step 4Scan the block matrix — Each row is one covered block: name, `U+start–U+end` range, `hit / size` glyph count, percentage, and a green fill bar. Blocks with zero coverage are omitted, so everything you see is a script the font actually carries.
- Step 5Check the common-web-subsets table — Below the matrix, the six web subsets (Latin, Latin-ext, Cyrillic, Greek, Vietnamese, symbols) are scored against their exact ranges. These are the buckets that line up with a real subsetting plan.
- Step 6Download the report — Hit Download to save the rendered matrix as `<fontname>.coverage.html` — a self-contained file you can drop into a font-evaluation folder, attach to a procurement ticket, or diff against a later version.
What the Coverage Map reports
Every field in the output, where the number comes from, and how to read it. There are no configurable options — this is the full surface area of the tool.
| Output field | What it measures | How it's computed | How to read it |
|---|---|---|---|
N of 346 Unicode blocks covered | Number of named blocks with ≥1 covered codepoint | computeBlockCoverage buckets every covered codepoint into a block via binary search; blocks with a non-zero count are tallied | A Latin-only display face lands around 3–6; a pan-European text face 8–15; a CJK font far more |
unique codepoints covered | Count of distinct codepoints the cmap maps | Size of the Set<number> built from glyph.unicode + glyph.unicodes | This is the encoded character count — usually lower than total glyphs |
| Block row: Range | The block's codepoint span | U+start–U+end from the official block table | Tells you which characters the block contains, e.g. Basic Latin U+0000–U+007F |
| Block row: Glyphs | Covered vs total in that block | hit / size where size = end - start + 1 | 95 / 128 in Basic Latin means 95 of the 128 slots are mapped |
| Block row: % | Coverage percentage of the block | round(hit / size * 100) | 100% means every slot is mapped; partial flags missing characters worth investigating |
| Common web subsets table | Coverage of 6 web-relevant ranges | Each subset's [lo, hi] ranges scored against the covered set | Drives subsetting: 100% Latin = safe to ship a latin subset |
The six common web subsets it scores
The secondary table maps to UNICODE_SUBSETS in the codebase. These are NOT the same as the 346-block matrix — they are curated web ranges, scored separately.
| Subset key | Label in the output | Ranges scored | Codepoints |
|---|---|---|---|
latin | Latin Basic + Latin-1 Supplement | U+0020–U+007E, U+00A0–U+00FF | 191 |
latin-ext | Latin Extended-A + Extended-B | U+0100–U+024F, U+1E00–U+1EFF | 592 |
cyrillic | Cyrillic | U+0400–U+04FF, U+0500–U+052F | 304 |
greek | Greek | U+0370–U+03FF, U+1F00–U+1FFF | 400 |
vietnamese | Vietnamese | U+0100–U+024F, U+1E00–U+1EFF, U+20AB | 593 |
symbols | General punctuation + symbols | U+2000–U+206F, U+2070–U+209F, U+20A0–U+20CF | 256 |
Cookbook
Real reading workflows — how to turn the matrix into a yes/no decision. The tool has no options, so every recipe is about interpreting the output, not configuring it.
Confirm a font is safe for an English-only site
ExampleThe minimum bar for English plus common European punctuation is the Latin subset at 100%. Read the common-web-subsets row, not the full block matrix.
Coverage Map output (excerpt): Common web subsets Latin Basic + Latin-1 Supplement 191 / 191 100% Latin Extended-A + Extended-B 40 / 592 7% Cyrillic 0 / 304 0% Reading: Latin is fully covered → no tofu for English + €, £, ©, é, ñ. Latin-ext at 7% means most accented-Eastern-European letters are missing. Cyrillic 0% → do not use this font for Russian copy.
Spot a partially-covered block before it bites
Example100% is not guaranteed even for blocks the font 'supports'. A block at 62% will tofu on the missing slots. The matrix surfaces this directly.
Unicode block Range Glyphs % Basic Latin U+0000-U+007F 95 / 128 74% Latin-1 Supplement U+0080-U+00FF 62 / 128 48% Latin Extended-A U+0100-U+017F 80 / 128 62% Latin-1 at 48% is the warning: some of š ž ÿ ø ð þ are absent. Dig into the actual missing characters before assuming 'Latin = fine'.
Tell a brand display face from a text workhorse
ExampleThe headline block count separates the two instantly. Display faces are narrow; text families are broad.
Font A (display): 4 of 346 Unicode blocks covered
220 unique codepoints covered
Font B (text): 11 of 346 Unicode blocks covered
2,840 unique codepoints covered
Font A is a headline face — Latin + a few symbols only.
Font B is built for body text across European languages.Verify a subset you already produced
ExampleAfter running the Font Subsetter, re-run the Coverage Map on the output to confirm the subset kept what you needed and dropped the rest.
Before subsetting: 14 of 346 blocks, 3,100 codepoints After `latin` subset: 3 of 346 blocks, 191 codepoints Common web subsets (after): Latin Basic + Latin-1 Supplement 191 / 191 100% Cyrillic 0 / 304 0% The subset did its job: Latin intact, Cyrillic stripped → smaller file. Need to build the subset? Use /font-tools/font-subsetter.
Save the report as an evaluation artefact
ExampleThe download is a self-contained .coverage.html — perfect for attaching to a font-procurement decision or committing alongside a font in your repo.
Output file: Inter-Regular.coverage.html The file embeds the full block matrix + the web-subsets table, styled inline (no external CSS). Open it in any browser, or drop it into a /fonts/evaluations/ folder so the next person sees exactly which scripts each candidate font covered.
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.
Glyphs reachable only via OpenType features aren't counted
By designThe map counts encoded codepoints — glyph.unicode and glyph.unicodes. Alternates, swashes, and stylistic-set glyphs that have no cmap entry (accessible only through GSUB) exist in the font but contribute nothing to coverage. That's correct: an unencoded glyph can't be typed, so it can't fix tofu. The full glyph total appears in the metrics; the gap between it and unique codepoints is your unencoded alternates.
A character renders but the block shows < 100%
ExpectedCoverage is per-codepoint, so a block at 60% means 40% of its slots have no glyph — but those slots may be unassigned in Unicode anyway, or simply characters you'll never use. A percentage below 100 is a prompt to check which specific codepoints are missing, not an automatic fail.
Empty blocks don't appear in the matrix
By designOnly blocks with at least one covered glyph are listed. If you're looking for a script and don't see its row, the font has zero glyphs in that block. This keeps the matrix readable instead of showing 340-odd rows at 0% — but it means absence is shown by omission, not by a 0% line.
Codepoints in unassigned gaps are silently skipped
ExpectedIf a glyph is mapped to a codepoint that falls outside every named block (an unassigned gap, or a Private Use sub-range with no block), blockForCodepoint returns null and that codepoint is dropped from the block tally — though it still counts toward total glyphs. Properly-built fonts rarely hit this; malformed cmaps occasionally do.
Surrogate-range codepoints
ExpectedU+D800–U+DFFF (High/Low Surrogates) are reserved for UTF-16 encoding and are not real characters. opentype.js exposes scalar values rather than surrogate halves, so a correct font won't report coverage there; if a row appears it signals a cmap built from raw UTF-16 code units rather than codepoints.
File over the tier size limit
413 rejectedFree tier caps font uploads at 5 MB, Pro at 50 MB, Developer at 1 GB. A file over your tier's limit is rejected before parsing with a clear message — large CJK fonts routinely exceed 5 MB, so coverage-mapping a full Han font usually needs Pro.
Corrupt or non-font file
Invalid inputfileToSfntBuffer sniffs the magic bytes; anything that isn't a recognised TTF/OTF/WOFF/WOFF2 throws Unsupported font format. A renamed .zip or a .dfont resource fork won't parse. Convert exotic packaging to a flat sfnt first.
Variable font reports its default-instance coverage
SupportedA variable font has one cmap shared across all axis positions, so coverage is the same regardless of weight or width. The map reads that single cmap and reports the family's full character set — you don't need to freeze an instance first to see coverage.
Bitmap-only or colour fonts (CBDT/sbix/COLR)
SupportedCoverage is read from the cmap, which exists independently of how glyphs are drawn. An emoji font with colour layers reports its emoji-block coverage normally — the map doesn't care whether the outlines are vector, bitmap, or layered colour.
Frequently asked questions
How many Unicode blocks does the matrix score against?
All 346 standard blocks from Unicode 17.0.0 (parsed from the official Blocks.txt). The headline reads N of 346 Unicode blocks covered. Only the blocks the font actually covers are listed in the table — empty blocks are omitted to keep the matrix readable.
Does it output JSON I can parse?
No. This tool produces a single HTML report (<fontname>.coverage.html) with the block matrix and the web-subsets table, plus a few summary metrics. There is no JSON export. If you need machine-readable per-block data for a pipeline, replicate the logic in Node with opentype.js — the design-system automation guide walks through exactly that.
What's a sensible coverage target?
Latin Basic + Latin-1 Supplement at 100% is the baseline for English and common European punctuation. General Punctuation matters for typographic quotes and dashes. Currency Symbols depends on your audience. CJK and other scripts you target should be near-100% for the characters you'll actually render. Most consumer Latin sites use a tiny fraction of all 346 blocks.
Why does a glyph show in my design tool but not in the map?
Almost always because the glyph has no Unicode codepoint — it's an OpenType alternate reachable only via a feature like salt or ss01. The map counts encoded codepoints, so unencoded alternates don't count. That's deliberate: an unencoded glyph can't be typed and can't fix tofu.
Which file formats can I drop in?
TTF, OTF, WOFF, and WOFF2. WOFF2 is decompressed with wawoff2 and WOFF with pako before parsing, so all four are read natively — no manual conversion. To convert between formats first, see ttf-to-woff2 or woff2-to-ttf.
Are there any options to configure?
None. The tool's schema lists zero options — you drop the font and press Process. The output is fully determined by the font's cmap, so two people mapping the same file get identical results.
Does it tell the difference between CFF and TrueType outlines?
No — coverage is format-agnostic. It only reports whether a glyph exists for each codepoint. Whether those outlines are CFF (PostScript) or TrueType (quadratic), and their quality, are separate concerns — check the Font Format Identifier for the outline flavour.
How big a font can I map?
Up to your tier's per-file limit: 5 MB free, 50 MB Pro, 1 GB Developer. Latin and pan-European fonts are well under 5 MB. Full CJK fonts (with tens of thousands of glyphs) often exceed 5 MB, so they typically need Pro.
Does it confirm a font will render a language correctly?
It confirms the codepoints exist, which is necessary but not sufficient. Complex scripts (Arabic, Devanagari, Indic) also need correct GSUB/GPOS shaping rules — 100% codepoint coverage with broken shaping still renders wrong. Use the map as the first gate, then visually proof rendered text.
What do the metric chips at the top mean?
Three numbers: Unicode blocks (N / 346), Total codepoints (distinct encoded codepoints), and Total glyphs (the font's full glyph count including unencoded ones). The gap between total glyphs and total codepoints is your unencoded alternates and ligatures.
Is anything uploaded?
No. opentype.js, wawoff2, and pako all run in your browser; the result badge reads 0 bytes uploaded. Only an anonymous run counter is recorded for signed-in dashboard stats — never the font bytes. Safe for unreleased or licensed type.
Can I compare two fonts?
Run each separately and download both .coverage.html reports, then compare side by side. For a numeric breadth-vs-size view, pair this with the Glyph Count Analyzer; the comparison guide explains which question each answers.
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.