Custom Fonts
The fonts object registers custom font families from uploaded font files. Once registered, a font family can be referenced by name in any style definition using the font property.
Standard fonts (Helvetica, Times-Roman, Courier) are always available without registration.
Registering a font family
Declare fonts at the document level with a family name and paths to each variant:
{
"document": {
"fonts": {
"Roboto": {
"regular": "fonts/Roboto-Regular.ttf",
"bold": "fonts/Roboto-Bold.ttf",
"italic": "fonts/Roboto-Italic.ttf",
"boldItalic": "fonts/Roboto-BoldItalic.ttf"
}
},
"styles": {
"body": { "font": "Roboto", "fontSize": 10 }
},
"content": [
{ "p": "This text renders in Roboto.", "style": "body" }
]
}
}
Font files are resolved through the asset storage system — the same way images are loaded. Upload fonts via the tenant assets API before referencing them.
Font family properties
| Property | Type | Required | Description |
|---|---|---|---|
regular | string | Yes | Path to the regular weight font file |
bold | string | No | Path to the bold weight font file |
italic | string | No | Path to the italic font file |
boldItalic | string | No | Path to the bold-italic font file |
Only regular is required. Missing variants fall back to the regular file — the text renders but without the visual weight or slant.
Uploading font files
Upload font files to your tenant's asset storage:
POST /api/v1/tenants/{tenantId}/assets/fonts
Content-Type: multipart/form-data
file: Roboto-Regular.ttf
Supported formats: .ttf (TrueType) and .otf (OpenType). Fonts are embedded in the PDF with full Unicode support.
Using in styles
Reference the registered family name in any style's font property:
{
"fonts": {
"NotoSerif": {
"regular": "fonts/NotoSerif-Regular.ttf",
"bold": "fonts/NotoSerif-Bold.ttf",
"italic": "fonts/NotoSerif-Italic.ttf",
"boldItalic": "fonts/NotoSerif-BoldItalic.ttf"
}
},
"styles": {
"title": { "font": "NotoSerif", "fontSize": 22, "bold": true },
"body": { "font": "NotoSerif", "fontSize": 11 },
"bodyItalic": { "font": "NotoSerif", "fontSize": 11, "italic": true },
"caption": { "font": "NotoSerif", "fontSize": 8, "italic": true }
}
}
The bold, italic, and boldItalic style flags select the corresponding variant from the registered family.
Inline formatting
Inline tags ([b], [i], [bi]) use the correct font variant automatically:
{ "p": "Regular text with [b]bold emphasis[/b] and [i]italic notes[/i].", "style": "body" }
If the style's font is NotoSerif, [b] renders with NotoSerif-Bold.ttf, [i] with NotoSerif-Italic.ttf, and so on.
Mixing custom and standard fonts
Custom and standard fonts coexist in the same document. Styles without a font property use the default Helvetica:
{
"fonts": {
"NotoSerif": { "regular": "fonts/NotoSerif-Regular.ttf" }
},
"styles": {
"body": { "font": "NotoSerif", "fontSize": 11 },
"footnote": { "fontSize": 8, "fontColor": "#999999" }
},
"content": [
{ "p": "Serif body text.", "style": "body" },
{ "p": "Sans-serif footnote in default Helvetica.", "style": "footnote" }
]
}
Partial families
If you only have the regular weight, register just that file:
{
"fonts": {
"BrandFont": {
"regular": "fonts/BrandFont-Regular.ttf"
}
}
}
Bold, italic, and boldItalic all fall back to the regular file. The text renders but without visual weight or slant differences.
Full example
{
"document": {
"metadata": { "title": "Custom Font Report" },
"fonts": {
"NotoSerif": {
"regular": "fonts/NotoSerif-Regular.ttf",
"bold": "fonts/NotoSerif-Bold.ttf",
"italic": "fonts/NotoSerif-Italic.ttf",
"boldItalic": "fonts/NotoSerif-BoldItalic.ttf"
}
},
"styles": {
"title": { "font": "NotoSerif", "fontSize": 20, "bold": true },
"body": { "font": "NotoSerif", "fontSize": 11 },
"caption": { "font": "NotoSerif", "fontSize": 8, "italic": true, "fontColor": "#888888" }
},
"content": [
{ "h1": "Quarterly Report" },
{ "p": "This report uses Noto Serif throughout for a traditional, readable appearance.", "style": "body" },
{ "p": "Key metrics are highlighted with [b]bold[/b] and supplementary notes in [i]italic[/i].", "style": "body" },
{
"table": {
"widths": [3, 1, 1],
"rows": [
[{ "p": "Department", "style": "title" }, { "p": "Q1", "style": "title" }, { "p": "Q2", "style": "title" }],
[{ "p": "Engineering", "style": "body" }, { "p": "$1.2M", "style": "body" }, { "p": "$1.4M", "style": "body" }],
[{ "p": "Marketing", "style": "body" }, { "p": "$800K", "style": "body" }, { "p": "$950K", "style": "body" }]
]
}
},
{ "p": "Source: internal finance systems. Figures are unaudited.", "style": "caption" }
]
}
}
Notes
- Font files are embedded in the generated PDF. Recipients do not need the fonts installed.
- Fonts are loaded once per document generation and cached for the duration of the render.
- If a font file is missing or unreadable, the family is skipped and styles referencing it fall back to Helvetica.
- Font family names are case-insensitive:
"NotoSerif"and"notoserif"resolve to the same family.