Overview

The FontManagement subsystem provides application-wide font loading, caching, and discovery for every Beep WinForms control. It ships a large catalog of embedded font resources while remaining fully extensible — theme packs, plugins, and consuming projects can register their own fonts at any time without modifying the core library.

Architecture

Consumer (Controls / Painters)
BeepFontManager.ButtonFont FontListHelper.GetCachedFont() BeepFontPaths.RobotoRegular
↓
BeepFontManager — High-level static API
Element fonts (Button/Header/Title…) Initialize() / Register() Scale / Reset
↓
BeepFontPaths — Embedded catalog
300+ font constants GetAllFamilies() GetFontPath(family, style)
BeepFontRegistry — Plugin registry
Register(Assembly) RegisterFromAppDomain() Changed event
↓
FontListHelper — Core engine
LoadAllFonts(FontScanOptions) GetCachedFont() — per-key Dictionary cache DPI scaling — EnableDpiFontScaling / DpiScaleFactor
↓
System fonts InstalledFontCollection
File-system fonts .ttf / .otf directories
Embedded resources Assembly manifest streams

BeepFontManager

The top-level static API. All controls and painters use this class to obtain Font instances rather than creating fonts directly. Fonts are lazy-initialised and cached for the application lifetime.

Configuration Properties

PropertyTypeDefaultDescription
DefaultFontNamestring"Segoe UI"Name used for the DefaultFont instance
DefaultFontSizefloat9.0fPoint size for the DefaultFont
DefaultFontStyleFontStyleRegularStyle for the DefaultFont
AppFontNamestring"Segoe UI"Name used for all element-specific fonts
AppFontSizefloat9.0fBase point size for element fonts
AppFontStyleFontStyleRegularBase style for element fonts

Setting any configuration property calls ResetFonts() internally, which recreates all cached element font instances.

Element Font Properties (read-only)

PropertySize offsetStyleUse case
DefaultFontbaseDefaultFontStyleGeneric fallback font
ButtonFont+0AppFontStyleButton labels
LabelFont+0AppFontStyleRegular labels
HeaderFont+2BoldSection headers
TitleFont+4BoldWindow / dialog titles
MenuFont+0AppFontStyleMenu items
TooltipFont−1AppFontStyleTooltip popups
StatusBarFont−1AppFontStyleStatus bar text
DialogFont+0AppFontStyleDialog body text
MonospaceFont+0 (Consolas)AppFontStyleCode editors, log viewers

Discovery Lists

PropertySourceDescription
SystemFontNamesFontListHelperNames of all installed system fonts
CustomFontNamesFontListHelperNames of loaded private / embedded fonts
AllFontNamesFontListHelperUnion of system + custom font names
EmbeddedFontFamiliesBeepFontPathsFamily names shipping in the Controls assembly
FontConfigurationsFontListHelperFull FontConfiguration list for all loaded fonts

Initialization

Call Initialize() once at application startup before any Beep controls are shown. It is idempotent — subsequent calls return immediately.

// Program.cs / Application startup
await BeepFontManager.Initialize();

// After init you can read element fonts immediately
Font btn = BeepFontManager.ButtonFont;      // "Segoe UI", 9pt
Font hdr = BeepFontManager.HeaderFont;     // "Segoe UI", 11pt Bold
Font code = BeepFontManager.MonospaceFont; // "Consolas", 9pt

Initialize() performs the following steps:

  1. Builds a FontScanOptions from built-in namespaces plus anything registered in BeepFontRegistry.
  2. Runs FontListHelper.LoadAllFonts(opt) on a background thread.
  3. Creates all element font instances via CreateDefaultFonts().
  4. Subscribes to BeepFontRegistry.Changed for incremental late-registration.

Registration API

Plugins and theme packs register their font-bearing assemblies through BeepFontManager (preferred) or directly on BeepFontRegistry.

// Register a single assembly (triggers immediate scan if already initialized)
BeepFontManager.Register(Assembly.GetExecutingAssembly());

// Register multiple assemblies at once
BeepFontManager.Register(new[] { asmA, asmB });

// Register a namespace prefix (embedded resources whose path contains this string)
BeepFontManager.Register("MyCompany.MyTheme.Fonts");

// Register a file-system directory for .ttf / .otf scanning
BeepFontManager.RegisterDirectory(@"C:\MyFonts");

BeepFontPaths

A static partial class spread across several files, each covering one font category. Every constant is the fully-qualified embedded resource name for use with Assembly.GetManifestResourceStream().

Base namespace: TheTechIdea.Beep.Winform.Controls.Fonts

Font Catalog by Category

The built-in catalog ships 300+ font variants across 30 families, organised into five categories:

Web Fonts

