use egui::{Context, FontData, FontDefinitions, FontFamily};
use font_kit::family_name::FamilyName;
use font_kit::properties::Properties;
use font_kit::source::SystemSource;
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
pub struct FontManager {
loaded_fonts: Mutex<HashSet<String>>,
}
impl FontManager {
pub fn new() -> Self {
Self { loaded_fonts: Mutex::new(HashSet::new()) }
}
pub fn ensure_font_loaded(&self, ctx: &Context, family_name: &str) -> FontFamily {
let mut loaded = self.loaded_fonts.lock().unwrap();
if loaded.contains(family_name) {
return FontFamily::Name(family_name.into());
}
if let Ok(font_data) = self.load_system_font(family_name) {
let mut fonts = FontDefinitions::default();
ctx.fonts(|f| {
fonts = f.definitions().clone();
});
fonts
.font_data
.insert(family_name.to_string(), Arc::new(FontData::from_owned(font_data)));
fonts.families.insert(
FontFamily::Name(family_name.into()),
vec![family_name.to_string(), "Proportional".to_string()],
);
ctx.set_fonts(fonts);
loaded.insert(family_name.to_string());
log::debug!("Loaded font: {}", family_name);
FontFamily::Name(family_name.into())
} else {
log::warn!("Failed to load font '{}', using fallback", family_name);
FontFamily::Proportional
}
}
fn load_system_font(&self, family_name: &str) -> Result<Vec<u8>, String> {
let source = SystemSource::new();
let handle = source
.select_best_match(&[FamilyName::Title(family_name.to_string())], &Properties::new())
.map_err(|e| format!("Font not found: {}", e))?;
let font = handle.load().map_err(|e| format!("Failed to load font: {}", e))?;
font.copy_font_data()
.ok_or_else(|| "Font data not available".to_string())
.map(|arc| (*arc).clone())
}
}
impl Default for FontManager {
fn default() -> Self {
Self::new()
}
}