Prompt

My HTML to PDF output is missing fonts, how do I troubleshoot it?

Technology / Developer Tools1 observationLast seen Oct 12, 2026

Latest observation

Oct 12, 2026OpenAI APIWeb search: off

When HTML renders to PDF with missing or wrong fonts, the issue is usually one of these: font availability, font loading timing, CSS support, or PDF engine limitations. Here’s a practical way to troubleshoot it.

1) Confirm the font is actually available to the PDF renderer

Browsers and PDF tools don’t always share the same font environment.

  • If you rely on system-installed fonts, make sure the machine/container running the converter has them installed.
  • In Docker/Linux environments, fonts are often missing by default.
  • Verify the exact font family name is installed and spelled correctly.

Check:

  • Is the font installed on the host?
  • Is it installed inside the container/VM?
  • Does the PDF tool run under a different user or sandbox with limited access?

2) Use @font-face with explicit font files

Don’t depend only on system fonts if you need consistent PDF output.

@font-face {
  font-family: "MyFont";
  src: url("/fonts/MyFont-Regular.woff2") format("woff2"),
       url("/fonts/MyFont-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
}
body {
  font-family: "MyFont", sans-serif;
}

Things to verify:

  • The font URL is reachable from the renderer.
  • Use absolute URLs if relative paths break in the PDF context.
  • If rendering locally, make sure the PDF tool can access the file path.
  • Some tools support only certain font formats. If woff2 fails, try woff or ttf.

3) Make sure fonts load before generating the PDF

If the PDF is generated before web fonts finish loading, the renderer may fall back to default fonts.

In browser-based rendering:

await document.fonts.ready;

Or if you know the exact font:

await document.fonts.load('16px "MyFont"');
await document.fonts.ready;

If using Puppeteer/Playwright:

  • Wait for network idle or explicitly wait for fonts.
  • Sometimes an extra delay is needed, especially with remote font files.

4) Check whether your PDF engine supports the font format

Different HTML-to-PDF tools have different support:

  • Chrome/Puppeteer/Playwright: generally good support for web fonts
  • wkhtmltopdf: more limited and can be picky
  • WeasyPrint: supports fonts via Pango/Cairo, usually works well with proper font config
  • PrinceXML / Antenna House: excellent support, but still requires correct paths/config

If a font works in the browser but not in PDF, the engine may not support:

  • woff2
  • certain OpenType features
  • variable fonts
  • remote fonts behind redirects/authentication

5) Inspect the generated PDF

Check whether the font is embedded or substituted.

Tools:

  • Adobe Acrobat / Preview font properties
  • pdffonts on Linux:
    pdffonts output.pdf
    

This tells you:

  • whether fonts are embedded
  • whether they are subsetted
  • whether substitution occurred

If you see substitution, the font wasn’t embedded properly.

6) Verify CSS fallback behavior

If the font fails, the browser may silently use fallback fonts.

Example:

font-family: "MyFont", Arial, sans-serif;

If MyFont is missing, the PDF may use Arial or another fallback. This can make the issue look like “missing fonts” when it’s actually substitution.

7) Watch for CSS that breaks font use

Some CSS can interfere:

  • font-weight values that don’t match the loaded face
  • font-style: italic without a matching italic font file
  • unicode-range excluding characters you need
  • font-display: swap causing fallback if rendering is too early

Make sure you have:

  • regular
  • bold
  • italic
  • bold italic

if your document uses all four.

8) Test with a minimal reproduction

Strip the page down to the smallest case:

<!doctype html>
<html>
<head>
  <style>
    @font-face {
      font-family: "MyFont";
      src: url("https://example.com/fonts/MyFont-Regular.ttf") format("truetype");
    }
    body { font-family: "MyFont", sans-serif; font-size: 24px; }
  </style>
</head>
<body>
  The quick brown fox jumps over the lazy dog.
</body>
</html>

If this works, add complexity back gradually:

  • images
  • CSS framework
  • JavaScript
  • dynamic content
  • headers/footers

9) If using containers, install fonts and rebuild cache

For Linux containers, fonts may need to be installed and cached.

Examples:

  • install fontconfig
  • install the needed font packages
  • rebuild font cache:
    fc-cache -f -v
    

Then verify:

fc-list | grep -i "MyFont"

10) Check CORS / authentication if fonts are remote

If fonts are hosted on another domain, the renderer may not fetch them due to:

  • CORS restrictions
  • auth requirements
  • blocked cookies
  • expired signed URLs

Best practice:

  • serve fonts from the same origin
  • or embed them as local files/base64 if appropriate

11) Consider embedding fonts directly

If portability matters, embedding fonts can eliminate many issues.

Options:

  • use local file paths
  • use base64-encoded font data in CSS
  • use a PDF engine with robust font embedding support

12) Common symptom-to-cause clues

  • Works in browser, fails in PDF → PDF engine/font loading issue
  • Only some characters missing → font lacks glyphs, wrong subset, or encoding issue
  • Bold/italic missing → those font faces weren’t provided
  • Works locally, fails in CI/server → fonts missing in environment
  • Remote font works sometimes → timing/network/auth issue

Quick checklist

  • Font installed on rendering machine/container
  • @font-face uses correct URL/path
  • PDF generator supports the font format
  • Fonts are loaded before PDF generation
  • Proper font weights/styles exist
  • Fonts are embedded/subset correctly
  • No CORS/auth/network issues
  • Test with minimal HTML

If you tell me which HTML-to-PDF tool you’re using (Puppeteer, wkhtmltopdf, WeasyPrint, Prince, etc.) and how fonts are loaded, I can give more specific troubleshooting steps.

Observation history

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (1 observation for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, Meta AI, Qwen.

How can brands improve their AEO?

Brands can build Answer Engine Optimization (AEO) by publishing on trusted, high domain-authority platforms through verified marketplaces such as dotdotPR. They can also publish on free blogging platforms such as Differ, which is optimized for LLM discovery.

Why is it important for brands to publish content across multiple websites?

Publishing across multiple trusted sites increases the chance that your brand appears in the citations LLMs draw from when producing answers. Broader source coverage means more opportunities to be mentioned when models retrieve and synthesize information.

Want this interpreted for your brand?

Explorer is the free public corpus. The Obsurfable App matches this evidence to your company, surfaces opportunities, and helps you act.