> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openpdf.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Fonts and images

> Put files under the doc's assets folder, import them, and reference the imported value. Works in preview and export.

Fonts and images follow the same rule: put the file under `docs/<id>/assets/`, import it, and reference the imported value. The import resolves to a URL the renderer knows how to fetch in the preview and read during [export](/export).

## Images

```tsx theme={null}
import logo from './assets/logo.png';

<img src={logo} width={64} height={64} tw="rounded-lg" />
```

* **Always give `width` and `height`** (CSS px). The engine sizes the box from them; an unsized image is a layout surprise.
* Doc-local images live in `docs/<id>/assets/`. Assets shared across docs live in the project root `assets/` and import via `@assets/...`.
* PNG, JPEG, WebP, and GIF decode. `data:` URIs also work, fine for tiny marks, wasteful for anything else.
* Inline `<svg>` elements render as crisp vector paths. Prefer them for rules, checkmarks, and simple charts.
* Keep source images reasonably sized. A logo does not need 2000px, and image bytes embed into every rendered PDF.

## Fonts

The default (no `fonts` entry) is the engine's bundled Geist: full Latin coverage, weights honored, embedded and subset automatically. It stays the right choice unless you have a specific font or brand files.

To use a custom font, register it on `pageOptions.fonts` and reference it by the family name inside the font file:

```tsx theme={null}
import display from './assets/PlayfairDisplay.woff2';

export const pageOptions: PageOptions = {
  size: 'a4',
  margin: 64,
  fonts: [display],
};

<h1 tw="text-[28px] font-bold" style={{ fontFamily: 'Playfair Display' }}>...</h1>
```

Entries in `fonts` can be:

* an asset-import URL (`.woff2`, `.ttf`, `.otf` under the doc's assets),
* an absolute `https:` URL,
* a `{ name?, data, weight?, style? }` byte descriptor.

The engine reads family name, weight, and style from the file when not given.

<Note>
  **A registered font takes priority over the bundled default for unstyled text.** Register only faces you want in the document. If some text must stay on the default face while a custom face is registered, there is no way to name the default, so plan the registration around that.
</Note>

### Glyph coverage

Glyph coverage is a hard constraint: a character no registered font covers **fails the render** with a missing-glyph error. This is by design, an error beats silent tofu. For emoji, CJK, Arabic, or other scripts, register a font that covers them. Otherwise keep to Latin text and ordinary symbols.

Use font files you have rights to rather than hotlinking. For a Google font, download the `.woff2` into the doc's assets.

## Anti-patterns

* `fontFamily` naming a font that was never registered. It silently falls back.
* Registering fonts "just in case". They displace the default for all unstyled text.
* `<img>` without `width` and `height`.
* Multi-megapixel images for a letterhead logo.
* Emoji or non-Latin glyphs without a covering registered font. The render fails, by design.
* Stock-photo filler where type and layout would carry the page.
