---
title: "Chromium PDF — CSS Font-Face Must Be in HTML <head>, Not SVG <defs>"
tags: [chromium, pdf, css, fonts, svg, inkscape, indesign]
created: 2026-05-18
sources: daily/2026-05-18.md
---

# Chromium PDF — CSS Font-Face Must Be in HTML `<head>`, Not SVG `<defs>`

## The Problem

When Chromium renders an SVG to PDF via `page.pdf()` (Playwright) or `--print-to-pdf` (headless), `@font-face` rules placed only inside the SVG's `<defs>` block produce **Type 3 (bitmap) fonts** in the PDF:

```xml
<!-- SVG — @font-face here ONLY → bitmap Type 3 font in PDF -->
<svg xmlns="http://www.w3.org/2000/svg">
  <defs>
    <style>
      @font-face {
        font-family: "CustomFont";
        src: url("CustomFont.woff2") format("woff2");
      }
    </style>
  </defs>
  <text font-family="CustomFont">Hello</text>
</svg>
```

**Symptom:** PDF opens fine in browsers but shows rasterised/pixelated text in InDesign, Acrobat, or print-preflight; font is listed as Type 3 (not TrueType/CIDFont).

## Root Cause

Chromium's PDF renderer uses the HTML document's font-loading pipeline, not the SVG inline stylesheet, to decide which fonts to embed as vector outlines. `@font-face` inside SVG `<defs>` is applied for on-screen rendering but is **not** picked up by the PDF font-embedding step.

## Fix

Inject the same `@font-face` declaration into the **HTML document's `<head>`** before calling `page.pdf()`:

```python
# Playwright example
await page.set_content(f"""
<!DOCTYPE html>
<html>
<head>
  <style>
    @font-face {{
      font-family: "CustomFont";
      src: url("file:///path/to/CustomFont.woff2") format("woff2");
    }}
  </style>
</head>
<body>
  {svg_content}
</body>
</html>
""")
await page.pdf(path="output.pdf", print_background=True)
```

The SVG `<defs>` stylesheet can remain (no need to remove it); the HTML `<head>` declaration is additive and triggers proper CIDFont embedding.

## Verification

Open the resulting PDF in Acrobat → File → Properties → Fonts tab. The font should appear as **Type: TrueType (CID)** or **Type: OpenType (CID)**, not Type 3.

## Scope

Applies to:
- Playwright `page.pdf()`
- Puppeteer `page.pdf()`
- Chrome headless `--print-to-pdf`
- Any Chromium-based PDF renderer

Does **not** apply to wkhtmltopdf (uses QtWebKit) or WeasyPrint (uses Cairo).

## See Also

- `wiki/concepts/chromium-pdf-css-font-injection` — this article
