Custom fonts per map file #31

Closed
opened 2026-08-12 16:39:13 +00:00 by ImBenji · 1 comment
Owner

Let users import a custom .ttf/.otf font (single style — bold/italic synthesized by the renderer, same as station labels already handle missing weights) and use it in the currently open map.

Font bytes get embedded as base64 directly in the map's .ana file, same pattern ReferenceImageObjectData already uses for images, and registered at runtime via Flutter's FontLoader under a generated internal family id (decoupled from the user-facing display name to avoid collisions).

Needs a dedicated "Fonts" management panel (import/rename/delete) whose entries show up alongside the built-in Google Fonts list in the existing font pickers (text label properties panel, blender canvas panel, canvas settings panel). Also needs CanvasState.setStationLabelFontFamily's hard whitelist against kStationLabelFontOptions loosened to accept a map's registered custom fonts, or it silently resets to Inter.

See follow-up: multi-file font families.

Let users import a custom .ttf/.otf font (single style — bold/italic synthesized by the renderer, same as station labels already handle missing weights) and use it in the currently open map. Font bytes get embedded as base64 directly in the map's .ana file, same pattern ReferenceImageObjectData already uses for images, and registered at runtime via Flutter's FontLoader under a generated internal family id (decoupled from the user-facing display name to avoid collisions). Needs a dedicated "Fonts" management panel (import/rename/delete) whose entries show up alongside the built-in Google Fonts list in the existing font pickers (text label properties panel, blender canvas panel, canvas settings panel). Also needs CanvasState.setStationLabelFontFamily's hard whitelist against kStationLabelFontOptions loosened to accept a map's registered custom fonts, or it silently resets to Inter. See follow-up: multi-file font families.
ImBenji added the enhancementarea:ui/ux labels 2026-08-12 16:39:13 +00:00
ImBenji added this to the Arcs & Angles project 2026-08-12 16:39:13 +00:00
Author
Owner

Shipped. Verified against the repo 2026-09-05 during the roadmap audit.

CustomFontData (lib/models/fonts/custom_font_data.dart) carries id / name / bytes / format and base64-embeds the bytes straight into the .ana, exactly the pattern this issue described and the same one ReferenceImageObjectData uses for images. The generated id is what goes to FontLoader and TextStyle.fontFamily, decoupled from the user-facing name, so two maps both importing a "Brand Sans" cannot collide. A corrupt byte blob falls back to empty and logs rather than sinking the whole map load.

customFonts round-trips through map_serialisation.dart, CustomFontRegistry (lib/utils/custom_font_registry.dart) does the runtime registration, and lib/widgets/custom_fonts_dialog.dart is the management panel. Covered by custom_font_serialisation_test, custom_font_registry_test, custom_font_provider_test, font_naming_test.

Multi-file families are still open and correctly tracked separately in #32 - CustomFontData holds a single bytes/format pair, so bold and italic are still synthesised.

Shipped. Verified against the repo 2026-09-05 during the roadmap audit. `CustomFontData` (`lib/models/fonts/custom_font_data.dart`) carries id / name / bytes / format and base64-embeds the bytes straight into the `.ana`, exactly the pattern this issue described and the same one `ReferenceImageObjectData` uses for images. The generated `id` is what goes to FontLoader and TextStyle.fontFamily, decoupled from the user-facing `name`, so two maps both importing a "Brand Sans" cannot collide. A corrupt byte blob falls back to empty and logs rather than sinking the whole map load. `customFonts` round-trips through `map_serialisation.dart`, `CustomFontRegistry` (`lib/utils/custom_font_registry.dart`) does the runtime registration, and `lib/widgets/custom_fonts_dialog.dart` is the management panel. Covered by `custom_font_serialisation_test`, `custom_font_registry_test`, `custom_font_provider_test`, `font_naming_test`. Multi-file families are still open and correctly tracked separately in #32 - `CustomFontData` holds a single `bytes`/`format` pair, so bold and italic are still synthesised.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: IMBENJI.NET/Metro-Map-Maker#31