How to use svg fonts in pdf generation pipelines
- Step 1Check what your PDF engine actually wants — Most modern engines embed TTF/OTF directly via `@font-face { src: url(font.ttf) }` — in that case you do **not** need an SVG Font at all; just point the CSS at the TTF/OTF or WOFF2. Only convert to SVG Font if a specific legacy renderer in your stack documents that it consumes the SVG Fonts format.
- Step 2Pick the source font and subset it — Identify the exact characters your documents render — if they're templated invoices or reports, the charset is small and stable. Run the [Font Subsetter](/font-tools/font-subsetter) to keep only those glyphs. This keeps you well under the 5,000-glyph cap and shrinks the otherwise-bulky SVG output.
- Step 3Convert to SVG Font — Drop the subset TTF onto [Font-to-SVG-Font](/font-tools/font-to-svg-font) and Process. The result panel shows Glyphs exported, UPM, and Family. Confirm Glyphs exported matches your subset character count — a shortfall means some glyphs lacked codepoints.
- Step 4Wire the SVG Font into your pipeline — Place the `.svg` where your engine expects it and reference it however that engine documents (for native TTF engines, reference the TTF/OTF instead). Commit the converted asset to your repo or generate it in CI so the build is reproducible.
- Step 5Render a proof PDF and inspect the type — Generate a test PDF that exercises every glyph — uppercase, lowercase, digits, punctuation, any accented characters. Look specifically for missing glyphs (boxes), wrong spacing (the no-kerning effect), and broken ligatures. Fix the source/subset, not the SVG, then re-convert.
- Step 6Decide if SVG Font is even the right call — If your engine embeds TTF/OTF natively, skip the SVG Font entirely — native embedding preserves kerning and features that the SVG conversion drops. Use [Font Format Identifier](/font-tools/font-format-identifier) to confirm what you're holding, and [Font Metrics Analyzer](/font-tools/font-metrics-analyzer) to verify vertical metrics before committing to a format.
PDF engines and their preferred font input
What each common HTML/CSS-to-PDF engine embeds. SVG Font support is rare and engine-specific; treat "native TTF/OTF" as the default path.
| Engine | Native font embedding | Needs an SVG Font? |
|---|---|---|
| PrinceXML | TTF, OTF, WOFF, WOFF2 via @font-face | No — feed it TTF/OTF directly |
| WeasyPrint | TTF, OTF, WOFF, WOFF2 | No — native embedding, keeps features |
| wkhtmltopdf | System-installed fonts / TTF | No — install the TTF on the build box |
| Puppeteer / headless Chrome | WOFF2/WOFF/TTF via CSS | No — Chrome dropped SVG Fonts in 2015 |
| Legacy / bespoke vector-to-PDF tools | Varies | Sometimes — only if the tool documents SVG Fonts |
What the SVG Font carries into the PDF
The conversion is outline-only. Anything a PDF needs beyond outlines and advances must come from native embedding or be baked in upstream.
| Typographic feature | In the SVG Font? | Consequence in the PDF |
|---|---|---|
| Glyph shapes | Yes | Letters render correctly |
| Advance widths | Yes (per-glyph horiz-adv-x) | Default spacing is correct |
| Kerning pairs | No | Pairs like 'AV', 'To' look loose |
| Ligatures (fi, fl) | No | Rendered as separate glyphs |
| Variable axes | No | Only the default instance ships |
| Non-codepointed glyphs | No (skipped) | Missing from the document |
Pre-conversion checklist for a clean PDF
Run these before converting, in this order, to avoid the most common PDF font defects.
| Step | Why | Tool |
|---|---|---|
| Subset to your document charset | Stay under 5,000 glyphs; shrink XML | Font Subsetter |
| Confirm every needed glyph has a codepoint | Non-codepointed glyphs are skipped | Glyph Inspector |
| Verify vertical metrics | SVG ascent/descent fall back if absent | Font Metrics Analyzer |
| Confirm the input format | Don't feed a TTC or unknown file | Font Format Identifier |
Cookbook
Pipeline recipes for the PDF case. Most teams discover their engine wants TTF natively — recipe one covers that; the rest are for the genuine SVG-Font consumers.
The common case: your engine wants TTF, not SVG Font
ExamplePrinceXML, WeasyPrint, and headless Chrome all embed TTF/OTF natively and keep kerning and ligatures. If that's your engine, skip the SVG Font conversion entirely — it would only lose features.
/* report.css — native embedding (PrinceXML / WeasyPrint) */
@font-face {
font-family: "Report";
src: url(fonts/Report-Regular.ttf) format("truetype");
}
body { font-family: "Report", sans-serif; }
# No SVG Font needed — features (kern, liga) survive.Producing an SVG Font for a legacy consumer
ExampleOnly when a downstream tool documents SVG Fonts support: subset to the document charset, convert, and commit the result. Keeps the XML small and the output complete.
1. Report-Regular.ttf (full font, 900 glyphs) 2. Font Subsetter → keep document charset (A–Z a–z 0–9 . , - / $) → Report-Regular.subset.ttf (~80 glyphs) 3. Font-to-SVG-Font → Report-Regular.svg → Glyphs exported: 80 | UPM: 1000 | Family: Report 4. Commit Report-Regular.svg as a build asset.
Diagnosing boxes in the output PDF
ExampleBoxes (tofu) mean glyphs the document needs weren't in the SVG Font. Usually the source character lacked a codepoint, or the glyph index sat beyond the 5,000 cap. Check the Glyphs-exported count against your charset.
Symptom: '€' and 'fi' render as boxes in the PDF. Cause: '€' (U+20AC) → present, fine. 'fi' → a LIGATURE glyph with no codepoint → skipped on export. Fix: don't rely on the ligature glyph; the SVG Font has separate 'f' and 'i'. Let the document use f + i, or embed the TTF natively if true ligatures are required.
Loose spacing because kerning was dropped
ExampleThe SVG Font carries advance widths but no kerning pairs. Headlines and tabular figures look acceptable; large display type shows the gaps. If kerning matters, embed the TTF natively instead.
Headline 'WAVE' in the PDF: Native TTF embed → W A V E with kern adjustments (tight) SVG Font embed → W A V E at raw advances (loose W/A, A/V) Decision: for display type, prefer native TTF/OTF embedding in engines that support it; reserve SVG Font for plain body or for tools that accept nothing else.
Keeping the build reproducible in CI
ExampleGenerate or check in the SVG Font deterministically so the same source font always yields the same bytes. The browser converter is for one-offs; for CI, see the dedicated automation guide.
# CI font step (concept) # 1. fonts are version-pinned in the repo # 2. subset is deterministic (fixed charset) # 3. SVG Font committed as an artifact, not regenerated per build # # For a Node build-step that calls the JAD logic, see: # /font-tools/guides/automate-svg-font-generation-pdf-pipeline
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.
Your PDF engine doesn't actually consume SVG Fonts
Wrong formatThe most common mistake: converting to SVG Font for PrinceXML, WeasyPrint, or headless Chrome, all of which embed TTF/OTF natively and dropped SVG Fonts long ago. Feeding them an SVG Font either fails or loses features for nothing. Confirm your engine's documented font path before converting; for most, point CSS at the TTF/OTF directly.
Ligatures render as separate letters
Not exportedSVG Font conversion exports glyph outlines but not GSUB, so 'fi'/'fl' ligatures don't substitute. Worse, standalone ligature glyphs lack codepoints and are skipped entirely. For documents where ligatures matter (fine typography, certain scripts), embed the TTF/OTF natively in an engine that supports OpenType features.
Headline kerning looks loose in the PDF
Not exportedNo <hkern> is emitted, so pairs the font would tighten (AV, To, WA) render at raw advances. Body copy usually tolerates this; large display text does not. If kerning is required, use native TTF embedding; audit which pairs matter with the Kerning Pair Auditor.
A needed character is missing (tofu boxes)
SkippedAny glyph the document needs but the SVG Font lacks renders as a box. The two causes: the source glyph had no Unicode codepoint (so it was skipped), or its index was beyond the 5,000-glyph cap. Verify coverage with the Character Coverage Map and subset deliberately.
Vertical metrics look off in the document
Preserved (fallback)If the source font omits ascender/descender, the converter falls back to UPM×0.8 / UPM×-0.2. A PDF engine that trusts those values may set line spacing slightly differently than the font designer intended. Check real metrics with the Font Metrics Analyzer before relying on them.
SVG Font asset is huge in your repo
By designUncompressed XML makes SVG Fonts several times larger than the source TTF/WOFF2. A full font can produce hundreds of KB of XML. Subset to the document charset first — an invoice template often needs fewer than 100 glyphs, which keeps the committed artifact tiny.
Build font file exceeds the tier size limit
413 / rejectedThe browser converter rejects files over the tier ceiling (5 MB free / 50 MB Pro / 1 GB Developer). Large source fonts should be subset before upload, or converted on a paid tier. For unattended CI, run the conversion logic in Node rather than the browser UI (see the automation guide).
Variable font shipped as a single weight
Default instance onlyThe converter walks glyph outlines at the font's default-axis master values; it does not flatten a chosen weight/width. A variable font becomes one static SVG Font at its defaults. If you need a specific instance, freeze it first with the Variable Font Freezer (note its own gvar caveat) or instance with fontTools on the desktop.
Frequently asked questions
Does my PDF engine even need an SVG Font?
Usually no. PrinceXML, WeasyPrint, wkhtmltopdf, and headless Chrome all embed TTF/OTF (and WOFF/WOFF2) natively via @font-face, and native embedding preserves kerning and ligatures. Convert to SVG Font only if a specific legacy renderer in your stack documents that it reads the SVG Fonts format.
Will kerning survive into the PDF?
Not via the SVG Font. The converter emits advance widths but no <hkern> pairs, so kerning is lost. Body text usually looks fine; display type shows loose pairs. If kerning is essential, embed the original TTF/OTF natively in an engine that supports OpenType layout.
Why do some characters show as boxes?
The glyph wasn't in the SVG Font. Either the source glyph had no Unicode codepoint (the converter skips those) or its index was past the 5,000-glyph cap. Subset to your document charset first and confirm coverage with the Character Coverage Map so every needed glyph is codepointed and within range.
What's the maximum number of glyphs I can include?
5,000. The converter iterates min(glyphCount, 5000) glyph indices. For PDF templates this is rarely a constraint because your document charset is small — subset to it and you'll typically export well under 100 glyphs.
Can I convert a WOFF2 directly, or must I un-wrap it first?
Convert it directly. The tool Brotli-decompresses WOFF2 (and zlib-decompresses WOFF) to a flat sfnt in the browser before walking the glyphs. You don't need to convert back to TTF first; the SVG output is identical either way.
Is this safe for licensed brand fonts?
Yes on the free tier — conversion runs entirely client-side with opentype.js, so the font binary never reaches a server (the panel shows a 0 bytes uploaded badge). Paid tiers can optionally route to the local @jadapps/runner, which is still on your own machine. Respect your font license's embedding terms either way.
How do I make the conversion part of CI?
The browser tool is for one-offs. For an automated build step, port the same opentype.js glyph-walk logic into a Node script, or call the JAD runner's HTTP API. The dedicated guide at /font-tools/guides/automate-svg-font-generation-pdf-pipeline walks through it. Pin font versions and fix the subset charset for reproducible bytes.
Should I ship a variable font through this for PDF?
Only if you want the default instance. The converter walks outlines at the default-axis master values and ships one static SVG Font; it does not bake a chosen weight. Freeze the instance you want first with the Variable Font Freezer (mind its gvar caveat) or instance with fontTools, then convert.
My output PDF text is too loose everywhere — what happened?
Most likely the no-kerning effect plus default advances. The SVG Font has correct advance widths but no kerning, so everything sits at raw spacing. For tighter, designer-intended spacing, embed the TTF/OTF natively in your engine instead of converting to SVG Font.
Can I use this with Puppeteer / headless Chrome PDFs?
You can, but you shouldn't — Chrome removed SVG Fonts in 2015 and won't render them. For Puppeteer-based PDF generation, serve WOFF2/WOFF/TTF via CSS @font-face. Convert to WOFF2 instead of SVG Font for any Chrome-driven pipeline.
How big will the SVG Font asset be?
Several times the size of the source because SVG Fonts are uncompressed XML. A full font can be hundreds of KB. Subsetting to a document charset (often under 100 glyphs) keeps the committed artifact small — usually a few KB to tens of KB.
What if the font has no embedded family name?
The converter falls back to the file's stem name for both <font id> and font-family. The SVG Font is still valid; the family just reflects your filename. Rename the file before converting if you want a specific family name in the output, or edit the attribute afterwards.
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.