FamilyVariantsNotes
Cairo8 (ExtraLight → Black)Arabic / multilingual support
Roboto18 (Thin → Black + italics)Google's default UI font
RobotoCondensed18 (Thin → Black + italics)Condensed variant for tight layouts
OpenSans18 + condensed variantsHumanist sans-serif
Montserrat18 (Thin → Black + italics)Geometric, poster-style
Inter18pt static variantsOptimised for screen readability
NunitoMultiple weightsRounded, friendly style
TajawalMultiple weightsArabic + Latin
OxygenLight / Regular / BoldKDE / Linux standard

Monospace Fonts

FamilyVariantsNotes
FiraCode5 (Light → Bold)Ligature-enabled code font
JetBrainsMono16 (Thin → ExtraBold + italics)JetBrains IDE default
CascadiaCode12 (ExtraLight → Bold + italics)Windows Terminal default
SpaceMono4 (Regular / Italic / Bold / BoldItalic)Geometric monospace
DejaVuSansMono4 (Regular / Bold / Oblique / BoldOblique)Unicode-complete, ≈ Bitstream Vera

Display Fonts

FamilyNotes
BebasNeueAll-caps condensed, bold headers
OrbitronFuturistic / sci-fi UI
Exo2Contemporary geometric
RajdhaniDevanagari + Latin, compact display
WhitneyHumanist contemporary
NotoColorEmojiFull Unicode emoji coverage

Accessibility Fonts

FamilyNotes
AtkinsonHyperlegibleDesigned for low-vision readers
OpenDyslexicWeighted bottoms to anchor letter orientation
LexendReduces visual stress, improves reading speed

System / Miscellaneous Fonts

Family / ConstantNotes
SourceSansProAdobe's open-source UI font
ComicNeueModernised Comic Sans alternative
CaprasimoRegularDecorative single-weight display font
ConsolasMicrosoft monospace (embedded copy)

Lookup Methods

MethodReturnsDescription
GetAllPaths()Dictionary<string,string>All public static string fields that end in .ttf/.otf
GetAllFamilies()Dictionary<string, Dictionary<string,string>>Font paths grouped by family name; includes runtime-registered families
GetFontFamilyNames()List<string>Name list of all available families
GetFontPath(family, style)string?Resource path for a specific family + style combination
ResourceExists(path)boolChecks if the resource is present in ResourceAssembly
GetAvailableFontResources()string[]All .ttf/.otf resources from primary + all registered assemblies

Families Inner Class

For convenient strongly-typed access each font family has a nested static class under BeepFontPaths.Families:

// Direct constant path
string path = BeepFontPaths.RobotoRegular;

// Via Families nested class (same value, grouped namespace)
string path = BeepFontPaths.Families.Roboto.Regular;

// All weights as a dictionary
var robotoWeights = BeepFontPaths.Families.Roboto.GetAllStyles();
// => { "Regular", "Bold", "Thin", "Medium", ... }

// Cairo (8 weights)
var cairoWeights = BeepFontPaths.Families.Cairo.GetAllWeights();

// Get lookup across all families
var all = BeepFontPaths.GetAllFamilies();
// => { "Roboto": {...}, "Cairo": {...}, "FiraCode": {...}, ... }

Runtime Registration

When an external assembly registers its fonts (e.g. a theme pack), call RegisterFamilyFromAssembly to make those fonts discoverable through normal path lookups:

// Scan an assembly's embedded resources under a specific namespace root
BeepFontPaths.RegisterFamilyFromAssembly(
    Assembly.GetExecutingAssembly(),
    "MyThemePack.Fonts");

// Omit namespaceRoot to accept all embedded .ttf/.otf resources
BeepFontPaths.RegisterFamilyFromAssembly(Assembly.GetExecutingAssembly());

After this call, GetAllFamilies() and GetFontPath() will include the new fonts from that assembly.

BeepFontRegistry

Thread-safe central registry that tracks which assemblies, namespace prefixes, and file-system directories should be scanned for font resources. Uses a ReaderWriterLockSlim for concurrent access.

Registration Methods

MethodDescription
Register(Assembly)Registers a single assembly; auto-infers namespace roots from its embedded font resources and raises Changed
Register(IEnumerable<Assembly>)Batch registers multiple assemblies in one write-lock
Register(string namespacePrefix)Registers a namespace prefix so resources containing it are treated as fonts
RegisterFromDirectory(string)Registers a file-system path for .ttf/.otf scanning
RegisterFromAppDomain()Auto-discovers all non-System/Microsoft assemblies in the current AppDomain that pass HasConventionFontResource

Convention Discovery

An assembly is considered to contain fonts if it has at least one embedded .ttf or .otf resource whose namespace prefix ends with .Fonts. Example resource name: MyApp.Fonts.Roboto-Regular.ttf

// Check if an assembly satisfies the convention
bool hasFonts = BeepFontRegistry.HasConventionFontResource(myAssembly);

