1#![allow(clippy::len_without_is_empty)]
9
10use super::{FaceRef, FontSelector, Resolver};
11use crate::conv::{to_u32, to_usize};
12use fontique::{Blob, QueryStatus, Script, Synthesis};
13use std::collections::hash_map::{Entry, HashMap};
14use std::sync::{LazyLock, Mutex, MutexGuard, RwLock};
15use thiserror::Error;
16pub(crate) use ttf_parser::Face;
17
18#[derive(Error, Debug)]
20enum FontError {
21 #[error("font load error")]
22 TtfParser(#[from] ttf_parser::FaceParsingError),
23 #[cfg(feature = "ab_glyph")]
24 #[error("font load error")]
25 AbGlyph(#[from] ab_glyph::InvalidFont),
26 #[error("font load error")]
27 Swash,
28}
29
30#[derive(Error, Debug)]
35#[error("invalid FontId")]
36pub struct InvalidFontId;
37
38#[derive(Error, Debug)]
42#[error("no font match")]
43pub struct NoFontMatch;
44
45#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
49pub struct FaceId(pub(crate) u32);
50impl FaceId {
51 pub fn get(self) -> usize {
53 to_usize(self.0)
54 }
55}
56
57impl From<u32> for FaceId {
58 fn from(id: u32) -> Self {
59 FaceId(id)
60 }
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
67pub struct FontId(u32);
68impl FontId {
69 pub fn get(self) -> usize {
71 to_usize(self.0)
72 }
73}
74
75pub struct FaceStore {
77 blob: Blob<u8>,
78 index: u32,
79 face: Face<'static>,
80 #[cfg(feature = "rustybuzz")]
81 rustybuzz: rustybuzz::Face<'static>,
82 #[cfg(feature = "ab_glyph")]
83 ab_glyph: ab_glyph::FontRef<'static>,
84 swash: (u32, swash::CacheKey), synthesis: Synthesis,
86}
87
88impl FaceStore {
89 fn new(blob: Blob<u8>, index: u32, synthesis: Synthesis) -> Result<Self, FontError> {
91 let data = unsafe { extend_lifetime(blob.data()) };
95
96 let face = Face::parse(data, index)?;
97
98 Ok(FaceStore {
99 blob,
100 index,
101 #[cfg(feature = "rustybuzz")]
102 rustybuzz: {
103 use {rustybuzz::Variation, ttf_parser::Tag};
104
105 let len = synthesis.variation_settings().len();
106 debug_assert!(len <= 3);
107 let mut vars = [Variation {
108 tag: Tag(0),
109 value: 0.0,
110 }; 3];
111 for (r, (tag, value)) in vars.iter_mut().zip(synthesis.variation_settings()) {
112 r.tag = Tag::from_bytes(&tag.to_be_bytes());
113 r.value = *value;
114 }
115
116 let mut rustybuzz = rustybuzz::Face::from_face(face.clone());
117 rustybuzz.set_variations(&vars[0..len]);
118 rustybuzz
119 },
120 face,
121 #[cfg(feature = "ab_glyph")]
122 ab_glyph: {
123 let mut font = ab_glyph::FontRef::try_from_slice_and_index(data, index)?;
124 for (tag, value) in synthesis.variation_settings() {
125 ab_glyph::VariableFont::set_variation(&mut font, &tag.to_be_bytes(), *value);
126 }
127 font
128 },
129 swash: {
130 use easy_cast::Cast;
131 let f = swash::FontRef::from_index(data, index.cast()).ok_or(FontError::Swash)?;
132 (f.offset, f.key)
133 },
134 synthesis,
135 })
136 }
137
138 pub fn read_name(&self, id: u16) -> Option<String> {
147 use ttf_parser::PlatformId;
148 let name = self.face.names().get(id)?;
149
150 match name.platform_id {
153 PlatformId::Macintosh => Some(String::from_utf8_lossy(name.name).to_string()),
154 PlatformId::Unicode | PlatformId::Windows => {
156 let name: Vec<u16> = name
157 .name
158 .as_chunks()
159 .0
160 .iter()
161 .map(|chunk| u16::from_be_bytes(*chunk))
162 .collect();
163 Some(String::from_utf16_lossy(&name))
164 }
165 _ => None,
166 }
167 }
168
169 #[inline]
171 pub fn name_family(&self) -> Option<String> {
172 self.read_name(1)
173 }
174
175 #[inline]
177 pub fn name_subfamily(&self) -> Option<String> {
178 self.read_name(2)
179 }
180
181 #[inline]
183 pub fn name_full(&self) -> Option<String> {
184 self.read_name(4)
185 }
186
187 pub fn face(&self) -> &Face<'static> {
189 &self.face
190 }
191
192 pub fn face_ref(&self) -> FaceRef<'_> {
194 FaceRef(&self.face)
195 }
196
197 #[cfg(feature = "rustybuzz")]
199 pub fn rustybuzz(&self) -> &rustybuzz::Face<'static> {
200 &self.rustybuzz
201 }
202
203 #[cfg(feature = "ab_glyph")]
205 pub fn ab_glyph(&self) -> &ab_glyph::FontRef<'static> {
206 &self.ab_glyph
207 }
208
209 pub fn swash(&self) -> swash::FontRef<'_> {
211 swash::FontRef {
212 data: self.face.raw_face().data,
213 offset: self.swash.0,
214 key: self.swash.1,
215 }
216 }
217
218 pub fn synthesis(&self) -> &Synthesis {
220 &self.synthesis
221 }
222}
223
224#[derive(Default)]
225struct FaceList {
226 #[allow(clippy::vec_box)]
229 faces: Vec<Box<FaceStore>>,
230 source_hash: Vec<(u64, FaceId)>,
232}
233
234impl FaceList {
235 fn push(&mut self, face: Box<FaceStore>, source_hash: u64) -> FaceId {
236 let id = FaceId(to_u32(self.faces.len()));
237 self.faces.push(face);
238 self.source_hash.push((source_hash, id));
239 id
240 }
241}
242
243#[derive(Default)]
244struct FontList {
245 fonts: Vec<(FontId, Vec<FaceId>, HashMap<char, Option<FaceId>>)>,
247 sel_hash: Vec<(u64, FontId)>,
248}
249
250impl FontList {
251 fn push(&mut self, list: Vec<FaceId>, sel_hash: u64) -> FontId {
252 let id = FontId(to_u32(self.fonts.len()));
253 self.fonts.push((id, list, HashMap::new()));
254 self.sel_hash.push((sel_hash, id));
255 id
256 }
257}
258
259pub struct FontLibrary {
264 resolver: Mutex<Resolver>,
265 faces: RwLock<FaceList>,
266 fonts: RwLock<FontList>,
267}
268
269impl FontLibrary {
271 pub fn resolver(&self) -> MutexGuard<'_, Resolver> {
273 self.resolver.lock().unwrap()
274 }
275
276 pub fn first_face_for(&self, font_id: FontId) -> Result<FaceId, InvalidFontId> {
281 let fonts = self.fonts.read().unwrap();
282 for (id, list, _) in &fonts.fonts {
283 if *id == font_id {
284 return Ok(*list.first().unwrap());
285 }
286 }
287 Err(InvalidFontId)
288 }
289
290 #[inline]
294 pub fn get_first_face(&self, font_id: FontId) -> Result<FaceRef<'_>, InvalidFontId> {
295 let face_id = self.first_face_for(font_id)?;
296 Ok(self.get_face(face_id))
297 }
298
299 pub fn contains_face(&self, font_id: FontId, face_id: FaceId) -> Result<bool, InvalidFontId> {
301 let fonts = self.fonts.read().unwrap();
302 for (id, list, _) in &fonts.fonts {
303 if *id == font_id {
304 return Ok(list.contains(&face_id));
305 }
306 }
307 Err(InvalidFontId)
308 }
309
310 pub fn face_for_char(
318 &self,
319 font_id: FontId,
320 last_face_id: Option<FaceId>,
321 c: char,
322 ) -> Result<Option<FaceId>, InvalidFontId> {
323 let mut fonts = self.fonts.write().unwrap();
329 let font = fonts
330 .fonts
331 .iter_mut()
332 .find(|item| item.0 == font_id)
333 .ok_or(InvalidFontId)?;
334
335 let faces = self.faces.read().unwrap();
336
337 if let Some(face_id) = last_face_id
338 && font.1.contains(&face_id)
339 {
340 let face = &faces.faces[face_id.get()];
341 if face.face.glyph_index(c).is_some() {
343 return Ok(Some(face_id));
344 }
345 }
346
347 Ok(match font.2.entry(c) {
348 Entry::Occupied(entry) => *entry.get(),
349 Entry::Vacant(entry) => {
350 let mut id: Option<FaceId> = None;
351 for face_id in font.1.iter() {
352 let face = &faces.faces[face_id.get()];
353 if face.face.glyph_index(c).is_some() {
354 id = Some(*face_id);
355 break;
356 }
357 }
358
359 entry.insert(id);
363 id
364 }
365 })
366 }
367
368 pub fn select_font(
373 &self,
374 selector: &FontSelector,
375 script: Script,
376 ) -> Result<FontId, NoFontMatch> {
377 let sel_hash = {
378 use std::collections::hash_map::DefaultHasher;
379 use std::hash::{Hash, Hasher};
380
381 let mut s = DefaultHasher::new();
382 selector.hash(&mut s);
383 script.hash(&mut s);
384 s.finish()
385 };
386
387 let fonts = self.fonts.read().unwrap();
388 for (h, id) in &fonts.sel_hash {
389 if *h == sel_hash {
390 return Ok(*id);
391 }
392 }
393 drop(fonts);
394
395 let mut faces = Vec::new();
396 let mut families = Vec::new();
397 let mut resolver = self.resolver.lock().unwrap();
398 let mut face_list = self.faces.write().unwrap();
399
400 selector.select(&mut resolver, script, |qf| {
401 if log::log_enabled!(log::Level::Debug) {
402 families.push(qf.family);
403 }
404
405 let source_hash = {
406 use std::hash::{DefaultHasher, Hash, Hasher};
407
408 let mut hasher = DefaultHasher::new();
409 qf.blob.id().hash(&mut hasher);
410 hasher.write_u32(qf.index);
411 for var in qf.synthesis.variation_settings() {
413 var.0.hash(&mut hasher);
414 }
415 qf.synthesis.embolden().hash(&mut hasher);
416 qf.synthesis.skew().is_some().hash(&mut hasher);
417 hasher.finish()
418 };
419
420 for (h, id) in face_list.source_hash.iter().cloned() {
421 if h == source_hash {
422 let face = &face_list.faces[id.get()];
423 if face.blob.id() == qf.blob.id()
424 && face.index == qf.index
425 && face.synthesis == qf.synthesis
426 {
427 faces.push(id);
428 return QueryStatus::Continue;
429 }
430 }
431 }
432
433 match FaceStore::new(qf.blob.clone(), qf.index, qf.synthesis) {
434 Ok(store) => {
435 let id = face_list.push(Box::new(store), source_hash);
436 faces.push(id);
437 }
438 Err(err) => {
439 log::error!("Failed to load font: {err}");
440 }
441 }
442
443 QueryStatus::Continue
444 });
445
446 for family in families {
447 if let Some(name) = resolver.font_family(family.0) {
448 log::debug!("match: {name}");
449 }
450 }
451
452 if faces.is_empty() {
453 return Err(NoFontMatch);
454 }
455 let font = self.fonts.write().unwrap().push(faces, sel_hash);
456 Ok(font)
457 }
458}
459
460impl FontLibrary {
462 pub fn get_face(&self, id: FaceId) -> FaceRef<'static> {
466 self.get_face_store(id).face_ref()
467 }
468
469 pub fn get_face_store(&self, id: FaceId) -> &'static FaceStore {
473 let faces = self.faces.read().unwrap();
474 assert!(id.get() < faces.faces.len(), "FontLibrary: invalid {id:?}!",);
475 let faces: &FaceStore = &faces.faces[id.get()];
476 unsafe { extend_lifetime(faces) }
478 }
479}
480
481pub(crate) unsafe fn extend_lifetime<'b, T: ?Sized>(r: &'b T) -> &'static T {
482 unsafe { std::mem::transmute::<&'b T, &'static T>(r) }
483}
484
485static LIBRARY: LazyLock<FontLibrary> = LazyLock::new(|| FontLibrary {
486 resolver: Mutex::new(Resolver::new()),
487 faces: Default::default(),
488 fonts: Default::default(),
489});
490
491pub fn library() -> &'static FontLibrary {
493 &LIBRARY
494}