Skip to main content

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

PropertyTypeRequiredDescription
regularstringYesPath to the regular weight font file
boldstringNoPath to the bold weight font file
italicstringNoPath to the italic font file
boldItalicstringNoPath 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.