---
title: "How to configure custom fonts in Document Engine"
canonical_url: "https://www.nutrient.io/guides/document-engine/configuration/custom-fonts/"
md_url: "https://www.nutrient.io/guides/document-engine/configuration/custom-fonts.md"
last_updated: "2026-06-09T10:25:14.392Z"
description: "Learn how to configure custom fonts in Document Engine for enhanced text rendering. Follow step-by-step instructions to add and manage custom fonts for PDFs."
---

# 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:

- [Get global fonts](https://www.nutrient.io/api/reference/document-engine/upstream/#tag/Fonts/operation/get-global-fonts)

- [Get configured font substitutions](https://www.nutrient.io/api/reference/document-engine/upstream/#tag/Fonts/operation/get-configured-font-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](https://www.nutrient.io/guides/web/viewer/fonts/support.md#built-in-custom-font-support-with-document-engine) guide.

## Mounting custom fonts

To add custom fonts to Document Engine, mount them in the `/custom-fonts` directory.

If you deploy using [Helm](https://www.nutrient.io/guides/document-engine/deployment/helm.md), specify additional mounts using the following [values](https://github.com/PSPDFKit/helm-charts/blob/master/charts/document-engine/values.yaml):

```yaml

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](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) and download them to an [`emptyDir` volume](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir). 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:

```yaml

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:

```yaml

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:

1. **Clear the browser cache** — Removes cached rendering artifacts from the browser.

2. **Restart Document Engine** — Clears the in-memory cache for rendered pages.

3. **If using Redis** — Delete keys with the `PSPDFKit-TileCache-` and `PSPDFKit-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](http://corefonts.sourceforge.net/eula.htm). To use these fonts, download them from [SourceForge](https://sourceforge.net/projects/corefonts/files/the%20fonts/final/) and add them as custom fonts.




## Using emojis

To display emojis, import the [Windows-compatible Noto Color Emoji font](https://github.com/googlefonts/noto-emoji/blob/main/fonts/NotoColorEmoji_WindowsCompatible.ttf). 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):

```ts

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:**

```json

{
  "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:

```json

{
  "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](https://www.nutrient.io/api/reference/document-engine/upstream/#tag/Fonts).

Any document layer substitutions defined through the [font substitutions API](https://www.nutrient.io/api/reference/document-engine/upstream/#tag/Fonts) 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](https://www.nutrient.io/guides/document-engine/deployment/kubernetes.md) deployment is by creating a [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/):

```yaml

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](https://www.nutrient.io/guides/document-engine/deployment/helm.md), specify additional mounts for the ConfigMap resource using the following [values](https://github.com/PSPDFKit/helm-charts/blob/master/charts/document-engine/values.yaml):

```yaml

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:

```yaml

document-engine:
  volumes:
    - /path-to-font-substitutions-json-on-host:/font-substitutions.json

```
---

## Related pages

- [Certificate trust settings](/guides/document-engine/configuration/certificate-trust.md)
- [Cache configuration](/guides/document-engine/configuration/cache.md)
- [Asset storage configuration](/guides/document-engine/configuration/asset-storage.md)
- [Hold workers across short idle gaps such as zoom or scroll pauses:](/guides/document-engine/configuration/large-documents.md)
- [Configuration options](/guides/document-engine/configuration/options.md)
- [HTTP/2 shared rendering](/guides/document-engine/configuration/http2-shared-rendering.md)

