# Gum Runtime Fonts

> Runtime Font Loading

- Skill: `vchelaru/gum-runtime-fonts` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vchelaru/gum-runtime-fonts`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/gum-runtime-fonts/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: vchelaru (https://skillmd.com/u/vchelaru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vchelaru/gum-runtime-fonts

---


# 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` (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:

1. **LoaderManager cache** — already-loaded BitmapFont by full path
2. **Embedded resource** — MonoGameGum ships `Font18Arial` (plus Bold/Italic/Bold_Italic variants) as embedded resources; these are the default fonts
3. **IInMemoryFontCreator** — generates BitmapFont in memory, no disk I/O
4. **IRuntimeFontService** — generates `.fnt`/`.png` files on disk, then falls through to step 5 (typically tool-only, not used in game code)
5. **Disk load** — `new BitmapFont(fullPath)` if the file exists
6. **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 |

