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> {
93 let data = unsafe { extend_lifetime(blob.data()) };
97
98 let face = Face::parse(data, index)?;
99
100 Ok(FaceStore {
101 blob,
102 index,
103 #[cfg(feature = "rustybuzz")]
104 rustybuzz: {
105 use {rustybuzz::Variation, ttf_parser::Tag};
106
107 let len = synthesis.variation_settings().len();
108 debug_assert!(len <= 3);
109 let mut vars = [Variation {
110 tag: Tag(0),
111 value: 0.0,
112 }; 3];
113 for (r, (tag, value)) in vars.iter_mut().zip(synthesis.variation_settings()) {
114 r.tag = Tag::from_bytes(&tag.to_be_bytes());
115 r.value = *value;
116 }
117
118 let mut rustybuzz = rustybuzz::Face::from_face(face.clone());
119 rustybuzz.set_variations(&vars[0..len]);
120 rustybuzz
121 },
122 face,
123 #[cfg(feature = "ab_glyph")]
124 ab_glyph: {
125 let mut font = ab_glyph::FontRef::try_from_slice_and_index(data, index)?;
126 for (tag, value) in synthesis.variation_settings() {
127 ab_glyph::VariableFont::set_variation(&mut font, &tag.to_be_bytes(), *value);
128 }
129 font
130 },
131 swash: {
132 use easy_cast::Cast;
133 let f = swash::FontRef::from_index(data, index.cast()).ok_or(FontError::Swash)?;
134 (f.offset, f.key)
135 },
136 synthesis,
137 })
138 }
139
140 pub fn face(&self) -> &Face<'static> {
142 &self.face
143 }
144
145 pub fn face_ref(&self) -> FaceRef<'_> {
147 FaceRef(&self.face)
148 }
149
150 #[cfg(feature = "rustybuzz")]
152 pub fn rustybuzz(&self) -> &rustybuzz::Face<'static> {
153 &self.rustybuzz
154 }
155
156 #[cfg(feature = "ab_glyph")]
158 pub fn ab_glyph(&self) -> &ab_glyph::FontRef<'static> {
159 &self.ab_glyph
160 }
161
162 pub fn swash(&self) -> swash::FontRef<'_> {
164 swash::FontRef {
165 data: self.face.raw_face().data,
166 offset: self.swash.0,
167 key: self.swash.1,
168 }
169 }
170
171 pub fn synthesis(&self) -> &Synthesis {
173 &self.synthesis
174 }
175}
176
177#[derive(Default)]
178struct FaceList {
179 #[allow(clippy::vec_box)]
182 faces: Vec<Box<FaceStore>>,
183 source_hash: Vec<(u64, FaceId)>,
185}
186
187impl FaceList {
188 fn push(&mut self, face: Box<FaceStore>, source_hash: u64) -> FaceId {
189 let id = FaceId(to_u32(self.faces.len()));
190 self.faces.push(face);
191 self.source_hash.push((source_hash, id));
192 id
193 }
194}
195
196#[derive(Default)]
197struct FontList {
198 fonts: Vec<(FontId, Vec<FaceId>, HashMap<char, Option<FaceId>>)>,
200 sel_hash: Vec<(u64, FontId)>,
201}
202
203impl FontList {
204 fn push(&mut self, list: Vec<FaceId>, sel_hash: u64) -> FontId {
205 let id = FontId(to_u32(self.fonts.len()));
206 self.fonts.push((id, list, HashMap::new()));
207 self.sel_hash.push((sel_hash, id));
208 id
209 }
210}
211
212pub struct FontLibrary {
217 resolver: Mutex<Resolver>,
218 faces: RwLock<FaceList>,
219 fonts: RwLock<FontList>,
220}
221
222impl FontLibrary {
224 pub fn resolver(&self) -> MutexGuard<'_, Resolver> {
226 self.resolver.lock().unwrap()
227 }
228
229 pub fn first_face_for(&self, font_id: FontId) -> Result<FaceId, InvalidFontId> {
234 let fonts = self.fonts.read().unwrap();
235 for (id, list, _) in &fonts.fonts {
236 if *id == font_id {
237 return Ok(*list.first().unwrap());
238 }
239 }
240 Err(InvalidFontId)
241 }
242
243 #[inline]
247 pub fn get_first_face(&self, font_id: FontId) -> Result<FaceRef<'_>, InvalidFontId> {
248 let face_id = self.first_face_for(font_id)?;
249 Ok(self.get_face(face_id))
250 }
251
252 pub fn contains_face(&self, font_id: FontId, face_id: FaceId) -> Result<bool, InvalidFontId> {
254 let fonts = self.fonts.read().unwrap();
255 for (id, list, _) in &fonts.fonts {
256 if *id == font_id {
257 return Ok(list.contains(&face_id));
258 }
259 }
260 Err(InvalidFontId)
261 }
262
263 pub fn face_for_char(
273 &self,
274 font_id: FontId,
275 last_face_id: Option<FaceId>,
276 c: char,
277 ) -> Result<Option<FaceId>, InvalidFontId> {
278 let mut fonts = self.fonts.write().unwrap();
284 let font = fonts
285 .fonts
286 .iter_mut()
287 .find(|item| item.0 == font_id)
288 .ok_or(InvalidFontId)?;
289
290 let faces = self.faces.read().unwrap();
291
292 if let Some(face_id) = last_face_id {
293 if font.1.contains(&face_id) {
294 let face = &faces.faces[face_id.get()];
295 if face.face.glyph_index(c).is_some() {
297 return Ok(Some(face_id));
298 }
299 }
300 }
301
302 Ok(match font.2.entry(c) {
303 Entry::Occupied(entry) => *entry.get(),
304 Entry::Vacant(entry) => {
305 let mut id: Option<FaceId> = None;
306 for face_id in font.1.iter() {
307 let face = &faces.faces[face_id.get()];
308 if face.face.glyph_index(c).is_some() {
309 id = Some(*face_id);
310 break;
311 }
312 }
313
314 if id.is_none() {
318 id = font.1.first().map(|id| *id);
319 }
320
321 entry.insert(id);
322 id
323 }
324 })
325 }
326
327 pub fn select_font(
332 &self,
333 selector: &FontSelector,
334 script: Script,
335 ) -> Result<FontId, NoFontMatch> {
336 let sel_hash = {
337 use std::collections::hash_map::DefaultHasher;
338 use std::hash::{Hash, Hasher};
339
340 let mut s = DefaultHasher::new();
341 selector.hash(&mut s);
342 script.hash(&mut s);
343 s.finish()
344 };
345
346 let fonts = self.fonts.read().unwrap();
347 for (h, id) in &fonts.sel_hash {
348 if *h == sel_hash {
349 return Ok(*id);
350 }
351 }
352 drop(fonts);
353
354 let mut faces = Vec::new();
355 let mut families = Vec::new();
356 let mut resolver = self.resolver.lock().unwrap();
357 let mut face_list = self.faces.write().unwrap();
358
359 selector.select(&mut resolver, script, |qf| {
360 if log::log_enabled!(log::Level::Debug) {
361 families.push(qf.family);
362 }
363
364 let source_hash = {
365 use std::hash::{DefaultHasher, Hash, Hasher};
366
367 let mut hasher = DefaultHasher::new();
368 qf.blob.id().hash(&mut hasher);
369 hasher.write_u32(qf.index);
370 hasher.finish()
371 };
372
373 for (h, id) in face_list.source_hash.iter().cloned() {
374 if h == source_hash {
375 let face = &face_list.faces[id.get()];
376 if face.blob.id() == qf.blob.id() && face.index == qf.index {
377 faces.push(id);
378 return QueryStatus::Continue;
379 }
380 }
381 }
382
383 match FaceStore::new(qf.blob.clone(), qf.index, qf.synthesis) {
384 Ok(store) => {
385 let id = face_list.push(Box::new(store), source_hash);
386 faces.push(id);
387 }
388 Err(err) => {
389 log::error!("Failed to load font: {err}");
390 }
391 }
392
393 QueryStatus::Continue
394 });
395
396 for family in families {
397 if let Some(name) = resolver.font_family(family.0) {
398 log::debug!("match: {name}");
399 }
400 }
401
402 if faces.is_empty() {
403 return Err(NoFontMatch);
404 }
405 let font = self.fonts.write().unwrap().push(faces, sel_hash);
406 Ok(font)
407 }
408}
409
410impl FontLibrary {
412 pub fn get_face(&self, id: FaceId) -> FaceRef<'static> {
416 self.get_face_store(id).face_ref()
417 }
418
419 pub fn get_face_store(&self, id: FaceId) -> &'static FaceStore {
423 let faces = self.faces.read().unwrap();
424 assert!(id.get() < faces.faces.len(), "FontLibrary: invalid {id:?}!",);
425 let faces: &FaceStore = &faces.faces[id.get()];
426 unsafe { extend_lifetime(faces) }
428 }
429}
430
431pub(crate) unsafe fn extend_lifetime<'b, T: ?Sized>(r: &'b T) -> &'static T {
432 std::mem::transmute::<&'b T, &'static T>(r)
433}
434
435static LIBRARY: LazyLock<FontLibrary> = LazyLock::new(|| FontLibrary {
436 resolver: Mutex::new(Resolver::new()),
437 faces: Default::default(),
438 fonts: Default::default(),
439});
440
441pub fn library() -> &'static FontLibrary {
443 &LIBRARY
444}