Runtime Font Loading
Gum renders text using BitmapFont — a .fnt descriptor file plus one or more .png texture atlases. There are three ways to get a BitmapFont onto a TextRuntime, each with different tradeoffs. Path 3 (in-memory generation, typically KernSmith) is the recommended route for new projects; pre-generated .fnt files on disk are the older path.
For crisp text under camera/layer zoom, see docs/code/files-and-fonts/font-oversampling.md — TextRuntime.UseFontOversampling rebuilds the font bigger automatically when zoom changes; unrelated to the three loading paths below.
.fnt encodings
BMFont defines three encodings for the same data, and ParsedFontFile picks a branch off the first character. Gum's tool and KernSmith both emit text, so that is the only branch normal users reach. XML requires hand-authoring a file from BMFont's XML export and deserializes via XmlSerializer (so it is not Native AOT safe); binary throws outright. Treat gaps confined to the XML or binary branch as near-zero user impact.
Three Font Loading Paths
Path 1: Custom Font File (UseCustomFont = true)
User provides a pre-built .fnt file directly:
textRuntime.UseCustomFont = true;
textRuntime.CustomFontFile = "fonts/MyFont.fnt";
- File path resolves relative to
FileManager.RelativeDirectory(typicallyContent/) - Loaded via
new BitmapFont(path), cached inLoaderManager - If the file doesn't exist, load silently skips — element gets
DefaultBitmapFont - No property-to-filename mapping; user controls the exact file
Path 2: Font Property Cache Lookup (UseCustomFont = false, the default)
Six properties combine into a deterministic filename in FontCache/:
| Property | Default | Effect on filename |
|---|---|---|
| Font / FontFamily | "Arial" | Base name (spaces → underscores) |
| FontSize | 18 | Base size number |
| OutlineThickness | 0 | _o{N} suffix if non-zero |
| UseFontSmoothing | true | _noSmooth suffix if false |
| IsItalic | false | _Italic suffix if true |
| IsBold | false | _Bold suffix if true |
Naming formula: FontCache/Font{size}{name}[_o{N}][_noSmooth][_Italic][_Bold].fnt
Examples: FontCache/Font18Arial.fnt, FontCache/Font24Times_New_Roman_o1_Bold.fnt
BmfcSave.GetFontCacheFileNameFor() produces this name. Every property setter on TextRuntime (Font, FontSize, etc.) calls UpdateToFontValues(), which regenerates the filename and attempts to load.
Key gotcha: Unless an IInMemoryFontCreator or IRuntimeFontService is registered, the .fnt file must already exist in FontCache/. Users often set FontSize = 24 expecting it to work, but silently get DefaultBitmapFont because Font24Arial.fnt was never generated. There is no error or warning — the text just renders in the default font.
All platforms (MonoGame/KNI/FNA and Raylib) raise CustomSetPropertyOnRenderable.PropertyAssignmentError when a wired InMemoryFontCreator throws, or declines and nothing else resolves a usable font — but it's a static event with no default subscriber, so even that diagnostic is silent unless the consumer subscribes to it themselves.
Path 3: In-Memory Font Creation (IInMemoryFontCreator) — New
Generates a BitmapFont entirely in memory at runtime — no pre-built .fnt files needed. The loading code already checks for this; it slots into the cascade between embedded resources and disk-based generation.
When registered on CustomSetPropertyOnRenderable.InMemoryFontCreator, font-property changes (Path 2) automatically create fonts on demand. This eliminates the FontCache pre-population requirement.
Lookup Cascade
When UseCustomFont = false and a font property changes, UpdateToFontValues tries these sources in order:
- LoaderManager cache — already-loaded BitmapFont by full path
- Embedded resource — MonoGameGum ships
Font18Arial(plus Bold/Italic/Bold_Italic variants) as embedded resources; these are the default fonts - IInMemoryFontCreator — generates BitmapFont in memory, no disk I/O
- IRuntimeFontService — generates
.fnt/.pngfiles on disk, then falls through to step 5 (typically tool-only, not used in game code) - Disk load —
new BitmapFont(fullPath)if the file exists - DefaultBitmapFont fallback —
Text.DefaultBitmapFont(Font18Arial, set duringSystemManagersinitialization)
The result is cached in LoaderManager so subsequent lookups for the same font properties hit step 1.
Wiring
SystemManagers initialization (called by GumService.Initialize) sets up the font system:
- Loads embedded Font18Arial as
Text.DefaultBitmapFont - Wires
GraphicalUiElement.UpdateFontFromProperties→CustomSetPropertyOnRenderable.UpdateToFontValues
Game code can optionally set CustomSetPropertyOnRenderable.InMemoryFontCreator to enable Path 3.
Key Files
| File | Purpose |
|---|---|
MonoGameGum/GueDeriving/TextRuntime.cs |
User-facing font properties; each setter calls UpdateToFontValues() |
RenderingLibrary/Graphics/Text.cs |
Renderable; holds BitmapFont instance and static DefaultBitmapFont |
RenderingLibrary/Graphics/Fonts/BitmapFont.cs |
Loads .fnt + .png textures; stores character metrics |
RenderingLibrary/Graphics/Fonts/BmfcSave.cs |
GetFontCacheFileNameFor() — deterministic cache filename from properties |
Gum/Wireframe/CustomSetPropertyOnRenderable.cs |
UpdateToFontValues() — orchestrates the lookup cascade |
RenderingLibrary/Graphics/Fonts/IInMemoryFontCreator.cs |
Interface for runtime font generation without disk I/O |
RenderingLibrary/Graphics/Fonts/IRuntimeFontService.cs |
Interface for disk-based font generation (typically tool-only) |
RenderingLibrary/SystemManagers.cs |
Wires font delegates and loads default embedded font |