// Scan entire AppDomain automatically (useful for plugin hosts)
BeepFontRegistry.RegisterFromAppDomain();

Read-only Views

MethodReturns
GetRegisteredAssemblies()IReadOnlyCollection<Assembly> — thread-safe snapshot
GetRegisteredNamespaces()IReadOnlyCollection<string>
GetRegisteredDirectories()IReadOnlyCollection<string>

Changed Event

BeepFontRegistry.Changed fires whenever a new assembly, namespace, or directory is added. BeepFontManager subscribes after Initialize() to trigger incremental scans for late-registered plugins:

// After BeepFontManager.Initialize(), this is done automatically.
// You can also subscribe manually:
BeepFontRegistry.Changed += (s, e) =>
{
    // Re-scan or update UI font lists
};

PrimaryAssembly Override

Set BeepFontRegistry.PrimaryAssembly to redirect BeepFontPaths.ResourceAssembly to a theme-pack assembly that ships its own font catalog and embedded fonts:

// In a theme-pack initializer
BeepFontRegistry.PrimaryAssembly = Assembly.GetExecutingAssembly();
// All BeepFontPaths resource lookups now query this assembly first

FontListHelper

The low-level engine responsible for scanning, loading, and caching fonts from all three sources: system, file-system, and embedded assembly resources.

FontScanOptions

Controls what is scanned during LoadAllFonts():

PropertyTypeDefaultDescription
ScanSystemFontsbooltrueInclude all installed system (GDI) fonts
DirectoriesList<string>[ AppBase/Fonts ]File-system directories to scan for .ttf/.otf
EmbeddedNamespacesstring[]built-in prefixesNamespace prefixes that identify embedded font resources
AdditionalAssembliesList<Assembly>[ ]Extra assemblies to scan beyond executing assembly
IncludeFrameworkAssembliesboolfalseScan System.* / Microsoft.* assemblies
IncludeReferencedAssembliesbooltrueFollow assembly references during scanning
MaxReferenceDepthint2Maximum depth for assembly reference traversal
UseConventionDiscoverybooltrueAuto-register assemblies that satisfy the .Fonts convention
var options = new FontListHelper.FontScanOptions
{
    ScanSystemFonts = true,
    Directories = new List<string> { @"C:\MyApp\Fonts" },
    EmbeddedNamespaces = new[] { "MyCompany.Fonts" },
    AdditionalAssemblies = new List<Assembly> { myPluginAssembly },
    IncludeReferencedAssemblies = true,
    MaxReferenceDepth = 1,
    UseConventionDiscovery = true
};

FontListHelper.LoadAllFonts(options);

Font Caching

An application-lifetime Dictionary<string, Font> cache is keyed by "name|size|style". Paintings call these methods on every render; the cache prevents allocation spam.

MethodDescription
GetCachedFont(name, size, style)Returns a cached Font, applying the current DpiScaleFactor if EnableDpiFontScaling is true
GetCachedFont(name, size, dpiScale, style)Uses an explicit DPI scale factor instead of the global one
GetCachedFontForControl(name, size, control, style)Reads DPI from the given Control handle and scales accordingly
ClearFontCache()Evicts all entries; call on theme change or memory pressure
// In a painter's OnPaint — zero allocation after first call
Font f = FontListHelper.GetCachedFont("Roboto", 11f, FontStyle.Regular);
g.DrawString(text, f, brush, rect);

DPI Scaling

DPI scaling is off by default for backward compatibility. Enable it globally or per-control:

MemberDescription
EnableDpiFontScalingGlobal on/off switch (default: false)
DpiScaleFactorCurrent multiplier (default: 1.0 = 96 DPI); setting it clears the font cache
UpdateDpiFromControl(control)Reads the control's current DPI and updates DpiScaleFactor
UpdateDpiFromSystem()Reads system-wide DPI via DpiScalingHelper
// Enable DPI-aware fonts globally (do once at startup)
FontListHelper.EnableDpiFontScaling = true;
FontListHelper.UpdateDpiFromSystem();

// Per-control scaling in a custom form
protected override void OnDpiChanged(DpiChangedEventArgs e)
{
    base.OnDpiChanged(e);
    FontListHelper.UpdateDpiFromControl(this);
}

Discovery Methods

MethodReturnsDescription
GetSystemFonts()List<FontConfiguration>All installed InstalledFontCollection fonts; adds new entries to FontConfigurations
GetFontFilesLocations(directory)voidScans a directory for .ttf/.otf and loads them into PrivateFontCollection
GetFontNames()List<string>All font names (system + private)
GetSystemFontNames()List<string>System-only font names
GetPrivateFontNames()List<string>Private / embedded-only font names
GetFont(name, size, style)Font?Creates a font from the private collection; falls back to GDI
RegisterAndLoad(assembly)voidScans an assembly's embedded resources and loads discovered fonts immediately

