Font fallbacks¶
When EPPlus measures, shapes or renders text, the fonts used in the workbook are not always available on the machine running the code, and an available font does not always contain every character in the text. EPPlus handles both cases with built-in fallbacks at three levels:
| Level | Applies when | Handled by |
|---|---|---|
| Font | The requested font cannot be found | Fallback chains per font name |
| Script | The font is found, but lacks the characters of a writing system, for example Japanese | Fallback chains per Unicode script |
| Glyph | A single character is missing from the font | The script chains, and the bundled Noto Emoji and Noto Math fonts |
In addition, the HTML and SVG exports substitute Office cloud fonts with fonts that browsers have, and text measurement can use serialized font metrics when a font is missing.
The defaults are designed so that you don't need to configure anything. To change them, see Configure fonts.
Font-level fallback¶
When text uses a font, EPPlus resolves it in this order:
- The requested font. The font is searched for in the configured font directories and, unless
SearchSystemDirectoriesis turned off, the operating system's font directories. The match must be exact: the same family and the same style. A request for Calibri Bold is not satisfied by Calibri Regular. - Your own fallback chain for the font name, if you have configured one in
FontFallbacks. - The built-in fallback chain for the font name, if there is one (see the table below).
- Archivo Narrow, a last-resort font that is embedded in EPPlus and is always available.
Every font in a chain must also be an exact match, family and style, to be used. If no font in a chain matches, resolution continues with the next step. Your own chain is tried before the built-in one, so your choices always win.
If you supply your own FontResolver, it replaces this whole process.
Built-in fallback chains¶
The built-in chains cover common Office and system fonts. They prefer metric-compatible fonts: open-source fonts designed with the same character widths as the original, such as Carlito for Calibri, Caladea for Cambria and Arimo for Arial. With a metric-compatible substitute, text takes up the same space as with the original font, so column widths, line breaks and page layout stay the same.
These fonts are not bundled with EPPlus. They are used only if they are installed or available in one of your font directories. On Linux servers and in containers, installing the Carlito, Caladea and Liberation font packages gives you substitutes for the most common Office fonts.
| Requested font | Fallbacks, in order |
|---|---|
| Aptos | Carlito, Liberation Sans, Arial, Helvetica |
| Aptos Display | Carlito, Liberation Sans, Arial, Helvetica |
| Aptos Light | Carlito, Liberation Sans, Arial |
| Aptos Mono | Cascadia Mono, Cascadia Code, Consolas, Liberation Mono, Courier New |
| Aptos Narrow | Liberation Sans Narrow, Arial Narrow, Carlito, Liberation Sans, Arial |
| Aptos SemiBold | Carlito, Liberation Sans, Arial |
| Arabic Transparent | Arial, Arimo |
| Arabic Transparent Bold | Arial Bold |
| Arial | Arimo, Liberation Sans, Helvetica |
| Arial Black | Liberation Sans, Arial |
| Arial Narrow | Liberation Sans Narrow, Liberation Sans |
| Book Antiqua | Palatino Linotype, Palatino, Liberation Serif, Times New Roman |
| Bookman Old Style | Liberation Serif, Times New Roman, Times |
| Caladea | Liberation Serif, Times New Roman, Times |
| Calibri | Carlito, Liberation Sans, Arial, Helvetica |
| Calibri Light | Carlito, Liberation Sans, Arial, Helvetica |
| Cambria | Caladea, Liberation Serif, Times New Roman, Times |
| Cambria Math | Latin Modern Math, STIX Two Math, Caladea, Liberation Serif |
| Candara | Carlito, Liberation Sans, Arial |
| Carlito | Liberation Sans, Arial, Helvetica |
| Cascadia Code | Cascadia Mono, Consolas, Liberation Mono, Courier New |
| Cascadia Mono | Cascadia Code, Consolas, Liberation Mono, Courier New |
| Century Gothic | Liberation Sans, Arial, Helvetica |
| Century Schoolbook | Liberation Serif, Times New Roman, Times |
| Comic Sans MS | Liberation Sans, Arial |
| Consolas | Cascadia Mono, Cascadia Code, Liberation Mono, Courier New |
| Constantia | Caladea, Liberation Serif, Georgia, Times New Roman |
| Corbel | Carlito, Liberation Sans, Arial |
| Courier | Courier New, Cousine, Liberation Mono |
| Courier New | Cousine, Liberation Mono, Courier |
| DejaVu Sans | Liberation Sans, Arial, Helvetica |
| DejaVu Sans Condensed | DejaVu Sans, Liberation Sans, Arial |
| DejaVu Sans Mono | Liberation Mono, Courier New, Courier |
| Garamond | Liberation Serif, Times New Roman, Times |
| Georgia | Gelasio, Liberation Serif, Times New Roman, Times |
| Helvetica | Arial, Arimo, Liberation Sans |
| Helvetica Neue | Helvetica, Arial, Arimo, Liberation Sans |
| Impact | Liberation Sans Narrow, Arial Narrow, Arial Black, Arial |
| Liberation Mono | Courier New, Cousine, Courier |
| Liberation Sans | Arial, Arimo, Helvetica |
| Liberation Sans Narrow | Arial Narrow, Liberation Sans, Arial |
| Liberation Serif | Times New Roman, Tinos, Times |
| Lucida Console | DejaVu Sans Mono, Liberation Mono, Consolas, Courier New |
| Lucida Sans Unicode | DejaVu Sans, Liberation Sans, Arial |
| MS Sans Serif | Arial, Arimo, Liberation Sans, Helvetica |
| MS Serif | Times New Roman, Tinos, Liberation Serif, Times |
| Palatino | Palatino Linotype, Book Antiqua, Liberation Serif, Times New Roman |
| Palatino Linotype | Palatino, Book Antiqua, Liberation Serif, Times New Roman |
| Segoe Print | Comic Sans MS, Liberation Sans, Arial |
| Segoe Script | Comic Sans MS, Liberation Sans, Arial |
| Segoe UI | Liberation Sans, Arial, Helvetica |
| Tahoma | DejaVu Sans Condensed, DejaVu Sans, Liberation Sans, Arial |
| Times | Times New Roman, Tinos, Liberation Serif |
| Times New Roman | Tinos, Liberation Serif, Times |
| Trebuchet MS | Liberation Sans, Arial, Helvetica |
| Verdana | DejaVu Sans, Liberation Sans, Arial |
To add a chain for a font that is not in the table, or to replace a built-in chain, use
FontFallbacks:
The last-resort font¶
Archivo Narrow guarantees that font resolution always ends with a font, so a missing font never causes an export to fail. It covers Latin script. Text in other writing systems is still handled by glyph-level fallback, which works on top of whichever font was resolved, including Archivo Narrow.
Script-level fallback¶
A font can be available and still lack the characters of a writing system. A typical example is a Latin font such as Calibri used for a cell that contains Japanese text. For each writing system (Unicode script), EPPlus has a fallback chain of fonts to take the missing characters from.
The built-in chains try the fonts that ship with Windows and macOS first, then the Noto fonts that are common on Linux:
| Script | Fallbacks, in order |
|---|---|
Han (Chinese ideographs) |
Microsoft YaHei, SimSun, Noto Sans CJK SC, PingFang SC |
Hiragana |
Yu Gothic, MS Gothic, Meiryo, Noto Sans CJK JP |
Katakana |
Yu Gothic, MS Gothic, Meiryo, Noto Sans CJK JP |
Hangul |
Malgun Gothic, Gulim, Noto Sans CJK KR |
Arabic |
Segoe UI, Tahoma, Arial, Noto Sans Arabic |
Hebrew |
Segoe UI, Tahoma, Arial, Noto Sans Hebrew |
Thai |
Tahoma, Leelawadee UI, Noto Sans Thai |
Devanagari |
Mangal, Nirmala UI, Noto Sans Devanagari |
A chain never crosses into another language. Japanese text does not fall back to a Chinese font, or the reverse, since the same ideograph is drawn differently in Chinese and Japanese typography.
Fonts in a script chain must be installed or available in a font directory, and are skipped if they are not. A chain is resolved the first time a character of that script is encountered.
There are no built-in chains for Latin, Cyrillic or Greek, since these are expected to be
covered by the font itself. If your workbooks contain Cyrillic or Greek text in fonts that may be
missing on the machine, configure a chain for those scripts.
Use SetScriptFallback to configure a chain. It replaces the built-in chain for the script
completely; the two are not merged:
package.Workbook.ConfigureFonts(cfg =>
{
cfg.FontDirectories.Add(@"C:\MyFonts");
// Japanese text: prefer our own font, then the Windows default.
cfg.SetScriptFallback(UnicodeScript.Hiragana, "Noto Sans JP", "Yu Gothic");
cfg.SetScriptFallback(UnicodeScript.Katakana, "Noto Sans JP", "Yu Gothic");
// Cyrillic has no built-in chain.
cfg.SetScriptFallback(UnicodeScript.Cyrillic, "Noto Sans", "Arial");
// No fallback at all for Thai: missing characters render as .notdef.
cfg.SetScriptFallback(UnicodeScript.Thai);
});
package.Workbook.ConfigureFonts(Sub(cfg)
cfg.FontDirectories.Add("C:\MyFonts")
' Japanese text: prefer our own font, then the Windows default.
cfg.SetScriptFallback(UnicodeScript.Hiragana, "Noto Sans JP", "Yu Gothic")
cfg.SetScriptFallback(UnicodeScript.Katakana, "Noto Sans JP", "Yu Gothic")
' Cyrillic has no built-in chain.
cfg.SetScriptFallback(UnicodeScript.Cyrillic, "Noto Sans", "Arial")
' No fallback at all for Thai: missing characters render as .notdef.
cfg.SetScriptFallback(UnicodeScript.Thai)
End Sub)
Note that Japanese text is usually a mix of Han, Hiragana and Katakana. To make all of it come
from the same font, configure the same chain for all three scripts.
Glyph-level fallback¶
Fallback within a text is decided character by character. For each character, EPPlus uses the first of the following that has a glyph for it:
- The resolved font, that is the requested font or the font chosen by font-level fallback.
- Depending on the character:
- Emoji: Noto Emoji, which is bundled with EPPlus.
- Mathematical symbols: Noto Math, which is bundled with EPPlus.
- Other characters: the script chain for the character's script.
- If no font has the glyph, the character is drawn with the font's missing glyph symbol
(
.notdef), usually an empty box. The export still succeeds.
A single cell can therefore be drawn with several fonts, for example Calibri for the Latin text, Yu Gothic for the Japanese characters and Noto Emoji for an emoji. In a PDF, each font that contributed glyphs is subsetted and embedded.
Emoji and mathematical symbols are always handled by the bundled fonts and cannot be configured.
Limitations¶
- Text shaping applies to the resolved font only. Kerning, ligatures, contextual forms and the placement of diacritical marks are applied to glyphs from the resolved font, but not to glyphs taken from a fallback font. For most scripts this makes little visible difference. For scripts that depend on contextual forms, such as Arabic and Devanagari, use a font that covers the script as the cell's font rather than relying on fallback.
- Fallback glyphs are always regular. Fonts in a script chain are loaded in their regular style, so bold or italic text that falls back to another font is drawn without bold or italic.
- Variation sequences (a character followed by a variation selector, used by some emoji and CJK ideographs) are only honoured when the same font has both the character and the sequence.
Web font substitution¶
When a workbook is exported to HTML or SVG, the text is drawn by the browser, with the fonts the browser has access to. Office cloud fonts, such as Aptos, are delivered to Office through Microsoft's font service and are not installed on the system, so browsers can't use them, even on machines where Office has them.
For the HTML and SVG exports, EPPlus therefore substitutes these fonts with fonts that ship with Windows and have metric-compatible open alternatives. The text is then both measured on the server and drawn in the browser with the same font:
| Font in the workbook | Used in HTML and SVG |
|---|---|
| Aptos | Arial |
| Aptos Display | Arial |
| Aptos Narrow | Calibri |
| Aptos Serif | Cambria |
| Aptos Mono | Consolas |
| Grandview | Calibri |
| Seaford | Segoe UI |
| Tenorite | Segoe UI |
| Bierstadt | Arial |
| Skeena | Segoe UI |
The substitution is a single step: the substitute is not substituted again. PDF export is not affected, since a PDF embeds the fonts it uses.
To change the substitutions, edit WebFontSubstitutions. Remove an entry to pass the original font
name through to the browser, for example if your users have the font installed:
Measurement without the font¶
Measuring text, for example to autofit columns or to break lines, needs the widths of the characters, not their outlines. When font resolution ends at the last-resort font, EPPlus measures with serialized metrics of the requested font instead, if it has them. These are character widths for common fonts that are included in EPPlus. Measuring the right font from stored widths is closer to the result in Excel than measuring a different font exactly.
A metric-compatible substitute found by font-level fallback is always preferred, since it is a real
font. The behaviour is controlled by MetricsFallback:
| Value | Behaviour |
|---|---|
WhenFontMissing |
Default. Serialized metrics are used when font resolution ends at the last-resort font. |
Always |
Serialized metrics are used whenever they exist for the font, without looking for font files. Measurements are then the same on every machine, regardless of the fonts installed. |
Disabled |
Serialized metrics are never used. |
Serialized metrics are used for measurement only. Text that is rendered, for example in a PDF, always needs a real font.
Finding out which fonts were used¶
To see which fonts and fallbacks EPPlus actually used, assign a logger to the Logger property in
ConfigureFonts. The fallback decisions are reported as events:
| Event | Severity | Reported when |
|---|---|---|
FontFallback |
Information | A font was replaced by a font from a fallback chain. |
FontLastResort |
Warning | No font in any chain was found, and the last-resort font was used. |
ScriptChainResolved |
Information | The script chain for a script was resolved, with the status of each font in it. |
ScriptFallbackUsed |
Information | A fallback font supplied glyphs for the first time. |
GlyphFallback |
Debug | A character was taken from a fallback font (once per character). |
GlyphMissing |
Warning | No font had a glyph for a character (once per character, at most 100). |
MeasurementMetricsUsed |
Information | Text was measured with serialized metrics instead of a font file. |
WebFontSubstitution |
Debug | A font was substituted for the HTML and SVG exports. |
A GlyphMissing warning includes the reason, for example that no script chain is configured for the
character's script, or that none of the fonts in the chain is installed.