pub use valo_fontmgr::{self as fontmgr, Watch};
use valo_fontmgr::{FontManager, Slant, Style, Typeface};
use valo_text::{match_style, FaceSet, Font, FontAttrs, FontDemand, FontSource};
pub struct SystemFonts {
manager: Box<dyn FontManager>,
locales: Vec<String>,
}
impl SystemFonts {
#[cfg(not(target_arch = "wasm32"))]
pub fn load() -> Self {
Self::with_manager(valo_fontmgr::platform())
}
pub fn with_manager(manager: Box<dyn FontManager>) -> Self {
SystemFonts {
manager,
locales: Vec::new(),
}
}
pub fn set_locales(&mut self, locales: Vec<String>) {
self.locales = locales;
}
pub fn manager(&mut self) -> &mut dyn FontManager {
&mut *self.manager
}
pub fn face_count(&self) -> usize {
self.manager.face_count()
}
pub fn system_family(&mut self, size: f32) -> Vec<Font> {
let mut fonts: Vec<Font> = Vec::new();
for weight in (100..=900).step_by(100) {
let attrs = FontAttrs {
weight,
..FontAttrs::default()
};
let Some(typeface) = self.manager.system_font(size, style_of(attrs)) else {
continue;
};
let Some(font) = match_instance(typeface, attrs) else {
continue;
};
if !fonts.iter().any(|known| known.attrs() == font.attrs()) {
fonts.push(font);
}
}
fonts
}
pub fn satisfy(&mut self, faces: &FaceSet, demand: &FontDemand) -> Option<FaceSet> {
faces.grown_by(self, demand)
}
}
impl FontSource for SystemFonts {
fn family(&mut self, name: &str) -> Vec<Font> {
self.manager
.family(name)
.into_iter()
.flat_map(fonts_of)
.collect()
}
fn face_for_codepoint(&mut self, codepoint: char, attrs: FontAttrs) -> Option<Font> {
let locales: Vec<&str> = self.locales.iter().map(String::as_str).collect();
let typeface = self
.manager
.match_character(None, style_of(attrs), &locales, codepoint)?;
match_instance(typeface, attrs).filter(|font| font.covers(codepoint))
}
}
fn fonts_of(typeface: Typeface) -> Vec<Font> {
Font::instances_from_data(typeface.shared_data(), typeface.index())
}
fn match_instance(typeface: Typeface, attrs: FontAttrs) -> Option<Font> {
let mut instances = fonts_of(typeface);
let nearest = match_style(&instances, attrs)?;
let index = instances.iter().position(|font| font == nearest)?;
Some(instances.swap_remove(index))
}
fn style_of(attrs: FontAttrs) -> Style {
Style {
weight: attrs.weight,
width: attrs.stretch,
slant: if attrs.italic {
Slant::Italic
} else {
Slant::Upright
},
}
}