| name | gum-runtime-fonts |
| description | Gum runtime font loading (MonoGame/KNI) — three loading paths (custom, font-property cache, in-memory generation), lookup cascade, FontCache naming. Triggers: TextRuntime font properties, BitmapFont loading, CustomSetPropertyOnRenderable.UpdateToFontValues, IInMemoryFontCreator. |
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.
.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 (typically Content/)
- Loaded via
new BitmapFont(path), cached in LoaderManager
- 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/.png files 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 during SystemManagers initialization)
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 |