1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::sync::Arc;
4use std::sync::OnceLock;
5
6use fontdb::{Database, Family, Query, Source, Style, Weight};
7use harfrust::{FontRef, ShaperData};
8
9use crate::types::FontFaceId;
10
11pub type SharedFontData = Arc<dyn AsRef<[u8]> + Sync + Send>;
17
18enum FontData {
27 Resident(SharedFontData),
28 Lazy {
29 path: PathBuf,
30 cell: OnceLock<SharedFontData>,
31 },
32}
33
34pub struct FontEntry {
35 pub fontdb_id: fontdb::ID,
36 pub face_index: u32,
37 data: FontData,
38 pub swash_cache_key: swash::CacheKey,
39 pub is_system: bool,
44 shaper_data: OnceLock<ShaperData>,
51}
52
53impl FontEntry {
54 pub fn bytes(&self) -> &[u8] {
59 match &self.data {
60 FontData::Resident(data) => (**data).as_ref(),
62 FontData::Lazy { path, cell } => {
63 let arc = cell.get_or_init(|| {
64 let bytes = std::fs::read(path).unwrap_or_default();
65 Arc::new(bytes) as SharedFontData
66 });
67 (**arc).as_ref()
68 }
69 }
70 }
71
72 pub fn shaper_data(&self, font: &FontRef) -> &ShaperData {
79 self.shaper_data.get_or_init(|| ShaperData::new(font))
80 }
81}
82
83pub struct FontRegistry {
84 fontdb: Database,
85 fonts: Vec<Option<FontEntry>>,
86 fontdb_index: HashMap<fontdb::ID, FontFaceId>,
89 generic_families: HashMap<String, String>,
90 default_font: Option<FontFaceId>,
91 default_size_px: f32,
92}
93
94impl Default for FontRegistry {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100impl FontRegistry {
101 pub fn new() -> Self {
111 let mut registry = Self::new_without_system_fonts();
112 registry.load_system_fonts();
113 registry
114 }
115
116 pub fn new_without_system_fonts() -> Self {
119 Self {
120 fontdb: Database::new(),
121 fonts: Vec::new(),
122 fontdb_index: HashMap::new(),
123 generic_families: HashMap::new(),
124 default_font: None,
125 default_size_px: 16.0,
126 }
127 }
128
129 fn load_system_fonts(&mut self) {
133 self.fontdb.load_system_fonts();
134
135 let discovered: Vec<(fontdb::ID, u32, PathBuf)> = self
138 .fontdb
139 .faces()
140 .filter_map(|f| match &f.source {
141 Source::File(path) => Some((f.id, f.index, path.clone())),
142 Source::SharedFile(path, _) => Some((f.id, f.index, path.clone())),
143 Source::Binary(_) => None,
144 })
145 .collect();
146
147 for (fontdb_id, face_index, path) in discovered {
148 if self.fontdb_index.contains_key(&fontdb_id) {
149 continue;
150 }
151 let entry = FontEntry {
152 fontdb_id,
153 face_index,
154 data: FontData::Lazy {
155 path,
156 cell: OnceLock::new(),
157 },
158 swash_cache_key: swash::CacheKey::new(),
159 is_system: true,
160 shaper_data: OnceLock::new(),
161 };
162 let face_id = FontFaceId(self.fonts.len() as u32);
163 self.fonts.push(Some(entry));
164 self.fontdb_index.insert(fontdb_id, face_id);
165 }
166 }
167
168 pub fn register_font(&mut self, data: &[u8]) -> Vec<FontFaceId> {
177 let arc_data: SharedFontData = Arc::new(data.to_vec());
178 self.register_font_shared(arc_data)
179 }
180
181 pub fn register_font_shared(&mut self, data: SharedFontData) -> Vec<FontFaceId> {
189 let source = Source::Binary(data.clone());
190 let fontdb_ids = self.fontdb.load_font_source(source);
191
192 let mut face_ids = Vec::new();
193 for fontdb_id in fontdb_ids {
194 let face_index = self.fontdb.face(fontdb_id).map(|f| f.index).unwrap_or(0);
195
196 let swash_cache_key = swash::CacheKey::new();
197 let entry = FontEntry {
198 fontdb_id,
199 face_index,
200 data: FontData::Resident(data.clone()),
201 swash_cache_key,
202 is_system: false,
203 shaper_data: OnceLock::new(),
204 };
205
206 let face_id = FontFaceId(self.fonts.len() as u32);
207 self.fonts.push(Some(entry));
208 self.fontdb_index.insert(fontdb_id, face_id);
209 face_ids.push(face_id);
210 }
211 face_ids
212 }
213
214 pub fn register_font_as(
216 &mut self,
217 data: &[u8],
218 family: &str,
219 weight: u16,
220 italic: bool,
221 ) -> Vec<FontFaceId> {
222 let arc_data: SharedFontData = Arc::new(data.to_vec());
223 self.register_font_shared_as(arc_data, family, weight, italic)
224 }
225
226 pub fn register_font_shared_as(
229 &mut self,
230 data: SharedFontData,
231 family: &str,
232 weight: u16,
233 italic: bool,
234 ) -> Vec<FontFaceId> {
235 let source = Source::Binary(data.clone());
236 let fontdb_ids = self.fontdb.load_font_source(source);
237
238 let mut face_ids = Vec::new();
239 for fontdb_id in fontdb_ids {
240 if let Some(face_info) = self.fontdb.face(fontdb_id) {
242 let mut info = face_info.clone();
243 info.families = vec![(family.to_string(), fontdb::Language::English_UnitedStates)];
244 info.weight = Weight(weight);
245 info.style = if italic { Style::Italic } else { Style::Normal };
246 let face_index = info.index;
248 self.fontdb.remove_face(fontdb_id);
249 let new_id = self.fontdb.push_face_info(info);
250
251 let swash_cache_key = swash::CacheKey::new();
252 let entry = FontEntry {
253 fontdb_id: new_id,
254 face_index,
255 data: FontData::Resident(data.clone()),
256 swash_cache_key,
257 is_system: false,
258 shaper_data: OnceLock::new(),
259 };
260
261 let face_id = FontFaceId(self.fonts.len() as u32);
262 self.fonts.push(Some(entry));
263 self.fontdb_index.insert(new_id, face_id);
264 face_ids.push(face_id);
265 }
266 }
267 face_ids
268 }
269
270 pub fn set_default_font(&mut self, face: FontFaceId, size_px: f32) {
271 self.default_font = Some(face);
272 self.default_size_px = size_px;
273 }
274
275 pub fn default_font(&self) -> Option<FontFaceId> {
276 self.default_font
277 }
278
279 pub fn default_size_px(&self) -> f32 {
280 self.default_size_px
281 }
282
283 pub fn set_generic_family(&mut self, generic: &str, family: &str) {
284 self.generic_families
285 .insert(generic.to_string(), family.to_string());
286 }
287
288 pub fn resolve_family_name<'a>(&'a self, family: &'a str) -> &'a str {
291 self.generic_families
292 .get(family)
293 .map(|s| s.as_str())
294 .unwrap_or(family)
295 }
296
297 pub fn query_font(&self, family: &str, weight: u16, italic: bool) -> Option<FontFaceId> {
299 let resolved = self.resolve_family_name(family);
300 let style = if italic { Style::Italic } else { Style::Normal };
301
302 let query = Query {
303 families: &[Family::Name(resolved)],
304 weight: Weight(weight),
305 style,
306 ..Query::default()
307 };
308
309 let fontdb_id = self.fontdb.query(&query)?;
310 self.fontdb_id_to_face_id(fontdb_id)
311 }
312
313 pub fn font_family_name(&self, face_id: FontFaceId) -> Option<String> {
315 let entry = self.get(face_id)?;
316 let face_info = self.fontdb.face(entry.fontdb_id)?;
317 face_info.families.first().map(|(name, _)| name.clone())
318 }
319
320 pub fn get(&self, face_id: FontFaceId) -> Option<&FontEntry> {
322 self.fonts
323 .get(face_id.0 as usize)
324 .and_then(|opt| opt.as_ref())
325 }
326
327 pub fn query_variant(
330 &self,
331 base_face: FontFaceId,
332 weight: u16,
333 italic: bool,
334 ) -> Option<FontFaceId> {
335 let entry = self.get(base_face)?;
336 let face_info = self.fontdb.face(entry.fontdb_id)?;
337 let family_name = face_info.families.first().map(|(name, _)| name.as_str())?;
338 self.query_font(family_name, weight, italic)
339 }
340
341 fn fontdb_id_to_face_id(&self, fontdb_id: fontdb::ID) -> Option<FontFaceId> {
343 self.fontdb_index.get(&fontdb_id).copied()
344 }
345
346 pub fn all_entries(&self) -> impl Iterator<Item = (FontFaceId, &FontEntry)> {
348 self.fonts
349 .iter()
350 .enumerate()
351 .filter_map(|(i, opt)| opt.as_ref().map(|entry| (FontFaceId(i as u32), entry)))
352 }
353}