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>
60 lines
2.5 KiB
Swift
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))")
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|