# Configure fonts

When EPPlus renders text to the HTML/SVG and PDF exports, it measures and shapes the text using real font data. EPPlus ships with sensible defaults, including built-in fallbacks for missing fonts and characters, so in most cases you don't need to configure anything. When you do, `Workbook.ConfigureFonts` lets you control where fonts are loaded from, how missing fonts and characters are handled, and how fonts are embedded in PDF files.

Font configuration is applied **per workbook** and must be set **before** you export. It does not affect any other workbook or any global state.

## Default behaviour

If you never call `ConfigureFonts`:

- Fonts are searched for in the operating system's standard font directories.
- A missing font is replaced using EPPlus's built-in fallback chains, which prefer metric-compatible substitutes such as Carlito for Calibri. If none is available, the embedded **Archivo Narrow** font is used, so a missing font never causes an export to fail.
- Characters that the font lacks, for example Japanese text in a Latin font, are taken from per-script fallback fonts when these are installed. Emoji and mathematical symbols always come from the bundled **Noto Emoji** and **Noto Math** fonts.

The export always succeeds, but the result is not always the intended one. Text in a writing system for which no font is available is drawn with missing-glyph boxes. To render, for example, Chinese, Japanese or Korean text on a server, make sure a font for the script is installed or supplied through `FontDirectories`.

How the fallbacks work, and the complete list of built-in chains, is described in [Font fallbacks](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/index.md>).

## Configuring fonts

Call `ConfigureFonts` on the workbook and change the configuration inside the callback. The example below adds a font directory and turns off the system font search, so that only fonts from your directory are used:

```csharp
using var package = new ExcelPackage();
package.Workbook.ConfigureFonts(cfg =>
{
    cfg.FontDirectories.Add(@"C:\MyFonts");
    cfg.SearchSystemDirectories = false;
});

// ...add drawings/charts, then export
```

```vbnet
Using package As New ExcelPackage()
    package.Workbook.ConfigureFonts(Sub(cfg)
                                        cfg.FontDirectories.Add("C:\MyFonts")
                                        cfg.SearchSystemDirectories = False
                                    End Sub)

    ' ...add drawings/charts, then export
End Using
```

The changes you make inside the callback are applied as a single transaction: when the callback returns, the font resolver is rebuilt and the workbook's font caches are cleared.

These are the members you can use inside the callback:

