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.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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.
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 oneReferenceImageObjectDatauses for images. The generatedidis what goes to FontLoader and TextStyle.fontFamily, decoupled from the user-facingname, 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.customFontsround-trips throughmap_serialisation.dart,CustomFontRegistry(lib/utils/custom_font_registry.dart) does the runtime registration, andlib/widgets/custom_fonts_dialog.dartis the management panel. Covered bycustom_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 -
CustomFontDataholds a singlebytes/formatpair, so bold and italic are still synthesised.