How to configure custom fonts in Document Engine
PDF files should render consistently across different PDF viewers. This consistency is possible because a PDF file can embed the fonts required for rendering.
However, in some cases — due to file size or other considerations — PDFs don’t embed fonts. When this happens, the PDF viewer relies on system fonts, which may cause rendering issues if the required fonts are unavailable.
Embedding fonts in PDFs is the best way to ensure accurate rendering, but this isn’t always possible, especially when working with third-party PDFs. Custom font path support addresses this issue.
Available fonts
Refer to the following APIs for a list of available fonts and configured substitutions:
If you mount custom fonts and configure substitutions, you can use the same API to verify your setup.
To view a list of preloaded fonts when using Nutrient Web SDK with Document Engine, refer to the PDF viewer supported fonts guide.
Mounting custom fonts
To add custom fonts to Document Engine, mount them in the /custom-fonts
directory.
If you deploy using Helm, specify additional mounts using the following values:
extraVolumes: - name: my-custom-fonts <volume specification> extraVolumeMounts: - name: my-custom-fonts mountPath: /custom-fonts
To provide Document Engine with access to the font files, you can use an init container and download them to an emptyDir
volume. Alternatively, use a dedicated image with built-in fonts or another storage option, such as a network volume.
The following example uses an init container:
extraVolumes: - name: my-custom-fonts emptyDir: {} extraVolumeMounts: - name: my-custom-fonts mountPath: /custom-fonts initContainers: - name: my-font-downloader image: curlimages/curl:latest args: - "-o" - "/tmp/data/wonderful.ttf" - "https://my.site.local/path/to/wonderful.ttf" volumeMounts: - name: my-custom-fonts mountPath: /tmp/data
With Docker Compose
If you use Docker Compose, expose a directory of fonts from the host machine to the container by adding the following to your docker-compose.yml
file:
document-engine:
volumes:
- /font-directory-path-on-the-host:/custom-fonts
Caching considerations
After adding fonts, you may still notice PDFs rendered with incorrect fonts in the web viewer. Multiple layers of caching can cause this issue, displaying previously rendered pages. Follow the steps below to resolve it:
-
Clear the browser cache — Removes cached rendering artifacts from the browser.
-
Restart Document Engine — Clears the in-memory cache for rendered pages.
-
If using Redis — Delete keys with the
PSPDFKit-TileCache-
andPSPDFKit-PageCache-
prefixes to remove cached renderings. Be aware that this can impact performance in high-volume deployments, as previously cached pages will need to be rerendered. Consider applying Redis eviction policies to remove cache entries gradually.
The font directory can be any location accessible to your app. All .ttf
, .ttc
, and .otf
files in this directory will be added to the Nutrient font list.
Microsoft core fonts
Microsoft core fonts are widely used on the web and in PDFs. Adding them as custom fonts improves document conversion and rendering accuracy. Nutrient doesn’t include these fonts because Microsoft no longer provides them directly, and redistribution is prohibited by license. To use these fonts, download them from SourceForge and add them as custom fonts.
Using emojis
To display emojis, import the Windows-compatible Noto Color Emoji font. Currently, this is the only supported font for emojis.
Font substitutions
Document Engine may not have access to required fonts for conversions or rendering annotations if they’re missing from the default container fonts or the /custom-fonts
directory.
To specify alternative fonts for unavailable fonts, create a font-substitutions.json
file and mount it in the Document Engine container — refer to the mounting font substitutions section further down to learn how to do that.
The font-substitutions.json
file follows this schema (in TypeScript notation):
type FontSubstitutions = { fontSubstitutions: FontSubstitution[]; }; type FontSubstitution = { // Note that font family name replacements are made based upon pattern matching, // allowing for a font family name to be replaced with a different name. // Patterns are matched using the following rules: // - `*` matches multiple characters // - `?` matches a single character pattern: string; // The font that should be used as a replacement // when any font matching the given pattern is unavailable. target: string; };
Example font-substitions.json
file:
{ "fontSubstitutions": [ { "pattern": "Roboto-*", "target": "Courier New" }, { "pattern": "Calibri", "target": "Caladea" } ] }
Notes on font substitutions
Case-insensitive — The pattern
and target
names are case-insensitive.
Ordering matters — Substitutions are applied in the order listed. If multiple patterns match a font, the first match takes priority.
For example, consider the following list of font substitutions:
{ "fontSubstitutions": [ { "pattern": "Roboto-*", "target": "Courier New" }, { "pattern": "Roboto-Medium", "target": "Menlo" }, { "pattern": "Roboto-Medium*", "target": "Consolas" } ] }
If Document Engine processes a document with Roboto-MediumItalic
font but the font is unavailable, it will substitute Courier New
, as Roboto-*
is the first match.
Document Engine applies font substitutions from font-substitutions.json
in all document processing contexts, including conversions and annotation rendering.
To define font substitutions for a specific document and layer, use the API reference for the font substitutions endpoint.
Any document layer substitutions defined through the font substitutions API merge with those in font-substitutions.json
. If both specify a substitution for the same pattern, the API-defined substitution takes precedence.
For example, if both sources specify a target for Roboto-Medium*
, Document Engine will use the target set through the API instead of the one in font-substitutions.json
.
Mounting font substitutions
The most convenient way to add a file to Kubernetes deployment is by creating a ConfigMap:
apiVersion: v1 kind: ConfigMap metadata: name: my-default-font-substitutions namespace: <your namespace> data: font-substitutions.json: | { "fontSubstitutions": [ { "pattern": "Roboto-*", "target": "Courier New" } ] }
If you deploy using Helm, specify additional mounts for the ConfigMap resource using the following values:
extraVolumes: - name: my-font-substitutions configMap: name: my-default-font-substitutions items: - key: font-substitutions.json path: font-substitutions.json extraVolumeMounts: - name: my-font-substitutions mountPath: /font-substitutions.json subPath: font-substitutions.json
With Docker Compose
If you use Docker Compose, expose the font-substitutions.json
file from the host machine to the container by adding the following to your docker-compose.yml
file:
document-engine: volumes: - /path-to-font-substitutions-json-on-host:/font-substitutions.json