text_typeset/font/
resolve.rs1use crate::font::registry::FontRegistry;
2use crate::types::FontFaceId;
3
4pub 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 pub scale_factor: f32,
15 pub weight: u16,
20}
21
22#[allow(clippy::too_many_arguments)]
30pub fn resolve_font(
31 registry: &FontRegistry,
32 font_family: Option<&str>,
33 font_weight: Option<u32>,
34 font_bold: Option<bool>,
35 font_italic: Option<bool>,
36 font_point_size: Option<u32>,
37 scale_factor: f32,
38 font_scale: f32,
39) -> Option<ResolvedFont> {
40 let weight = resolve_weight(font_weight, font_bold);
41 let italic = font_italic.unwrap_or(false);
42 let size_px = font_point_size
47 .map(|s| s as f32)
48 .unwrap_or(registry.default_size_px())
49 * font_scale;
50
51 if let Some(family) = font_family {
53 if let Some(face_id) = registry.query_font(family, weight, italic) {
54 let entry = registry.get(face_id)?;
55 return Some(ResolvedFont {
56 font_face_id: face_id,
57 size_px,
58 face_index: entry.face_index,
59 swash_cache_key: entry.swash_cache_key,
60 scale_factor,
61 weight,
62 });
63 }
64 warn_unknown_family(family);
72 }
73
74 let default_id = registry.default_font()?;
77 if (weight != 400 || italic)
78 && let Some(variant_id) = registry.query_variant(default_id, weight, italic)
79 {
80 let variant_entry = registry.get(variant_id)?;
81 return Some(ResolvedFont {
82 font_face_id: variant_id,
83 size_px,
84 face_index: variant_entry.face_index,
85 swash_cache_key: variant_entry.swash_cache_key,
86 scale_factor,
87 weight,
88 });
89 }
90 let entry = registry.get(default_id)?;
91 Some(ResolvedFont {
92 font_face_id: default_id,
93 size_px,
94 face_index: entry.face_index,
95 swash_cache_key: entry.swash_cache_key,
96 scale_factor,
97 weight,
98 })
99}
100
101fn warn_unknown_family(family: &str) {
104 use std::collections::HashSet;
105 use std::sync::{LazyLock, Mutex};
106 static WARNED: LazyLock<Mutex<HashSet<String>>> = LazyLock::new(|| Mutex::new(HashSet::new()));
107 if let Ok(mut warned) = WARNED.lock()
108 && warned.insert(family.to_string())
109 {
110 eprintln!(
111 "text-typeset: font family {family:?} is not registered; falling back \
112 to the default font. Register it (e.g. via the host application's font \
113 registrar) before shaping."
114 );
115 }
116}
117
118pub fn font_has_glyph(registry: &FontRegistry, face_id: FontFaceId, ch: char) -> bool {
122 let entry = match registry.get(face_id) {
123 Some(e) => e,
124 None => return false,
125 };
126 let font_ref = match swash::FontRef::from_index(entry.bytes(), entry.face_index as usize) {
127 Some(f) => f,
128 None => return false,
129 };
130 font_ref.charmap().map(ch) != 0
131}
132
133pub fn find_fallback_font(
139 registry: &FontRegistry,
140 ch: char,
141 exclude: FontFaceId,
142) -> Option<FontFaceId> {
143 let covers = |entry: &crate::font::registry::FontEntry| {
144 swash::FontRef::from_index(entry.bytes(), entry.face_index as usize)
145 .is_some_and(|font_ref| font_ref.charmap().map(ch) != 0)
146 };
147
148 for system_pass in [false, true] {
150 for (face_id, entry) in registry.all_entries() {
151 if face_id == exclude || entry.is_system != system_pass {
152 continue;
153 }
154 if covers(entry) {
155 return Some(face_id);
156 }
157 }
158 }
159 None
160}
161
162fn resolve_weight(font_weight: Option<u32>, font_bold: Option<bool>) -> u16 {
164 if let Some(w) = font_weight {
165 return w.min(1000) as u16;
166 }
167 if font_bold == Some(true) {
168 return 700;
169 }
170 400 }