How to ttf to woff2: edge cases and troubleshooting reference
- Step 1Confirm the input format first — Before blaming the converter, run the file through the [Font Format Identifier](/font-tools/font-format-identifier). It reads the 4-byte magic and tells you if a file claiming to be `.ttf` is really a `.ttc` (`ttcf`) — the single most common cause of conversion errors.
- Step 2Check the file against your tier limit — The converter enforces a per-file byte cap: 5 MB Free, 50 MB Pro, 1 GB Developer. An over-limit file is blocked before compression with `exceeds the <tier> tier per-job limit`. CJK and multi-script fonts are the usual offenders.
- Step 3If it's a collection, split it — A `.ttc` bundles multiple faces and is not supported. Split it into individual TTF/OTF files with a desktop tool (`otc2otf`, or `fonttools` ttx/ttLib), then convert each face separately.
- Step 4If output is huge, subset rather than re-compress — WOFF2 compresses the bytes you keep; it can't drop glyphs. For a CJK or icon font, use the [Font Subsetter](/font-tools/font-subsetter) or [Latin Filter](/font-tools/latin-filter) to cut to the characters you need, then convert the subset.
- Step 5If a colour font renders monochrome, check the renderer — WOFF2 preserves COLR/CPAL, sbix, CBDT/CBLC, and SVG tables losslessly. Monochrome rendering is a browser-support question (e.g. Safari renders sbix, Chrome renders COLRv1), not a conversion bug — the colour data is in the WOFF2.
- Step 6If conversion errors out, read the red panel — A genuine failure (corrupt SFNT, unsupported format) appears in the red error box with the message — e.g. `Unsupported font format: ttc` or a compression failure. There's no silent drop; if you see an error, the message names the cause.
Conversion diagnostic matrix
Symptom → what the converter actually does → the fix. Behaviour is grounded in the implementation (format sniff, size gate, wawoff2 compress).
| Symptom | What happens | Fix |
|---|---|---|
.ttc (or .ttc renamed .ttf) | Detected as ttcf; Unsupported font format: ttc thrown | Split into TTF/OTF faces first, convert each separately |
| File over the size cap | Blocked pre-compression: exceeds the <tier> tier per-job limit | Subset to drop bytes, or upgrade tier (5 MB → 50 MB → 1 GB) |
| Non-font / unrelated file | Extension or magic check rejects: not a supported font | Verify with Font Format Identifier |
| CJK WOFF2 still multi-MB | Converts fine; ~25–35% saving only (dense glyph data) | Subset to your locale's characters, then convert |
| WOFF2 input → ~0% saving | Decompressed then re-compressed; near-identical size | Expected — nothing to fix; it's a faithful round-trip |
| Colour font shows monochrome | COLR/sbix/CBDT preserved; renderer lacks support | Renderer/browser issue — data is intact in the WOFF2 |
| Corrupt / truncated SFNT | wawoff2.compress() returns falsy → error surfaced | Re-export the font from its source app |
How each special table type fares
WOFF2 is table-agnostic — it wraps whatever SFNT tables are present. Rendering of colour/variable tables is a separate, renderer-side concern.
| Table type | WOFF2 conversion | Renders depends on |
|---|---|---|
glyf/loca (TrueType outlines) | Preserved + transformed for compression | Universal |
CFF /CFF2 (PostScript outlines) | Preserved (OTTO flavor wraps fine) | Universal |
fvar/gvar/STAT (variable) | Preserved; axes work after wrap | Modern browsers (all) |
COLR/CPAL (vector colour) | Preserved | Chrome/Edge/Firefox (COLRv1) |
sbix (Apple bitmap colour) | Preserved | Safari / Apple platforms |
CBDT/CBLC (Google bitmap colour) | Preserved | Android / Chrome |
SVG (SVG-in-OpenType) | Preserved | Limited browser support |
Tier limits that gate conversion
Only file size is enforced for this tool in the web UI. The glyph and batch caps listed in tier config are not applied by the converter itself.
| Tier | File-size cap | Enforced here? |
|---|---|---|
| Free | 5 MB | Yes — blocks before compression |
| Pro | 50 MB | Yes |
| Developer | 1 GB | Yes |
| Glyph count | (tier constant exists) | No — not checked by ttf-to-woff2 |
| Batch / multi-file | (tier constant exists) | No — web UI is single-file only |
Cookbook
Diagnose-and-fix recipes for the cases that don't 'just work'. Each maps a real symptom to the exact remedy.
Rejected .ttc — split, then convert each face
ExampleA TrueType Collection is not a single SFNT, so the converter can't normalise it. Split it on the desktop first; then each face converts normally.
Symptom: upload SerifCollection.ttc → 'Unsupported font format: ttc' # split with fonttools (Python) otc2otf SerifCollection.ttc # → Serif-Regular.otf, Serif-Italic.otf, … # then drop each .ttf/.otf into /font-tools/ttf-to-woff2
Over-limit CJK font — subset to fit
ExampleA 12 MB CJK TTF fails on Free (5 MB) and is huge even on Pro. Subset to the characters you actually use, then convert the much smaller result.
Symptom: NotoSansCJK.ttc/.ttf (≈12 MB) → 'exceeds the free tier per-job limit (5.0 MB)' Step 1: /font-tools/font-subsetter → keep JIS L1 / GB2312 used chars Step 2: /font-tools/ttf-to-woff2 → now well under the cap
Colour font renders monochrome — verify the data is there
ExampleThe WOFF2 keeps the colour tables; the renderer just may not paint them. Confirm with the identifier, and check support per engine.
Convert EmojiBitmap.ttf → EmojiBitmap.woff2 (sbix preserved) Chrome: may show monochrome (no sbix) Safari: renders colour (sbix) # The sbix/COLR/CBDT data is intact — it's a renderer-support gap.
Confirm a 'failed' file is really a font
ExampleWhen conversion errors, the most common root cause is the input isn't what the extension claims. The identifier reads the 4-byte magic.
$ run /font-tools/font-format-identifier on mystery.ttf → format: ttc magic: 0x74746366 description: TrueType Collection # So it's a collection mislabelled .ttf → split it first.
Variable font — convert and keep the axes
ExampleVariable fonts convert losslessly; fvar/gvar ride along in the Brotli stream. To ship a single static weight instead, instance first.
Convert Inter.var.ttf → Inter.var.woff2 (axes preserved, smaller) # Want one static weight? Freeze first, then convert: /font-tools/variable-font-freezer → wght=400 instance → /font-tools/ttf-to-woff2
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.
TrueType Collection (.ttc) upload
Rejected — unsupportedA .ttc bundles several faces and isn't a single SFNT, so fileToSfntBuffer throws Unsupported font format: ttc. A .ttc renamed to .ttf is caught the same way because the ttcf magic is detected regardless of extension. Split it into individual TTF/OTF faces (otc2otf, fonttools) and convert each separately.
File exceeds the tier size limit
Blocked — 5/50/1000 MBThe size check runs before compression. On Free, anything over 5 MB fails with exceeds the free tier per-job limit (5.0 MB); Pro is 50 MB, Developer 1 GB. This is a hard gate, not a warning. Subset the font first to drop under the cap, or upgrade.
CJK output is barely smaller than the source
By designWOFF2 typically saves ~25–35% on CJK vs ~60% on Latin because the glyph outlines are already information-dense. The conversion succeeds and is smaller — there's no error. The real size lever for CJK is subsetting to the characters in use, then converting.
Re-converting an existing WOFF2 shows ~0% saving
ExpectedA .woff2 input is decompressed to SFNT and re-compressed, so the output is roughly the same size and the Saved: N% chip may not appear (it only shows on a positive reduction). This is a faithful round-trip, not a failure.
Colour font converts but renders monochrome
Preserved — renderer gapWOFF2 carries COLR/CPAL, sbix, and CBDT/CBLC through unchanged. If the glyphs show in black-and-white, the browser/OS doesn't support that colour table format (Safari renders sbix; Chrome renders COLRv1; support varies). The colour data is intact in the file.
Corrupt or truncated SFNT
Error — invalid inputIf the bytes don't form a valid SFNT table directory, the wawoff2 WASM encoder returns a falsy result and the run surfaces an error in the red panel. Re-export the font from its design app or font editor; verify the bytes with the Font Format Identifier.
Embedded-bitmap (EBDT/EBLC) TrueType font
PreservedEmbedded monochrome bitmap strikes (EBDT/EBLC) are SFNT tables like any other and are carried through the WOFF2 wrap. They don't block conversion. As with colour bitmaps, whether they're used at render time depends on the platform's preference for outlines vs strikes.
Variable font with many axes
Supportedfvar/gvar/avar/HVAR/MVAR/STAT are all preserved through the wrap; the glyf/loca transform applies to outlines and the variation tables ride along in the Brotli stream. Axes still work after conversion. To collapse to one static instance, use the Variable Font Freezer first.
Very large font briefly freezes the tab
Expected — main-thread WASMCompression runs on the page's main thread (not a Web Worker), so a large CJK font can make the tab unresponsive for a moment while Brotli runs. The conversion still completes correctly. If responsiveness matters at scale, run the same engine in a build script instead.
Output is a few percent bigger than woff2_compress CLI
Expected — default Brotli settingsThe tool uses wawoff2 at its default Brotli quality. The CLI woff2_compress uses the same algorithm; small size deltas come from quality/window tuning, not from a different or worse format. Both are standards-compliant WOFF2.
Frequently asked questions
Why does my .ttc file get rejected?
A TrueType Collection bundles multiple font faces and isn't a single SFNT, so the converter can't normalise it — it throws Unsupported font format: ttc. Renaming it to .ttf doesn't help because the ttcf magic is detected from the bytes. Split it into individual TTF/OTF faces (otc2otf or fonttools) and convert each one.
Why is my converted CJK font still huge?
WOFF2 saves ~25–35% on CJK fonts (versus ~60% on Latin) because most of the file is dense glyph data that's already efficiently packed. To dramatically shrink a CJK font, subset it to the characters your content uses with the Font Subsetter, then convert the subset.
What's the maximum file size I can convert?
5 MB on Free, 50 MB on Pro, 1 GB on Developer. The check runs before compression, so an over-limit file is blocked immediately with a clear message. File size is the only limit this tool enforces — there's no glyph-count gate on the conversion itself.
Can I convert variable fonts?
Yes. fvar/gvar/STAT and the other variation tables survive the WOFF2 wrap exactly, so font-variation-settings still works after conversion. Variable fonts compress well to WOFF2. If you'd rather ship a single static weight, freeze it first with the Variable Font Freezer.
What if my conversion fails silently?
It doesn't fail silently — a genuine error appears in the red error panel with the message (e.g. Unsupported font format: ttc, an over-limit message, or a compression failure on a corrupt SFNT). The most common root cause is a .ttc mislabelled .ttf; run the Font Format Identifier to confirm the real format.
Does WOFF2 preserve Apple's sbix colour table?
Yes. WOFF2 is table-agnostic — it preserves any SFNT table, including sbix (Apple bitmap colour), CBDT/CBLC (Google bitmap colour), COLR/CPAL (vector colour), and SVG . Whether a given browser renders those tables in colour is a separate, renderer-side question, but the data is preserved in the WOFF2.
Why does my colour emoji font render in black and white?
Because the renderer doesn't support that colour-table format, not because conversion lost data. Safari renders sbix; Chrome/Edge/Firefox render COLRv1; bitmap and SVG support vary. The colour tables are intact in the converted WOFF2 — test in a browser that supports the table type your font uses.
Can the converter handle embedded bitmaps (EBDT/EBLC)?
Yes — embedded bitmap strikes are ordinary SFNT tables and ride through the WOFF2 wrap without blocking conversion. Whether the platform uses the bitmap strikes or the outlines at render time is up to the OS/renderer, not the converter.
Why is the output sometimes the same size as the input?
Because the input was already a WOFF2. The tool decompresses it to SFNT and re-compresses, which yields roughly the same size — a faithful round-trip. The Saved: N% chip only shows when there's a positive reduction, so you may not see it for a WOFF2-in case.
Is my output worse than the woff2_compress command line?
No — it's the same WOFF2 format from the same algorithm. JAD's converter uses wawoff2 at default Brotli settings; the CLI uses the same Brotli internally. Differences of a few percent come from quality/window tuning, not from a degraded or non-standard output. Both files are valid WOFF2.
Does conversion ever change my glyphs or metrics?
No. WOFF2 is a lossless wrapper — outlines, metrics, kerning, ligatures, and hinting are byte-equivalent after the browser decompresses to an in-memory SFNT. Only the optional metadata/private blocks can be dropped by a wrapper; the rendered result is identical.
Where do I learn the byte-level details of the WOFF2 format?
See WOFF & WOFF2 internals explained for the header layouts, magic numbers, and the glyf/loca transform. This page is the practical troubleshooting reference; that one is the spec walk-through.
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.