Fonts & Colour Emoji
Every PDF makesPDF renders is self-contained: the fonts it uses are embedded and subsetted, so the document looks the same on any machine, in any viewer, years later. This page lists what ships, how characters that a font lacks still render, and how to bring your own fonts.
Fonts that ship
Four base families are embedded in every render, and two fallback sets load on demand only when a document needs them — nothing needs to be installed or fetched by you:
- Inter — the default across markdown and templates. Regular, SemiBold (what font-weight: bold resolves to), Bold and Italic.
- NotoSans — a humanist alternative, selectable with fontFamily: "NotoSans" on /md or font-family in a template.
- Cousine — monospace, used for code blocks and `inline code`.
- NotoSansSymbols2 — a symbol subset loaded on demand (checkboxes, bullets, arrows — the ☑/☐ in task lists, long → arrows).
- Noto Sans SC / TC / JP / KR — Chinese, Japanese and Korean draw with these automatically; only the unicode-range slices covering your document's characters load (see CJK below).
Per-character fallback
A character the current font doesn't have is drawn by the first font that does, character by character and grapheme cluster by grapheme cluster — the run is split silently, keeping size, colour, weight and decoration. Skin-tone modifiers, joiners (ZWJ), variation selectors, flags and keycaps are never split apart: a whole emoji sequence is resolved as one unit.
If no available font covers a character it is drawn blank and the render reports it as a layout warning (glyph-missing, naming the codepoints and a count — never your text). In practice that means an unsupported script, not a stray symbol; see the API reference for where warnings appear.
The coverage sets this is based on are assembled at render time and only the fallbacks a document actually needs are loaded — a page of plain Latin text never pays for the colour emoji font, and a document with no symbols never loads the symbol subset.
CJK
Chinese, Japanese and Korean render with no font upload, on every endpoint — markdown, templates and canvas boards alike. The renderer picks a script and loads only the unicode-range slices covering the document's CJK characters, so a short Japanese paragraph pulls a few hundred KB, and a document with no CJK text loads nothing.
Set the script with the lang option (a BCP 47 tag): ja → Japanese, ko → Korean, zh, zh-Hans, zh-CN, zh-SG → Simplified Chinese, and zh-Hant, zh-TW, zh-HK, zh-MO → Traditional Chinese. lang also writes the PDF's /Lang, which screen readers and accessibility checkers use to pronounce the text correctly. Without a lang, the text itself decides: any kana means Japanese, any Hangul means Korean, and Han-only text is treated as Simplified Chinese — so pass zh-Hant for Traditional Chinese text that carries no kana or Hangul.
A font-family you name still wins for the characters it covers: the built-in CJK fonts are a fallback, not an override. Cyrillic, Greek, Vietnamese, Georgian and Armenian still need a custom font (below); right-to-left and complex-script text is not supported yet.
Colour emoji
Emoji render in full colour, drawn as native vector graphics rather than images. They work anywhere text works: markdown paragraphs and headings, lists and task lists, tables, bold / italic / coloured runs, links, and canvas cards. Multi-codepoint sequences are supported — ZWJ families (👨👩👧👦), skin-tone modifiers (👍🏽), regional-indicator flags (🇦🇺), keycaps (1️⃣), heart-on-fire (❤️🔥) and tag-sequence subdivision flags.
Each emoji is one glyph for layout purposes — the advance width is its em box, so line breaking and alignment treat an emoji like any other character of that size. For copy / paste and screen readers, each emoji extracts as exactly one emoji: the PDF carries an /ActualText marker naming the sequence, so a family of four is not read out as four separate people.
Colour emoji come from the Twemoji Mozilla colour font (the COLRv0 build of mozilla/twemoji-colr), used under its open licences: the build is Apache-2.0, and the artwork is Twemoji by Twitter/X, licensed CC-BY 4.0 — creativecommons.org/licenses/by/4.0. The font is fetched only when a document contains an emoji, and the renderer paints its coloured layers directly into the PDF — no external images, no runtime text-shaping engine.
Custom fonts
Both POST /api/v1/preview and POST /api/v1/render accept a fonts array: each family names TTF or OTF variants by https:// URL, and they are fetched, subsetted and embedded per request. Register a family, then reference it from font-family:
{
"dsl": "const template = doc({}, page(col(s(\"Hello\", { class: \"brand\" }))));\nconst sampleData = {};",
"fonts": [
{
"family": "Brand",
"variants": [
{ "src": "https://cdn.example.com/Brand-Regular.ttf" },
{ "src": "https://cdn.example.com/Brand-Bold.ttf", "weight": "bold" }
]
}
]
}Limits: 4 families per request, 6 variants per family, 5 MB per file. Variant selection follows font-weight / font-style; families that collide with the embedded names above are rejected. Fetched files are cached (keyed by URL hash), so repeat renders don't re-download. Uploaded fonts are the supported path for non-Latin European scripts (Cyrillic, Greek, Vietnamese, Georgian, Armenian) — and the way to render CJK with a branded typeface.
Not yet supported: right-to-left scripts and complex shaping (Arabic, Hebrew, Persian, Indic, Thai, Khmer) — there is no bidirectional reordering or OpenType shaping in the pipeline, so those scripts reorder or join incorrectly regardless of the font.
Licences
The embedded fonts are open source: Inter (SIL Open Font License 1.1), Noto Sans, Noto Sans CJK SC/TC/JP/KR and NotoSansSymbols2 (SIL OFL 1.1), Cousine (Apache License 2.0), and Twemoji Mozilla (Apache-2.0 build; Twemoji artwork CC-BY 4.0, attributed above). The full licence text for the colour emoji font ships beside the font itself in the repository, and attribution for the Twemoji artwork is reproduced here and in the project documentation.
Where to next
Markdown to PDF for the supported syntax, the template DSL for font-family and text styles in templates, or the API reference for the endpoints' authentication, limits and response shapes.