# Embed custom fonts in our JavaScript PDF viewer

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.

Ensure you have appropriate licenses for any fonts you use. Some fonts may require commercial licenses for distribution or embedding.

## Nutrient Web SDK

You can specify an array of [`NutrientViewer.Font`](https://www.nutrient.io/api/web/NutrientViewer.Font.html) instances when initializing Nutrient Web SDK under the [`NutrientViewer.Configuration#customFonts`](https://www.nutrient.io/api/web/NutrientViewer.Configuration.html#customFonts) property:

```js

const fetcher = (fontFileName) =>
  fetch(`https://example.com/${fontFileName}`).then((r) => {
    if (r.status === 200) {
      return r.blob();
    } else {
      throw new Error();
    }
  });

const customFonts = ["arial.ttf", "helvetica.ttf", "tahoma.ttf"].map(
  (font) => new NutrientViewer.Font({ name: font, callback: fetcher }),
);

NutrientViewer.load({
  customFonts,
  //...your additional options.
});

```

Consider that the array specified must remain constant between different instances of Nutrient Web SDK. So instead of specifying [`NutrientViewer.Configuration#customFonts`](https://www.nutrient.io/api/web/NutrientViewer.Configuration.html#customFonts) as a literal array expression, we recommend passing an identifier you can reuse between Web SDK instances, as shown in the previous example. This is because we check during runtime that the same array of fonts is being specified when loading instances by using referential equality for the comparison. If a different array is specified between loading attempts, the instance won’t load.

When constructing `NutrientViewer.Font` instances, you need to identify a `name` property with the file name and extension of the font asset, and a `callback` that receives the same name as an argument where you can define the logic to use for retrieving the font. The callback must return a `Promise` that resolves to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).

For optimizing subsequent loads of the fonts, you can make use of different caching strategies. For example, you can rely on [HTTP caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching) headers returned from the server the font is fetched from, use a [service worker](https://developers.google.com/web/fundamentals/primers/service-workers) in combination with the [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache), use [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) to store the font blobs on the client side, or use any kind of strategy that better suits your use case.

If the callback rejects or returns a different value than the expected blob, the Web SDK will continue rendering the document using its built-in fallback fonts, and an error will be logged into the browser console.

Nutrient Web SDK will invoke the specified callbacks before beginning to render your document, which is why it’s important to consider a good strategy to prevent an impact on user experience. Custom fonts are also going to be retrieved if [`NutrientViewer.preloadWorker`](https://www.nutrient.io/api/web/NutrientViewer.html#.preloadWorker) is used. As a result, once you reach the point of creating an instance, both the fonts and WebAssembly artifacts might already be available upfront.

### Handling different fonts per document

It’s a known limitation that you cannot dynamically change the `customFonts` array between loading different documents in the same session. The SDK performs a referential equality check on the `customFonts` array when loading an instance. If you pass a new array when switching documents, it’ll cause the instance to fail to load.

To support different custom fonts for different documents, you can use one of the following strategies:

- **Preload all fonts** — Include all possible fonts that any document might need in the initial `customFonts` configuration when the SDK is first loaded.

- **Full application reload** — If you need to switch to a different set of fonts, you must fully reload your application and reinitialize the SDK with the new `customFonts` array.

  ```js

  // Clean up current instance.
  if (instance) {
    instance.unload();
  }

  // Initialize with new fonts.
  const newCustomFonts = ["times.ttf", "courier.ttf"].map(
    (font) => new NutrientViewer.Font({ name: font, callback: fetcher }),
  );

  const instance = await NutrientViewer.load({
    customFonts: newCustomFonts
    //...your additional options
  });
  ```

- **Use dynamic font loading** — Consider using the [dynamic font loading](https://www.nutrient.io/guides/web/viewer/fonts/dynamic-fonts.md) feature, which allows the SDK to fetch fonts on demand as needed. This approach is recommended if you cannot predict all the required fonts in advance.

## Nutrient Web SDK with Document Engine

You can expose a directory of fonts from the host machine to the Docker container by adding the following to your `docker-compose.yml` file:

```yaml

pspdfkit:
  volumes:
    - /font-directory-path-on-the-host:/custom-fonts

```

The font directory can be any directory that’s accessible to your app, and all `.ttf`, `.ttc`, and `.otf` files will be added to the font list of the Web SDK.

## 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 custom fonts with font substitution

When a PDF references non-embedded fonts by name, you can combine `customFonts` with [`fontSubstitutions`](https://www.nutrient.io/guides/web/viewer/fonts/substitution/) to remap those names to the font faces exposed by your custom fonts. This is useful for CJK documents that reference system fonts not available to the SDK. See the [font substitution](https://www.nutrient.io/guides/web/viewer/fonts/substitution.md) guide for details and examples. That guide also explains the distinction between a custom font asset name and the face name to use in `fontSubstitutions.target`.

## Troubleshooting

If custom fonts aren’t rendering as expected, refer to our [font rendering troubleshooting](https://www.nutrient.io/guides/web/rendering-issues/inconsistent-fonts.md) guide for help diagnosing issues with Identity-H encoding, non-embedded fonts, and font substitution.

## Using emojis

To use emojis in your project, 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 displaying emojis.
---

## Related pages

- [Load fonts on demand in PDFs using JavaScript](/guides/web/viewer/fonts/dynamic-fonts.md)
- [Substitute fonts in PDFs using JavaScript](/guides/web/viewer/fonts/substitution.md)
- [Introduction to PDF fonts](/guides/web/viewer/fonts/introduction.md)
- [PDF viewer supported fonts](/guides/web/viewer/fonts/support.md)

