Configure PDF fonts
Choose font sources, control substitutions, and inspect font evidence for PDF conversion.
Fonts set line breaks, page counts, and the glyphs embedded in the PDF. By default, the converter uses installed fonts and packaged substitutes.
Supply font files
The following example validates a licensed font with createFontSource and converts with system fonts off:
import { readFile, writeFile } from 'node:fs/promises';
import { createFontSource, exportPdf } from '@docx-editor.dev/docx-to-pdf';
const fontBytes = new Uint8Array(await readFile('ApplicationSans.ttf'));
const admitted = createFontSource(fontBytes, {
family: 'Application Sans',
weight: 400,
style: 'normal',
});
if ('failure' in admitted) {
throw new Error(admitted.failure.diagnostic ?? admitted.failure.reason);
}
const result = await exportPdf(await readFile('document.docx'), {
useSystemFonts: false,
fonts: { sources: [admitted.source] },
});
await writeFile('document.pdf', result.bytes);Register bold, italic, and bold-italic files separately, with weights 400 and 700 and styles 'normal' and 'italic'. Use defineFontResolver to read the families that the document requests. The PdfFontOrigin and PdfFontsSource types describe font inputs.
Choose source precedence
Earlier sources win. Each option takes one source or an ordered array.
| Order | Source | Notes |
|---|---|---|
| 1 | fonts | Overrides installed and packaged faces. |
| 2 | Installed fonts | On by default. Set useSystemFonts: false for the same output on every host. Skips installed copies of faces that the packages supply. |
| 3 | Packaged substitutes | Metric substitutes for common DOCX fonts. |
| 4 | fallbackFonts | Faces absent from earlier sources. |
| 5 | Supplemental packaged faces | Includes @docx-editor.dev/fonts-cjk when installed. |
| 6 | Document-embedded fonts | Faces stored in the DOCX. |
| 7 | lastResortFonts | Final caller fallback, after embedded fonts. |
| 8 | Generic substitutes | Unresolved families. Reports font-substitution. |
Default sources need no network access. A resolver that you supply can make network requests.
Add Chinese, Japanese, and Korean text
The converter has no CJK font. If the server has no suitable font, install the optional peer:
npm install @docx-editor.dev/fonts-cjk- The converter finds the package at runtime, with no configuration. It works with
useSystemFonts: false. - Add it to production dependencies, keep it external to the server bundle, and keep its
dist/andassets/directories in place. - It supplies Noto Sans CJK JP Regular (weight
400, style'normal'), the Japanese regional face. Supply your own font throughfontsfor other regional faces or designed styles. - If you set
glyphFallbacks, your list replaces the default. Add{ family: NOTO_SANS_CJK_JP_FAMILY, weight: 400, style: 'normal' }to keep this fallback. - Without coverage, export reports
missing-glyphand strict export fails.
The package runs on Node.js ^20.16.0 || >=22.3.0, reads its font from disk, and has these exports:
| Export | Value |
|---|---|
NOTO_SANS_CJK_JP_FAMILY | 'Noto Sans CJK JP' |
NOTO_SANS_CJK_JP_URL | file: URL for assets/NotoSansCJKjp-Regular.otf |
For browser loading, see Packaged browser fallback.
Choose a font policy
Font policy and PDF fidelity policy are separate checks:
| Policy | Rejects | Allows |
|---|---|---|
fontPolicy: 'best-effort' (default) | Nothing for fonts | Recovered source failures and incomplete coverage |
fontPolicy: 'strict' | Required source failures and incomplete family coverage, even when a later source supplies the font | Substitutes that complete coverage; a shaping-limit rejection of an optional glyph fallback alone |
fidelityPolicy: 'strict' (default) | Generic substitutions, missing glyphs, and unsupported or approximate output | Packaged metric substitutes |
fidelityPolicy: 'best-effort' | Nothing; reports diagnostics | All available output |
An optional glyph fallback is a glyphFallbacks family that the document does not request and that is not the default family. fidelityPolicy: 'strict' does not turn on strict font policy. The following example sets both:
const result = await exportPdf(source, {
fontPolicy: 'strict',
fidelityPolicy: 'strict',
useSystemFonts: false,
onFontResolution(report) {
console.table(report.families);
},
});The callback runs before a strict font refusal. Conversion does not await returned promises.
Read font evidence
| Field or code | Meaning |
|---|---|
fontResolution.families | Coverage and selected faces. Inspect sourceFamily, via, and substitution on each face. |
fontResolution.originFailures | Source failures and their causes. |
fontResolution.droppedEmbeddedFonts | Embedded faces rejected during admission. |
font-origin-failed | Source index, optional name, and guarded cause for one failed source. |
incomplete-font | Partial coverage or substituted variants. Informational when fontPolicy allows recovery. |
Troubleshoot fonts
| Symptom | Action |
|---|---|
| Pages differ across hosts | Set useSystemFonts: false, and use the same font bytes and package versions. |
font-substitution diagnostic | Supply the family through fonts, or review best-effort output. |
| Strict font policy rejects conversion | Read the onFontResolution report for failed sources and missing faces. |
| Missing font assets after bundling | Keep converter packages external and deploy their assets. See Deploy with Next.js. |
missing-glyph diagnostic | Supply a face with the characters. For CJK text, install @docx-editor.dev/fonts-cjk. |
font-origin-failed for the CJK package | Its assets are missing or unreadable. Read originFailures. |
Limits
- Check your font license before embedding. The writer rejects prohibited embedding, prohibited subsetting, variable fonts, and unsupported containers.
- Faces over the shaping byte limit are rejected; later sources can supply the face. The report records the rejection.
- Installed fonts resolve by their internal family name. Machines with different fonts can produce different PDFs.
- The converter builds its installed-font index once per process. It caches up to 128 MiB of font files per process and reloads a file that changes.
- A semicolon stays part of a font family name, so
Georgia;Verdanarequests one family. - Missing bold or italic faces draw with synthesized styles. Supply all four files for designed variants.
- Complete coverage can include substitutes. It does not prove original font selection.
- The CJK package code uses Apache 2.0, and its font uses the SIL Open Font License 1.1. Include its license files when you distribute it. The converter uses the EigenPal Pro License.