Missing Font Issues During File Export
Exporting a page, dashboard, report, contract, or invoice can turn into a frustrating experience. The source file looks fine on screen. The exported PDF, image, or Office document does not. Text may appear as empty rectangles, boxes, or question marks. Some glyphs disappear completely. Numbers and Latin characters might survive while Asian scripts fail. Spacing can shift, line height can change, and print output can become unreadable.
This is not simply a display bug. It usually points to a deeper issue: the export pipeline may not have access to the fonts used by the source, or the target format may not preserve those fonts correctly.
Why Missing Fonts Happen
The root causes are often grouped into a few patterns.
- The runtime lacks the font. Docker, Linux servers, and CI containers frequently ship only basic fonts, without CJK or symbol fonts.
- The font is not embedded. A PDF may record only a font name, then substitute when the reader cannot find it.
- The subset is incomplete. Embedding only selected characters can save space, but new or dynamic content may introduce missing glyphs.
- The font name differs. CSS may reference
Microsoft YaHei, while the actual family on Linux may beNoto Sans CJK SC. - Web fonts have not loaded. Headless browsers may export before font requests finish.
- Cross-origin or licensing limits interfere.
@font-facerules can be blocked by CORS, or licenses may forbid embedding. - Legacy tools struggle. Libraries such as wkhtmltopdf may have limited support for web fonts or variable fonts.
How to Diagnose the Problem
A clear diagnosis saves time. Start by identifying which characters are missing and which fonts the export actually uses.
- Inspect the output font list. Commands such as
pdffonts output.pdfcan show font names and embedding status. Look for whether the embedding flag is enabled. - Check server fonts. On Linux, run a font listing command for the target language. If nothing appears, the required font is likely absent.
- Check browser font readiness. In a page, use
document.fonts.checkanddocument.fonts.readybefore exporting. - Create a minimal reproduction. Use fixed HTML and a fixed font to rule out dynamic content.
- Review logs. Headless browsers, PDF libraries, and conversion tools often warn about font substitution or missing fonts.
Practical Fixes
Install Fonts in the Export Environment
For Linux or Docker setups, install an open-source CJK font such as Noto Sans CJK or Source Han Sans. Then refresh the font cache.
RUN apt-get update && apt-get install -y \
fonts-noto-cjk \
fonts-noto-color-emoji \
&& fc-cache -f -v
After installation, refresh the cache. If using PDF libraries such as PDFKit or ReportLab, register the font file explicitly.
doc.registerFont('cn', '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', 'NotoSansCJKsc-Regular');
doc.font('cn').text('Export content');
Embed Fonts Explicitly
For PDF export, prefer libraries that support embedding. Enable embedding or subsetting. In CSS, use @font-face to declare an accessible font.
@font-face {
font-family: 'ExportFont';
src: url('/fonts/NotoSansSC-Regular.woff2') format('woff2');
font-display: block;
}
body {
font-family: 'ExportFont', 'Noto Sans CJK SC', 'Microsoft YaHei', sans-serif;
}
font-display: block can reduce early fallback behavior in headless browsers, but waiting for document.fonts.ready is more reliable.
Wait for Web Fonts Before Exporting
When using Puppeteer or Playwright, wait for network idle and font readiness before generating the PDF.
await page.setContent(html, { waitUntil: 'networkidle0' });
await page.evaluate(() => document.fonts.ready);
await page.pdf({
path: 'output.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true
});
If fonts load through a URL, ensure the headless browser can access it and that responses allow cross-origin requests.
Handle Subsetting and Fallbacks
Subsetting reduces file size, but it must include every character in the exported content. User input, dynamic numbers, rare glyphs, and emoji require special testing. Keep a fallback stack, such as 'ExportFont', 'Noto Sans CJK SC', sans-serif, so a missing font does not break an entire paragraph.
For LibreOffice headless conversion, configure font substitution or replacement tables to map missing fonts to installed ones.
Best Practices
- Distribute font files with the application and pin exact versions.
- Install CJK fonts in the Docker image and run
fc-cache -f -v. - Wait for
document.fonts.readyand checkdocument.fonts.checkbefore export. - Verify PDF output with
pdffontsand confirm embedding is enabled. - Regression test multilingual, rare-glyph, emoji, and dynamic-field content.
- Respect font licenses and confirm embedding and commercial use are allowed.
- Migrate from older tools when support is limited.
Summary
Missing fonts in exported files come from gaps between the export environment, font loading, and font embedding. The fix usually involves installing fonts, registering them, waiting for load, enabling embedding, and preserving fallbacks. By treating fonts as part of the export dependency and validating them, teams can reduce