mana-swift-core/Sources/ManaTokens/Typography/ManaFonts.swift
Till JS 9ee01d97e5 feat(ManaTokens): Marken-Schriften nativ (Inter/Source Serif 4/JetBrains Mono)
Bündelt die selbst-gehosteten Variable-TTFs des Vereins (OFL, glyphengleich
zum Web-@mana/themes) + registriert sie idempotent bei CoreText. Neu:
ManaFonts (Registrierung, Familien-Namen per Test verifiziert) und
ManaBrandTypography (spiegelt ManaTypography in den Marken-Schriften,
Dynamic-Type-gekoppelt). Additiv + opt-in — System-SF bleibt Default.

Fonts liegen in ManaTokens (~4 MB pro Konsument); eigenes opt-in-Product
ist die Alternative, falls zu schwer. Kein Tag bis Struktur-Entscheidung.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 17:10:35 +02:00

60 lines
2.5 KiB
Swift

import CoreText
import Foundation
import OSLog
/// Selbst-gehostete Marken-Schriften des Vereins, gebündelt als Variable-TTF
/// und zur Laufzeit bei CoreText registriert — derselbe Schriftsatz wie im
/// Web (`@mana/themes`): **Inter** (sans), **Source Serif 4** (serif),
/// **JetBrains Mono** (mono). Alle drei sind OFL-lizenziert (Lizenztexte in
/// `Resources/Fonts/OFL-*.txt`), kein externes CDN — Souveränität + DSGVO.
///
/// Die Familien-Namen matchen die `@font-face`-Namen im Web, damit Web und
/// Native identisch benannt sind.
///
/// Registrierung ist idempotent und thread-safe; sie passiert automatisch
/// beim ersten Zugriff auf ``ManaBrandTypography``. Wer ganz früh im
/// App-Start sichergehen will, ruft ``ManaFonts/register()`` explizit auf.
public enum ManaFonts {
/// Registrierter Familien-Name der Sans-Schrift (Body).
public static let sans = "Inter"
/// Registrierter Familien-Name der Serifen-Schrift (Lese-/Display-Text).
public static let serif = "Source Serif 4"
/// Registrierter Familien-Name der Monospace-Schrift (Code, Zahlen).
public static let mono = "JetBrains Mono"
private static let log = Logger(subsystem: "ev.mana.tokens", category: "fonts")
/// Registriert die gebündelten Variable-Fonts genau einmal bei CoreText.
/// Nachfolgende Aufrufe sind No-ops. Thread-safe (Swift `static let`).
public static func register() {
_ = registerOnce
}
private static let registerOnce: Void = {
let names = [
"InterVariable",
"InterVariable-Italic",
"SourceSerif4Variable",
"SourceSerif4Variable-Italic",
"JetBrainsMonoVariable",
]
for name in names {
guard let url = Bundle.module.url(forResource: name, withExtension: "ttf") else {
log.error("Font nicht im Bundle gefunden: \(name, privacy: .public).ttf")
continue
}
var error: Unmanaged<CFError>?
if !CTFontManagerRegisterFontsForURL(url as CFURL, .process, &error) {
// Bereits registriert (Code 105) ist harmlos — nur echte
// Fehler loggen.
let cfErr = error?.takeRetainedValue()
let code = (cfErr as Error?).map { ($0 as NSError).code } ?? -1
if code != 105 {
log.error("Font-Registrierung fehlgeschlagen: \(name, privacy: .public) (\(code))")
}
}
}
}()
}