use crate::font::registry::FontRegistry;
use crate::types::FontFaceId;
pub struct ResolvedFont {
pub font_face_id: FontFaceId,
pub size_px: f32,
pub face_index: u32,
pub swash_cache_key: swash::CacheKey,
pub scale_factor: f32,
pub weight: u16,
}
#[allow(clippy::too_many_arguments)]
pub fn resolve_font(
registry: &FontRegistry,
font_family: Option<&str>,
font_weight: Option<u32>,
font_bold: Option<bool>,
font_italic: Option<bool>,
font_point_size: Option<u32>,
scale_factor: f32,
font_scale: f32,
) -> Option<ResolvedFont> {
let weight = resolve_weight(font_weight, font_bold);
let italic = font_italic.unwrap_or(false);
let size_px = font_point_size
.map(|s| s as f32)
.unwrap_or(registry.default_size_px())
* font_scale;
if let Some(family) = font_family {
if let Some(face_id) = registry.query_font(family, weight, italic) {
let entry = registry.get(face_id)?;
return Some(ResolvedFont {
font_face_id: face_id,
size_px,
face_index: entry.face_index,
swash_cache_key: entry.swash_cache_key,
scale_factor,
weight,
});
}
warn_unknown_family(family);
}
let default_id = registry.default_font()?;
if (weight != 400 || italic)
&& let Some(variant_id) = registry.query_variant(default_id, weight, italic)
{
let variant_entry = registry.get(variant_id)?;
return Some(ResolvedFont {
font_face_id: variant_id,
size_px,
face_index: variant_entry.face_index,
swash_cache_key: variant_entry.swash_cache_key,
scale_factor,
weight,
});
}
let entry = registry.get(default_id)?;
Some(ResolvedFont {
font_face_id: default_id,
size_px,
face_index: entry.face_index,
swash_cache_key: entry.swash_cache_key,
scale_factor,
weight,
})
}
fn warn_unknown_family(family: &str) {
use std::collections::HashSet;
use std::sync::{LazyLock, Mutex};
static WARNED: LazyLock<Mutex<HashSet<String>>> = LazyLock::new(|| Mutex::new(HashSet::new()));
if let Ok(mut warned) = WARNED.lock()
&& warned.insert(family.to_string())
{
eprintln!(
"text-typeset: font family {family:?} is not registered; falling back \
to the default font. Register it (e.g. via the host application's font \
registrar) before shaping."
);
}
}
pub fn font_has_glyph(registry: &FontRegistry, face_id: FontFaceId, ch: char) -> bool {
let entry = match registry.get(face_id) {
Some(e) => e,
None => return false,
};
let font_ref = match swash::FontRef::from_index(entry.bytes(), entry.face_index as usize) {
Some(f) => f,
None => return false,
};
font_ref.charmap().map(ch) != 0
}
pub fn find_fallback_font(
registry: &FontRegistry,
ch: char,
exclude: FontFaceId,
) -> Option<FontFaceId> {
let covers = |entry: &crate::font::registry::FontEntry| {
swash::FontRef::from_index(entry.bytes(), entry.face_index as usize)
.is_some_and(|font_ref| font_ref.charmap().map(ch) != 0)
};
for system_pass in [false, true] {
for (face_id, entry) in registry.all_entries() {
if face_id == exclude || entry.is_system != system_pass {
continue;
}
if covers(entry) {
return Some(face_id);
}
}
}
None
}
fn resolve_weight(font_weight: Option<u32>, font_bold: Option<bool>) -> u16 {
if let Some(w) = font_weight {
return w.min(1000) as u16;
}
if font_bold == Some(true) {
return 700;
}
400 }