Usage Patterns

1. Basic Application Setup

// Program.cs
static async Task Main(string[] args)
{
    // Initialize font system before creating any controls
    await BeepFontManager.Initialize();

    // Optionally customise the app font
    BeepFontManager.AppFontName = "Roboto";
    BeepFontManager.AppFontSize = 10f;

    Application.Run(new MainForm());
}

2. Using Element Fonts in a Control

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    // Use pre-cached element fonts — no allocation
    e.Graphics.DrawString("Header", BeepFontManager.HeaderFont, Brushes.Black, headerRect);
    e.Graphics.DrawString("Label",  BeepFontManager.LabelFont,  Brushes.Gray,  labelRect);
    e.Graphics.DrawString("0x1A3F", BeepFontManager.MonospaceFont, Brushes.Navy, codeRect);
}

3. Loading an Embedded Font by Path

// Get the resource path constant
string path = BeepFontPaths.Families.Roboto.Bold; 
// => "TheTechIdea.Beep.Winform.Controls.Fonts.Roboto.Roboto-Bold.ttf"

// Create a font from the embedded resource using BeepFontPathsExtensions
Font robotoBold = BeepFontPathsExtensions.CreateFontFromResource(path, 14f, FontStyle.Bold);

4. Plugin / Theme Pack Registration

// In a theme pack assembly's static initializer
public static class MyThemeInitializer
{
    public static void Register()
    {
        // Preferred: register via BeepFontManager (handles both pre- and post-init)
        BeepFontManager.Register(Assembly.GetExecutingAssembly());

        // Optionally redirect the primary assembly so path lookups favour this pack
        BeepFontRegistry.PrimaryAssembly = Assembly.GetExecutingAssembly();
    }
}

// Consuming application startup:
MyThemeInitializer.Register();
await BeepFontManager.Initialize();

5. DPI-Aware Painter

// In a custom BeepControl override
protected override void OnDpiChanged(DpiChangedEventArgs e)
{
    base.OnDpiChanged(e);
    FontListHelper.UpdateDpiFromControl(this);
    Invalidate(); // repaint with new font sizes
}

protected override void OnPaint(PaintEventArgs e)
{
    // GetCachedFontForControl reads the control's current DPI
    Font scaled = FontListHelper.GetCachedFontForControl("Roboto", 10f, this);
    e.Graphics.DrawString(Text, scaled, Brushes.Black, ClientRectangle);
}

6. Querying Available Families

// List all family names (built-in + runtime-registered)
List<string> families = BeepFontPaths.GetFontFamilyNames();

// Get a specific path by family + style name
string path = BeepFontPaths.GetFontPath("JetBrainsMono", "SemiBold");

// Enumerate all system font names
List<string> system = BeepFontManager.SystemFontNames;

// Enumerate all loaded (private) font names
List<string> custom = BeepFontManager.CustomFontNames;

File Organisation

FileClass / Role
BeepFontManager.csBeepFontManager — high-level static API, element fonts, initialization, registration
BeepFontRegistry.csBeepFontRegistry — thread-safe registry for assemblies, namespaces, directories and the Changed event
BeepFontPaths.Core.csBeepFontPaths (partial) — base namespace constant, individual font constants, GetAllPaths(), ResourceExists(), GetAvailableFontResources()
BeepFontPaths.Families.csBeepFontPaths.Families — nested classes (Cairo, Roboto, RobotoCondensed, …) with GetAllStyles() / GetAllWeights()
BeepFontPaths.WebFonts.csBeepFontPaths (partial) — Cairo, Roboto, RobotoCondensed, OpenSans, Montserrat, Inter, Nunito, Tajawal, Oxygen resource paths
BeepFontPaths.MonospaceFonts.csBeepFontPaths (partial) — FiraCode, JetBrainsMono, CascadiaCode, SpaceMono, DejaVuSansMono resource paths
BeepFontPaths.DisplayFonts.csBeepFontPaths (partial) — BebasNeue, Orbitron, Exo2, Rajdhani, Whitney, NotoColorEmoji resource paths
BeepFontPaths.AccessibilityFonts.csBeepFontPaths (partial) — AtkinsonHyperlegible, OpenDyslexic, Lexend resource paths
BeepFontPaths.SystemFonts.csBeepFontPaths (partial) — SourceSansPro, ComicNeue resource paths
BeepFontPaths.Extensions.csBeepFontPaths (partial) — GetAllFamilies(), GetFontPath(), RegisterFamilyFromAssembly(), runtime family dictionary
BeepFontPaths.Refactored.csBeepFontPaths (partial) — additional helpers and refactored look-up utilities
FontListHelper.csFontListHelper — core scanning/loading/caching engine, DPI scaling, FontScanOptions, discovery methods
README.mdArchitecture overview and quick-start guide

Best Practices