Skip to content

Fonts and other scripts

Templates can use Helvetica, Times and Courier (and common names that map to them, such as Arial, Georgia and monospace). They are the 14 standard PDF fonts: text is measured with their exact metrics, so line breaks on screen and in the PDF match, and they need no files.

They cover Latin text. For other scripts, or to use your brand font, register a font.

A font has three jobs, and each has its own place:

  1. Measuring text for line breaks: createFontMeasurer(fonts) gives the layout engine the font’s real metrics.
  2. Drawing it in the PDF: exportPdf(doc, { fonts }) embeds the font (subset).
  3. Showing it on screen: the browser needs the font via @font-face, as for any web font.
import { createFontMeasurer, exportPdf, generateReport } from '@ariana-reports/core';
const fonts = [
{ family: 'Roboto', data: await (await fetch('/fonts/Roboto-Regular.ttf')).arrayBuffer() },
{ family: 'Roboto', bold: true, data: await (await fetch('/fonts/Roboto-Bold.ttf')).arrayBuffer() },
];
const measurer = await createFontMeasurer(fonts);
const doc = generateReport(template, data, { measurer });
const pdf = await exportPdf(doc, { fonts });

Then set "fontFamily": "Roboto" in a style. The families you register fall back to the closest standard font for variants you did not provide.

<ReportViewer template={template} data={data} measurer={measurer} pdf={{ fonts }} />
app.css
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Regular.ttf');
}

The font drop-down lists the standard fonts, every font already used in the template, the families in preview.pdf.fonts, and any extra names you pass with fonts:

<ReportDesigner
template={template}
fonts={['Vazirmatn', 'Roboto']}
preview={{ measurer, pdf: { fonts } }}
/>

Listing a family only makes it selectable. It still needs the @font-face rule to display, and a file in pdf.fonts to be embedded.

Use .ttf, .otf or .woff files. WOFF2 files are rejected with an error, because they cannot be embedded.

Each character is drawn with the first font that has it: the families in fontFamily, then the standard font, then every other registered font. So with Roboto and Vazirmatn registered, a Roboto component that contains Persian words draws them with Vazirmatn. The measurer makes the same choice, so line breaks still match.

A family can be split over several files, as Fontsource and Google Fonts ship them (one file per script). Register each file under the same family:

const fonts = [
{ family: 'Vazirmatn', data: arabicFile },
{ family: 'Vazirmatn', data: latinFile },
];

When no registered font has a character, the PDF shows ? (standard fonts) or an empty box (registered fonts), and exportPdf reports the characters through onWarning (or the console when there is no handler).

Persian, Arabic and other right-to-left text

Section titled “Persian, Arabic and other right-to-left text”

The standard PDF fonts have no Persian or Arabic letters, so a PDF with such text needs a font file. Listing a name in the designer’s fonts prop is not enough: register the file in pdf.fonts (and in preview.pdf.fonts for the designer).

const fonts = [
{ family: 'Vazirmatn', data: await (await fetch('/fonts/Vazirmatn-Regular.ttf')).arrayBuffer() },
{ family: 'Vazirmatn', bold: true, data: await (await fetch('/fonts/Vazirmatn-Bold.ttf')).arrayBuffer() },
];

The embedded font joins the letters (contextual shaping), and every line is reordered with the Unicode bidirectional algorithm, so numbers and Latin words inside Persian text keep their order. The base direction comes from the direction style; see Languages.