| Member | Data type | Description |
| --- | --- | --- |
| `FontDirectories` | `IList<string>` | Additional directories to search for font files, beyond the system font directories. |
| `SearchSystemDirectories` | `bool` | Whether the operating system's standard font directories are searched. Defaults to `true`. Set to `false` to use only `FontDirectories`. |
| `FontFallbacks` | `IDictionary<string, string[]>` | Your own fallback chains, mapping a font name to the fonts to try when it is missing. Tried before the built-in chains. See [Font-level fallback](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/#font-level-fallback>). |
| `SetScriptFallback(...)` | method | Replaces the fallback chain for a Unicode script, used for characters the font lacks. See [Script-level fallback](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/#script-level-fallback>). |
| `WebFontSubstitutions` | `IDictionary<string, string>` | Fonts substituted in the HTML and SVG exports, for fonts that browsers can't use, such as Aptos. See [Web font substitution](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/#web-font-substitution>). |
| `MetricsFallback` | `MetricsFallbackMode` | When text is measured with serialized font metrics instead of a font file. See [Measurement without the font](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/#measurement-without-the-font>). |
| `OnFontEmbedding(...)` | method | Decides how fonts are embedded in PDF files. See [Embedding restrictions](<#embedding-restrictions>). |
| `Logger` | `IFontLogger` | Receives reports about font resolution, fallbacks and embedding. See [Finding out which fonts were used](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/#finding-out-which-fonts-were-used>). |
| `FontResolver` | `IFontResolver` | Replaces the font resolver entirely. See [Custom font resolution](<#custom-font-resolution>). |
| `Reset()` | method | Restores the default configuration, including the built-in script chains and web font substitutions. |

## Common configurations

### Fonts on a server or in a container

Servers and containers often have few fonts installed. Either install the fonts the workbooks use, or ship them with your application and add their directory:

```csharp
package.Workbook.ConfigureFonts(cfg =>
{
    cfg.FontDirectories.Add(Path.Combine(AppContext.BaseDirectory, "Fonts"));
});
```

```vbnet
package.Workbook.ConfigureFonts(Sub(cfg)
                                    cfg.FontDirectories.Add(Path.Combine(AppContext.BaseDirectory, "Fonts"))
                                End Sub)
```

If you can't distribute the original fonts, the metric-compatible open fonts Carlito, Caladea and the Liberation family are used automatically by the built-in fallback chains when they are available. They keep column widths and line breaks close to the original.

### A fallback for your own font

```csharp
package.Workbook.ConfigureFonts(cfg =>
{
    // If "Corporate Sans" is not available, try these in order.
    cfg.FontFallbacks["Corporate Sans"] = new[] { "Arial", "Open Sans" };
});
```

```vbnet
package.Workbook.ConfigureFonts(Sub(cfg)
                                    ' If "Corporate Sans" is not available, try these in order.
                                    cfg.FontFallbacks("Corporate Sans") = New String() {"Arial", "Open Sans"}
                                End Sub)
```

### A font for a writing system

```csharp
package.Workbook.ConfigureFonts(cfg =>
{
    cfg.FontDirectories.Add(@"C:\MyFonts");

    // Use "Noto Sans JP" for Chinese/Japanese ideographs the cell's font lacks.
    cfg.SetScriptFallback(UnicodeScript.Han, "Noto Sans JP");
});
```

```vbnet
package.Workbook.ConfigureFonts(Sub(cfg)
                                    cfg.FontDirectories.Add("C:\MyFonts")

                                    ' Use "Noto Sans JP" for Chinese/Japanese ideographs the cell's font lacks.
                                    cfg.SetScriptFallback(UnicodeScript.Han, "Noto Sans JP")
                                End Sub)
```

Setting a chain replaces the built-in chain for that script completely. Pass no font names to turn off fallback for a script.

## Embedding restrictions

When EPPlus embeds a font in a PDF it subsets the font by default: only the glyphs actually used are included, which keeps the file small. Fonts declare, through their OS/2 `fsType` field, whether and how they may be embedded. EPPlus honours those declarations:

- **Installable, Preview &amp; Print, or Editable** embedding: the font is subsetted and embedded normally.
- **No subsetting:** the font may be embedded, but not as a subset. EPPlus embeds the complete, unmodified font instead. This produces a larger PDF but respects the font's terms.
- **Restricted License:** the font declares that it must not be embedded at all. By default EPPlus throws an exception rather than silently embedding a font you may not have the right to embed.

The `fsType` field is the font vendor's *declaration*; it is not a technical lock. If you hold a licence from the font's owner that permits embedding, regardless of what `fsType` says, you can override EPPlus's default handling with `OnFontEmbedding`:

```csharp
package.Workbook.ConfigureFonts(cfg =>
{
    cfg.OnFontEmbedding(info =>
    {
        // You have licensed "Acme Display" for embedding.
        if (info.FontName == "Acme Display")
            return FontEmbeddingDecision.Subset;
        // Everything else follows EPPlus's default handling.
        return FontEmbeddingDecision.Default;
    });
});
```

```vbnet
package.Workbook.ConfigureFonts(Sub(cfg)
                                    cfg.OnFontEmbedding(Function(info)
                                                            ' You have licensed "Acme Display" for embedding.
                                                            If info.FontName = "Acme Display" Then
                                                                Return FontEmbeddingDecision.Subset
                                                            End If
                                                            ' Everything else follows EPPlus's default handling.
                                                            Return FontEmbeddingDecision.Default
                                                        End Function)
                                End Sub)
```

The callback is invoked once per font about to be embedded and receives a `FontEmbeddingInfo` describing the font: its `FontName` and the `Restriction` it declares (a `FontEmbeddingRestriction` of `None`, `NoSubsetting` or `NoEmbedding`). You can inspect the declared restriction and decide accordingly, for example keeping restricted fonts out of the PDF entirely rather than failing the export:

```csharp
package.Workbook.ConfigureFonts(cfg =>
{
    cfg.OnFontEmbedding(info =>
    {
        // We hold no licence for restricted fonts, so keep them out of the PDF
        // and let the fallback chain render their text instead.
        if (info.Restriction == FontEmbeddingRestriction.NoEmbedding)
            return FontEmbeddingDecision.Skip;
        // Everything else follows EPPlus's default handling.
        return FontEmbeddingDecision.Default;
    });
});
```

```vbnet
package.Workbook.ConfigureFonts(Sub(cfg)
                                    cfg.OnFontEmbedding(Function(info)
                                                            ' We hold no licence for restricted fonts, so keep them out of the PDF
                                                            ' and let the fallback chain render their text instead.
                                                            If info.Restriction = FontEmbeddingRestriction.NoEmbedding Then
                                                                Return FontEmbeddingDecision.Skip
                                                            End If
                                                            ' Everything else follows EPPlus's default handling.
                                                            Return FontEmbeddingDecision.Default
                                                        End Function)
                                End Sub)
```

Return one of:

| Decision | Effect |
| --- | --- |
| `Default` | Follow EPPlus's standard handling for the font's declared restriction (throw for Restricted License, embed whole for no subsetting, subset otherwise). |
| `Subset` | Subset and embed the font, regardless of `fsType`. |
| `EmbedWhole` | Embed the complete font without subsetting. |
| `Skip` | Do not embed the font; its text is rendered with the fallback chain instead (ultimately the last-resort font, Archivo Narrow). Lets you keep a font out of the PDF without failing the export. |

> [!WARNING]
>
> **You are responsible for compliance**
>
> Returning `Subset` or `EmbedWhole` for a font that restricts embedding asserts that you hold the necessary rights. EPPlus cannot verify any licence you may have obtained and does not check on your behalf.

Fonts bundled with EPPlus (Archivo Narrow, Noto Emoji and Noto Math) are always subsetted and embedded.

## Custom font resolution

The font pipeline is built around interfaces, so its parts can be replaced when the built-in behaviour isn't enough. Most notably, you can supply your own font resolver: implement `IFontResolver` (`OfficeOpenXml.Interfaces.Fonts`) and assign it to `FontResolver` to take full responsibility for producing font data, for example to load fonts from a database or an embedded resource instead of the file system.

A custom resolver replaces the whole [font-level fallback](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/#font-level-fallback>), including the built-in chains and the last-resort font. Script-level and glyph-level fallback still apply, and the fonts in the script chains are loaded through your resolver. Keep in mind:

- **Return a font for every request.** Archivo Narrow, the last-resort font, is part of EPPlus's own resolver and is not used with a custom one. If your resolver can't find a font, return a default font of your own rather than `null`.
- **Implement `IFontAvailabilityProvider` as well,** if your resolver substitutes fonts internally. EPPlus uses it to check whether a font in a script chain is really available. Without it, EPPlus can only check that the resolver returns *some* font, so every font in a chain looks available and the chain is not walked as intended.

```csharp
using OfficeOpenXml.Interfaces.Fonts;

package.Workbook.ConfigureFonts(cfg =>
{
    // MyCustomFontResolver implements IFontResolver
    cfg.FontResolver = new MyCustomFontResolver();
});
```

```vbnet
Imports OfficeOpenXml.Interfaces.Fonts

package.Workbook.ConfigureFonts(Sub(cfg)
                                    ' MyCustomFontResolver implements IFontResolver
                                    cfg.FontResolver = New MyCustomFontResolver()
                                End Sub)
```

## Which exports use the font configuration

Font configuration affects the exports that measure and shape text against real font data:

- **HTML/SVG export**: charts and shapes rendered to SVG.
- **PDF export**.

Other operations, such as saving the workbook as an `.xlsx` file, are not affected by this configuration.

## See also

- [Font fallbacks](<https://epplussoftware.com/docs/output-formats/fonts/fallbacks/index.md>)
- [PDF](<https://epplussoftware.com/docs/output-formats/pdf/index.md>)
- [HTML &amp; CSS](<https://epplussoftware.com/docs/output-formats/html/index.md>)
- [Autofit columns and text measurement](<https://epplussoftware.com/docs/worksheet-and-ranges/autofit-columns/index.md>)
