1use std::cell::{OnceCell, RefCell};
2use std::collections::HashMap;
3use std::rc::Rc;
4
5use crate::logging::{LogArea, LogEventLevel, Logger};
6use crate::media::composite_font_family_key::CompositeFontFamilyKey;
7use crate::media::fonts::font_collection_base::compare_ordinal_ignore_case;
8use crate::media::fonts::{
9 EmbeddedFontCollection, EmptySystemFontCollection, FontCollectionBase, FontFamilyKey, IFontCollection,
10 SystemFontCollection,
11};
12use crate::media::text_formatting::unicode::Script;
13use crate::media::{
14 FontFallback, FontFamily, FontManagerOptions, FontStretch, FontStyle, FontWeight, GlyphTypeface, Typeface,
15};
16use crate::platform::IFontManagerImpl;
17use crate::utilities::{CultureInfo, Uri, UriExtensions, UriKind};
18use crate::{FerroLocator, LocatorExtensions};
19
20pub struct FontManager {
24 font_collections: RefCell<HashMap<Uri, Rc<dyn IFontCollection>>>,
25 font_fallbacks: Option<Vec<FontFallback>>,
26 font_family_mappings: Option<Vec<(String, FontFamily)>>,
28 platform_impl: Rc<dyn IFontManagerImpl>,
29 default_font_family: OnceCell<FontFamily>,
30}
31
32impl PartialEq for FontManager {
35 #[inline]
36 fn eq(&self, other: &Self) -> bool {
37 std::ptr::eq(self, other)
38 }
39}
40
41impl FontManager {
42 pub const FONT_COLLECTION_SCHEME: &'static str = "fonts";
43 pub const SYSTEM_FONT_SCHEME: &'static str = "systemfont";
44 pub const COMPOSITE_FONT_SCHEME: &'static str = "compositefont";
45
46 pub fn system_fonts_key() -> Uri {
51 crate::ferro_thread_local! {
52 static SYSTEM_FONTS_KEY: Uri = Uri::try_create("fonts:SystemFonts", UriKind::Absolute)
53 .expect("the system fonts key is an absolute uri");
54 }
55
56 SYSTEM_FONTS_KEY.with(Clone::clone)
57 }
58
59 pub fn new(platform_impl: Rc<dyn IFontManagerImpl>) -> Rc<FontManager> {
65 let options = FerroLocator::current().get_service::<FontManagerOptions>();
66
67 let font_manager = FontManager {
68 font_collections: RefCell::new(HashMap::new()),
69 font_fallbacks: options.as_ref().and_then(|options| options.font_fallbacks.clone()),
70 font_family_mappings: Self::create_font_family_mappings(
71 options.as_ref().and_then(|options| options.font_family_mappings.as_ref()),
72 ),
73 platform_impl,
74 default_font_family: OnceCell::new(),
75 };
76
77 let default_font_family_name = font_manager.get_default_font_family_name(options.as_deref());
78
79 font_manager
80 .default_font_family
81 .get_or_init(|| FontFamily::new(&default_font_family_name));
82
83 Rc::new(font_manager)
84 }
85
86 fn create_font_family_mappings(
90 font_family_mappings: Option<&HashMap<String, FontFamily>>,
91 ) -> Option<Vec<(String, FontFamily)>> {
92 let font_family_mappings = font_family_mappings.filter(|mappings| !mappings.is_empty())?;
93
94 let mut entries: Vec<(&String, &FontFamily)> = font_family_mappings.iter().collect();
99
100 entries.sort_by(|a, b| a.0.cmp(b.0));
101
102 let mut mappings: Vec<(String, FontFamily)> = Vec::with_capacity(entries.len());
103
104 for (name, font_family) in entries {
105 match mappings.binary_search_by(|(existing, _)| compare_ordinal_ignore_case(existing, name)) {
106 Ok(index) => mappings[index].1 = font_family.clone(),
107 Err(index) => mappings.insert(index, (name.clone(), font_family.clone())),
108 }
109 }
110
111 Some(mappings)
112 }
113
114 fn try_get_font_family_mapping(&self, family_name: &str) -> Option<&FontFamily> {
115 let mappings = self.font_family_mappings.as_ref()?;
116
117 mappings
118 .binary_search_by(|(existing, _)| compare_ordinal_ignore_case(existing, family_name))
119 .ok()
120 .map(|index| &mappings[index].1)
121 }
122
123 pub fn current() -> Rc<FontManager> {
130 if let Some(current) = FerroLocator::current().get_service::<FontManager>() {
131 return current;
132 }
133
134 let font_manager_impl = FerroLocator::current().get_required_service::<dyn IFontManagerImpl>();
135
136 let current = FontManager::new(font_manager_impl);
137
138 FerroLocator::current_mutable().bind::<FontManager>().to_constant(current.clone());
139
140 current
141 }
142
143 pub fn default_font_family(&self) -> &FontFamily {
145 self.default_font_family.get().expect("the default font family is set by the constructor")
146 }
147
148 pub fn system_fonts(&self) -> Rc<dyn IFontCollection> {
150 if let Some(font_collection) = self.try_get_font_collection(&Self::system_fonts_key()) {
151 return font_collection;
152 }
153
154 Rc::new(EmptySystemFontCollection::new())
156 }
157
158 pub fn platform_impl(&self) -> &Rc<dyn IFontManagerImpl> {
163 &self.platform_impl
164 }
165
166 pub fn try_get_glyph_typeface(&self, typeface: &Typeface) -> Option<Rc<GlyphTypeface>> {
171 let font_family = self
172 .try_get_font_family_mapping(typeface.font_family().family_names().primary_family_name())
173 .unwrap_or(typeface.font_family());
174
175 if typeface.font_family().name() == FontFamily::DEFAULT_FONT_FAMILY_NAME {
176 return self.try_get_glyph_typeface(&self.default_typeface(typeface));
177 }
178
179 if let Some(font_family_key) = font_family.key() {
180 if let Some(composite_keys) = CompositeFontFamilyKey::keys(font_family_key) {
181 for (i, composite_key) in composite_keys.iter().enumerate() {
182 let mut key = composite_key.clone();
183
184 let mut family_name = &font_family.family_names()[i];
185
186 if let Some(mapped_font_family) = self.try_get_font_family_mapping(family_name) {
187 key = match mapped_font_family.key() {
188 Some(mapped_key) => mapped_key.clone(),
189 None => FontFamilyKey::new(Self::system_fonts_key()),
190 };
191
192 family_name = mapped_font_family.family_names().primary_family_name();
193 }
194
195 if family_name == FontFamily::DEFAULT_FONT_FAMILY_NAME {
196 return self.try_get_glyph_typeface(&self.default_typeface(typeface));
197 }
198
199 if let Some(glyph_typeface) = self.try_get_glyph_typeface_by_key_and_name(typeface, &key, family_name)
200 {
201 if glyph_typeface.family_name().contains(family_name) {
202 return Some(glyph_typeface);
203 }
204 }
205 }
206 } else {
207 let family_name = font_family.family_names().primary_family_name();
208
209 return self.try_get_glyph_typeface_by_key_and_name(typeface, font_family_key, family_name);
210 }
211 } else {
212 let family_name = font_family.family_names().primary_family_name();
213
214 if let Some(glyph_typeface) = self.system_fonts().try_get_glyph_typeface(
215 family_name,
216 typeface.style(),
217 typeface.weight(),
218 typeface.stretch(),
219 ) {
220 return Some(glyph_typeface);
221 }
222 }
223
224 if typeface.font_family() == self.default_font_family() {
225 return None;
226 }
227
228 self.try_get_glyph_typeface(&self.default_typeface(typeface))
230 }
231
232 fn default_typeface(&self, typeface: &Typeface) -> Typeface {
234 Typeface::with_style(
235 self.default_font_family().clone(),
236 typeface.style(),
237 typeface.weight(),
238 typeface.stretch(),
239 )
240 }
241
242 fn try_get_glyph_typeface_by_key_and_name(
243 &self,
244 typeface: &Typeface,
245 key: &FontFamilyKey,
246 family_name: &str,
247 ) -> Option<Rc<GlyphTypeface>> {
248 let source = key.source().ensure_absolute(key.base_uri());
249
250 let font_collection = self.try_get_font_collection(&source)?;
251
252 if let Some(glyph_typeface) =
253 font_collection.try_get_glyph_typeface(family_name, typeface.style(), typeface.weight(), typeface.stretch())
254 {
255 return Some(glyph_typeface);
256 }
257
258 if let Some(logger) = Logger::try_get(LogEventLevel::Debug, LogArea::FONTS) {
259 let present: Vec<String> =
260 font_collection.font_families().iter().map(|font_family| font_family.to_string()).collect();
261
262 logger.log(
263 Some(self),
264 &format!(
265 "Font family '{family_name}' could not be found. Present font families: [{}]",
266 present.join(",")
267 ),
268 );
269 }
270
271 None
272 }
273
274 pub fn add_font_collection(&self, font_collection: Rc<dyn IFontCollection>) {
281 let key = font_collection.key();
282
283 if !key.is_absolute_uri() || !UriExtensions::is_font_collection(&key) {
284 panic!("Font collection Key should follow the fonts: scheme.");
285 }
286
287 let old_collection = self.font_collections.borrow_mut().insert(key, font_collection);
288
289 if let Some(old_collection) = old_collection {
290 old_collection.dispose();
291 }
292 }
293
294 pub fn remove_font_collection(&self, key: &Uri) {
296 let font_collection = self.font_collections.borrow_mut().remove(key);
297
298 if let Some(font_collection) = font_collection {
299 font_collection.dispose();
300 }
301 }
302
303 pub fn try_match_character(
311 &self,
312 codepoint: i32,
313 font_style: FontStyle,
314 font_weight: FontWeight,
315 font_stretch: FontStretch,
316 font_family: Option<&FontFamily>,
317 culture: Option<&CultureInfo>,
318 ) -> Option<Typeface> {
319 self.try_match_character_with_script(
320 codepoint,
321 font_style,
322 font_weight,
323 font_stretch,
324 font_family,
325 culture,
326 Script::Unknown,
327 )
328 }
329
330 #[allow(clippy::too_many_arguments)]
335 pub(crate) fn try_match_character_with_script(
336 &self,
337 codepoint: i32,
338 font_style: FontStyle,
339 font_weight: FontWeight,
340 font_stretch: FontStretch,
341 font_family: Option<&FontFamily>,
342 culture: Option<&CultureInfo>,
343 shaping_script: Script,
344 ) -> Option<Typeface> {
345 if let Some(font_fallbacks) = &self.font_fallbacks {
346 for fallback in font_fallbacks {
347 if fallback.unicode_range.is_in_range(codepoint) {
348 let typeface =
349 Typeface::with_style(fallback.font_family.clone(), font_style, font_weight, font_stretch);
350
351 if let Some(glyph_typeface) = self.try_get_glyph_typeface(&typeface) {
352 if glyph_typeface.character_to_glyph_map().try_get_glyph(codepoint).is_some()
353 && (shaping_script == Script::Unknown || glyph_typeface.can_shape_script(shaping_script))
354 {
355 return Some(typeface);
356 }
357 }
358 }
359 }
360 }
361
362 if let Some(font_family) = font_family {
364 if let Some(font_family_key) = font_family.key() {
365 if let Some(composite_keys) = CompositeFontFamilyKey::keys(font_family_key) {
366 for (i, key) in composite_keys.iter().enumerate() {
367 let mut family_name = &font_family.family_names()[i];
368 let source = key.source().ensure_absolute(key.base_uri());
369
370 if family_name == FontFamily::DEFAULT_FONT_FAMILY_NAME {
371 family_name = self.default_font_family().name();
372 }
373
374 let Some(font_collection) = self.try_get_font_collection(&source) else {
375 continue;
376 };
377
378 if font_collection
380 .try_get_glyph_typeface(family_name, font_style, font_weight, font_stretch)
381 .is_none()
382 {
383 continue;
384 }
385
386 if let Some(typeface) = Self::try_match_character_in_collection(
387 &*font_collection,
388 codepoint,
389 font_style,
390 font_weight,
391 font_stretch,
392 Some(family_name),
393 culture,
394 shaping_script,
395 ) {
396 if typeface.font_family().name() == self.default_font_family().name()
397 && i + 1 < composite_keys.len()
398 {
399 continue;
400 }
401
402 return Some(typeface);
403 }
404 }
405 }
406
407 let font_uri = font_family_key.source().ensure_absolute(font_family_key.base_uri());
408
409 if UriExtensions::is_font_collection(&font_uri) {
410 if let Some(font_collection) = self.try_get_font_collection(&font_uri) {
411 if let Some(typeface) = Self::try_match_character_in_collection(
412 &*font_collection,
413 codepoint,
414 font_style,
415 font_weight,
416 font_stretch,
417 Some(font_family.name()),
418 culture,
419 shaping_script,
420 ) {
421 return Some(typeface);
422 }
423 }
424 }
425 }
426 }
427
428 Self::try_match_character_in_collection(
430 &*self.system_fonts(),
431 codepoint,
432 font_style,
433 font_weight,
434 font_stretch,
435 font_family.map(|font_family| font_family.name()),
436 culture,
437 shaping_script,
438 )
439 }
440
441 #[allow(clippy::too_many_arguments)]
445 fn try_match_character_in_collection(
446 font_collection: &dyn IFontCollection,
447 codepoint: i32,
448 font_style: FontStyle,
449 font_weight: FontWeight,
450 font_stretch: FontStretch,
451 family_name: Option<&str>,
452 culture: Option<&CultureInfo>,
453 shaping_script: Script,
454 ) -> Option<Typeface> {
455 if let Some(font_collection_base) = font_collection.as_font_collection_base() {
456 return FontCollectionBase::try_match_character_with_script(
457 font_collection_base,
458 codepoint,
459 font_style,
460 font_weight,
461 font_stretch,
462 family_name,
463 culture,
464 shaping_script,
465 );
466 }
467
468 font_collection.try_match_character(codepoint, font_style, font_weight, font_stretch, family_name, culture)
469 }
470
471 pub fn get_family_typefaces(&self, font_family: &FontFamily) -> Vec<Typeface> {
476 match font_family.key() {
477 None => self.system_fonts().try_get_family_typefaces(font_family.name()),
478 Some(key) => {
479 let source = key.source().ensure_absolute(key.base_uri());
480
481 self.try_get_font_collection(&source)
482 .and_then(|font_collection| font_collection.try_get_family_typefaces(font_family.name()))
483 }
484 }
485 .unwrap_or_default()
486 }
487
488 pub(crate) fn try_get_font_collection(&self, source: &Uri) -> Option<Rc<dyn IFontCollection>> {
493 debug_assert!(source.is_absolute_uri());
494
495 if source.scheme() == Self::SYSTEM_FONT_SCHEME || *source == Self::system_fonts_key() {
500 return Some(self.get_or_create_font_collection(&Self::system_fonts_key(), |_| {
501 Rc::new(SystemFontCollection::new(self.platform_impl.clone()))
502 }));
503 }
504
505 if UriExtensions::is_font_collection(source) {
508 return self.font_collections.borrow().get(source).cloned();
509 }
510
511 if UriExtensions::is_absolute_resm(source) || UriExtensions::is_asset(source) {
512 return Some(self.get_or_create_font_collection(source, |key| {
513 Rc::new(EmbeddedFontCollection::new(key.clone(), key.clone()))
514 }));
515 }
516
517 None
518 }
519
520 fn get_or_create_font_collection(
524 &self,
525 key: &Uri,
526 factory: impl FnOnce(&Uri) -> Rc<dyn IFontCollection>,
527 ) -> Rc<dyn IFontCollection> {
528 if let Some(existing) = self.font_collections.borrow().get(key) {
529 return existing.clone();
530 }
531
532 let candidate = factory(key);
534
535 let winner = self.font_collections.borrow_mut().entry(key.clone()).or_insert_with(|| candidate.clone()).clone();
536
537 if !Rc::ptr_eq(&winner, &candidate) {
538 candidate.dispose();
540 }
541
542 winner
543 }
544
545 fn get_default_font_family_name(&self, options: Option<&FontManagerOptions>) -> String {
546 let mut default_font_family_name = options
547 .and_then(|options| options.default_family_name.clone())
548 .unwrap_or_else(|| self.platform_impl.get_default_font_family_name());
549
550 if default_font_family_name.is_empty() {
551 let system_fonts = self.system_fonts();
552
553 if system_fonts.count() > 0 {
554 default_font_family_name = system_fonts.get(0).name().to_owned();
555 }
556 }
557
558 if default_font_family_name.is_empty() {
559 panic!("Default font family name can't be null or empty.");
560 }
561
562 if default_font_family_name == FontFamily::DEFAULT_FONT_FAMILY_NAME {
563 panic!(
564 "'{}' is a placeholder and cannot be used as the default font family name. Provide a concrete font family name via FontManagerOptions or the platform implementation.",
565 FontFamily::DEFAULT_FONT_FAMILY_NAME
566 );
567 }
568
569 default_font_family_name
570 }
571
572 pub fn dispose(&self) {
574 let font_collections: Vec<Rc<dyn IFontCollection>> =
575 self.font_collections.borrow_mut().drain().map(|(_, font_collection)| font_collection).collect();
576
577 for font_collection in font_collections {
578 font_collection.dispose();
579 }
580
581 self.platform_impl.dispose();
582 }
583}