Skip to main content

text_typeset/font/
resolve.rs

1use crate::font::registry::FontRegistry;
2use crate::types::FontFaceId;
3
4/// A resolved font face with all parameters needed for shaping and rasterization.
5pub struct ResolvedFont {
6    pub font_face_id: FontFaceId,
7    pub size_px: f32,
8    pub face_index: u32,
9    pub swash_cache_key: swash::CacheKey,
10}
11
12/// Resolve a font from text formatting parameters.
13///
14/// The resolution order is:
15/// 1. If font_family is set, query by family name (with generic mapping)
16/// 2. Apply font_weight (or font_bold as weight 700)
17/// 3. Apply font_italic
18/// 4. Fall back to the default font if no match
19pub fn resolve_font(
20    registry: &FontRegistry,
21    font_family: Option<&str>,
22    font_weight: Option<u32>,
23    font_bold: Option<bool>,
24    font_italic: Option<bool>,
25    font_point_size: Option<u32>,
26) -> Option<ResolvedFont> {
27    let weight = resolve_weight(font_weight, font_bold);
28    let italic = font_italic.unwrap_or(false);
29    let size_px = font_point_size
30        .map(|s| s as f32)
31        .unwrap_or(registry.default_size_px());
32
33    // Try the specified family first
34    if let Some(family) = font_family
35        && let Some(face_id) = registry.query_font(family, weight, italic)
36    {
37        let entry = registry.get(face_id)?;
38        return Some(ResolvedFont {
39            font_face_id: face_id,
40            size_px,
41            face_index: entry.face_index,
42            swash_cache_key: entry.swash_cache_key,
43        });
44    }
45
46    // Fall back to default font
47    let default_id = registry.default_font()?;
48    let entry = registry.get(default_id)?;
49    Some(ResolvedFont {
50        font_face_id: default_id,
51        size_px,
52        face_index: entry.face_index,
53        swash_cache_key: entry.swash_cache_key,
54    })
55}
56
57/// Check if a font has a glyph for the given character.
58/// Used for glyph fallback -trying other registered fonts when
59/// the primary font doesn't cover a character.
60pub fn font_has_glyph(registry: &FontRegistry, face_id: FontFaceId, ch: char) -> bool {
61    let entry = match registry.get(face_id) {
62        Some(e) => e,
63        None => return false,
64    };
65    let font_ref = match swash::FontRef::from_index(&entry.data, entry.face_index as usize) {
66        Some(f) => f,
67        None => return false,
68    };
69    font_ref.charmap().map(ch) != 0
70}
71
72/// Find a fallback font that has the given character.
73pub fn find_fallback_font(
74    registry: &FontRegistry,
75    ch: char,
76    exclude: FontFaceId,
77) -> Option<FontFaceId> {
78    for (face_id, entry) in registry.all_entries() {
79        if face_id == exclude {
80            continue;
81        }
82        let font_ref = match swash::FontRef::from_index(&entry.data, entry.face_index as usize) {
83            Some(f) => f,
84            None => continue,
85        };
86        if font_ref.charmap().map(ch) != 0 {
87            return Some(face_id);
88        }
89    }
90    None
91}
92
93/// Convert TextFormat weight fields to a u16 weight value for fontdb.
94fn resolve_weight(font_weight: Option<u32>, font_bold: Option<bool>) -> u16 {
95    if let Some(w) = font_weight {
96        return w.min(1000) as u16;
97    }
98    if font_bold == Some(true) {
99        return 700;
100    }
101    400 // Normal weight
102}