Fonts and Shaping
Standard fonts
The 14 standard PDF fonts need no embedding, but only support Latin text (WinAnsi encoding):
import { StandardFonts } from 'happypdf';
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);For anything else — accents outside Latin-1, or any non-Latin script — embed a font.
Embedding a custom font
const font = await pdfDoc.embedFont(fs.readFileSync('Ubuntu-R.ttf'));Accepts Uint8Array, ArrayBuffer, or a base64 string. TrueType and OpenType (including CFF) are supported.
Subsetting
const font = await pdfDoc.embedFont(bytes, { subset: true });Only the glyphs you actually draw are embedded. For a large font used for a little text this is the difference between a 5 MB file and a 30 KB one.
Subsetting happens when the document is saved, so it accounts for every string you drew with that font.
Complex scripts
Nothing special is required — shaping is always on:
const khmer = await pdfDoc.embedFont(
fs.readFileSync('NotoSansKhmer-Regular.ttf'),
);
page.drawText('សួស្តីពិភពលោក', { font: khmer, x: 50, y: 700, size: 24 });The same applies to Thai, Lao, Arabic, Devanagari, Tamil and other scripts that need reordering or mark attachment. See the live demo.
OpenType features
const font = await pdfDoc.embedFont(bytes, {
features: { liga: false, onum: true },
});Feature tags are passed to HarfBuzz. Set a tag to false to disable a feature that is on by default, or true to enable an optional one.
Variable fonts
variations pins a variable font's axes:
const regular = await pdfDoc.embedFont(bytes, { variations: { wght: 400 } });
const bold = await pdfDoc.embedFont(bytes, { variations: { wght: 700 } });Each combination produces a separately embedded instance, so use only the ones you need.
Axis ranges
Values are clamped to the axis range declared in the font's fvar table. A font whose wght runs 400–700 will treat { wght: 100 } as 400 — the request is silently clamped, not rejected.
Measuring text
font.widthOfTextAtSize('Hello', 24); // shaped advance width
font.heightAtSize(24); // line height
font.sizeAtHeight(30); // size that yields a given heightWidths come from the shaped run, so kerning and ligatures are accounted for.
Custom font names
const font = await pdfDoc.embedFont(bytes, { customName: 'MyFont' });Overrides the name written into the PDF, which some workflows rely on for downstream processing.