1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![expect(clippy::type_complexity)]
11#![warn(unused_extern_crates)]
12#![warn(missing_docs)]
13
14use font_features::RFontVariations;
15use hashbrown::{HashMap, HashSet};
16use std::{
17 borrow::Cow,
18 fmt, ops,
19 path::{Path, PathBuf},
20 slice::SliceIndex,
21 sync::Arc,
22};
23
24#[macro_use]
25extern crate bitflags;
26
27pub mod font_features;
28
29mod query_util;
30
31mod emoji_util;
32pub use emoji_util::*;
33
34mod ligature_util;
35use ligature_util::*;
36
37mod unicode_bidi_util;
38
39mod segmenting;
40pub use segmenting::*;
41
42mod shaping;
43pub use shaping::*;
44use zng_clone_move::{async_clmv, clmv};
45
46mod hyphenation;
47pub use self::hyphenation::*;
48
49mod unit;
50pub use unit::*;
51
52use parking_lot::{Mutex, RwLock};
53use pastey::paste;
54use zng_app::{
55 AppExtension,
56 event::{event, event_args},
57 render::FontSynthesis,
58 update::{EventUpdate, UPDATES},
59 view_process::{
60 VIEW_PROCESS_INITED_EVENT, ViewRenderer,
61 raw_events::{RAW_FONT_AA_CHANGED_EVENT, RAW_FONT_CHANGED_EVENT},
62 },
63};
64use zng_app_context::app_local;
65use zng_ext_l10n::{Lang, LangMap, lang};
66use zng_layout::unit::{
67 ByteUnits as _, EQ_GRANULARITY, EQ_GRANULARITY_100, Factor, FactorPercent, Px, PxPoint, PxRect, PxSize, TimeUnits as _, about_eq,
68 about_eq_hash, about_eq_ord, euclid,
69};
70use zng_task as task;
71use zng_txt::Txt;
72use zng_var::{
73 IntoVar, ResponderVar, ResponseVar, Var, animation::Transitionable, const_var, impl_from_and_into_var, response_done_var, response_var,
74 var,
75};
76use zng_view_api::{config::FontAntiAliasing, font::IpcFontBytes, ipc::IpcBytes};
77
78#[derive(Clone)]
86pub struct FontName {
87 txt: Txt,
88 is_ascii: bool,
89}
90impl fmt::Debug for FontName {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 if f.alternate() {
93 f.debug_struct("FontName")
94 .field("txt", &self.txt)
95 .field("is_ascii", &self.is_ascii)
96 .finish()
97 } else {
98 write!(f, "{:?}", self.txt)
99 }
100 }
101}
102impl PartialEq for FontName {
103 fn eq(&self, other: &Self) -> bool {
104 self.unicase() == other.unicase()
105 }
106}
107impl Eq for FontName {}
108impl PartialEq<str> for FontName {
109 fn eq(&self, other: &str) -> bool {
110 self.unicase() == unicase::UniCase::<&str>::from(other)
111 }
112}
113impl std::hash::Hash for FontName {
114 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
115 std::hash::Hash::hash(&self.unicase(), state)
116 }
117}
118impl Ord for FontName {
119 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120 if self == other {
121 return std::cmp::Ordering::Equal;
123 }
124 self.txt.cmp(&other.txt)
125 }
126}
127impl PartialOrd for FontName {
128 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
129 Some(self.cmp(other))
130 }
131}
132impl FontName {
133 fn unicase(&self) -> unicase::UniCase<&str> {
134 if self.is_ascii {
135 unicase::UniCase::ascii(self)
136 } else {
137 unicase::UniCase::unicode(self)
138 }
139 }
140
141 pub const fn from_static(name: &'static str) -> Self {
143 FontName {
144 txt: Txt::from_static(name),
145 is_ascii: {
146 let name_bytes = name.as_bytes();
148 let mut i = name_bytes.len();
149 let mut is_ascii = true;
150 while i > 0 {
151 i -= 1;
152 if !name_bytes[i].is_ascii() {
153 is_ascii = false;
154 break;
155 }
156 }
157 is_ascii
158 },
159 }
160 }
161
162 pub fn new(name: impl Into<Txt>) -> Self {
171 let txt = name.into();
172 FontName {
173 is_ascii: txt.is_ascii(),
174 txt,
175 }
176 }
177
178 pub fn serif() -> Self {
182 Self::new("serif")
183 }
184
185 pub fn sans_serif() -> Self {
190 Self::new("sans-serif")
191 }
192
193 pub fn monospace() -> Self {
197 Self::new("monospace")
198 }
199
200 pub fn cursive() -> Self {
205 Self::new("cursive")
206 }
207
208 pub fn fantasy() -> Self {
212 Self::new("fantasy")
213 }
214
215 pub fn name(&self) -> &str {
217 &self.txt
218 }
219
220 pub fn into_text(self) -> Txt {
224 self.txt
225 }
226}
227impl_from_and_into_var! {
228 fn from(s: &'static str) -> FontName {
229 FontName::new(s)
230 }
231 fn from(s: String) -> FontName {
232 FontName::new(s)
233 }
234 fn from(s: Cow<'static, str>) -> FontName {
235 FontName::new(s)
236 }
237 fn from(f: FontName) -> Txt {
238 f.into_text()
239 }
240}
241impl fmt::Display for FontName {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 f.write_str(self.name())
244 }
245}
246impl std::ops::Deref for FontName {
247 type Target = str;
248
249 fn deref(&self) -> &Self::Target {
250 self.txt.deref()
251 }
252}
253impl AsRef<str> for FontName {
254 fn as_ref(&self) -> &str {
255 self.txt.as_ref()
256 }
257}
258impl serde::Serialize for FontName {
259 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
260 where
261 S: serde::Serializer,
262 {
263 self.txt.serialize(serializer)
264 }
265}
266impl<'de> serde::Deserialize<'de> for FontName {
267 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
268 where
269 D: serde::Deserializer<'de>,
270 {
271 Txt::deserialize(deserializer).map(FontName::new)
272 }
273}
274
275#[derive(Eq, PartialEq, Hash, Clone, serde::Serialize, serde::Deserialize)]
304#[serde(transparent)]
305pub struct FontNames(pub Vec<FontName>);
306impl FontNames {
307 pub fn empty() -> Self {
309 FontNames(vec![])
310 }
311
312 pub fn windows_ui(lang: &Lang) -> Self {
314 if lang!("zh-Hans").matches(lang, true, false) {
318 ["Segoe UI", "Microsoft YaHei", "Segoe Ui Emoji", "sans-serif"].into()
319 } else if lang!("zh-Hant").matches(lang, true, false) {
320 ["Segoe UI", "Microsoft Jhenghei", "Segoe Ui Emoji", "sans-serif"].into()
321 } else if lang!(ja).matches(lang, true, false) {
322 ["Segoe UI", "Yu Gothic UI", "Meiryo UI", "Segoe Ui Emoji", "sans-serif"].into()
323 } else if lang!(ko).matches(lang, true, false) {
324 ["Segoe UI", "Malgun Gothic", "Dotom", "Segoe Ui Emoji", "sans-serif"].into()
325 } else {
326 ["Segoe UI", "Segoe Ui Emoji", "sans-serif"].into()
327 }
328 }
329
330 pub fn mac_ui(lang: &Lang) -> Self {
332 if lang!("zh-Hans").matches(lang, true, false) {
335 ["PingFang SC", "Hiragino Sans GB", "Apple Color Emoji", "sans-serif"].into()
336 } else if lang!("zh-Hant").matches(lang, true, false) {
337 ["PingFang TC", "Apple Color Emoji", "sans-serif"].into()
338 } else if lang!(ja).matches(lang, true, false) {
339 ["Hiragino Kaku Gothic Pro", "Apple Color Emoji", "sans-serif"].into()
340 } else if lang!(ko).matches(lang, true, false) {
341 [
342 "Nanum Gothic",
343 "Apple SD Gothic Neo",
344 "AppleGothic",
345 "Apple Color Emoji",
346 "sans-serif",
347 ]
348 .into()
349 } else {
350 ["Neue Helvetica", "Lucida Grande", "Apple Color Emoji", "sans-serif"].into()
351 }
352 }
353
354 pub fn linux_ui(lang: &Lang) -> Self {
356 if lang!("zh-Hans").matches(lang, true, false) {
359 [
360 "Ubuntu",
361 "Droid Sans",
362 "Source Han Sans SC",
363 "Source Han Sans CN",
364 "Source Han Sans",
365 "Noto Color Emoji",
366 "sans-serif",
367 ]
368 .into()
369 } else if lang!("zh-Hant").matches(lang, true, false) {
370 [
371 "Ubuntu",
372 "Droid Sans",
373 "Source Han Sans TC",
374 "Source Han Sans TW",
375 "Source Han Sans",
376 "Noto Color Emoji",
377 "sans-serif",
378 ]
379 .into()
380 } else if lang!(ja).matches(lang, true, false) {
381 [
382 "system-ui",
383 "Ubuntu",
384 "Droid Sans",
385 "Source Han Sans J",
386 "Source Han Sans JP",
387 "Source Han Sans",
388 "Noto Color Emoji",
389 "sans-serif",
390 ]
391 .into()
392 } else if lang!(ko).matches(lang, true, false) {
393 [
394 "system-ui",
395 "Ubuntu",
396 "Droid Sans",
397 "Source Han Sans K",
398 "Source Han Sans JR",
399 "Source Han Sans",
400 "UnDotum",
401 "FBaekmuk Gulim",
402 "Noto Color Emoji",
403 "sans-serif",
404 ]
405 .into()
406 } else {
407 ["system-ui", "Ubuntu", "Droid Sans", "Noto Color Emoji", "sans-serif"].into()
408 }
409 }
410
411 pub fn system_ui(lang: &Lang) -> Self {
413 if cfg!(windows) {
414 Self::windows_ui(lang)
415 } else if cfg!(target_os = "linux") {
416 Self::linux_ui(lang)
417 } else if cfg!(target_os = "macos") {
418 Self::mac_ui(lang)
419 } else {
420 [FontName::sans_serif()].into()
421 }
422 }
423
424 pub fn push(&mut self, font_name: impl Into<FontName>) {
426 self.0.push(font_name.into())
427 }
428}
429impl Default for FontNames {
430 fn default() -> Self {
431 Self::system_ui(&Lang::default())
432 }
433}
434impl fmt::Debug for FontNames {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 if f.alternate() {
437 f.debug_tuple("FontNames").field(&self.0).finish()
438 } else if self.0.is_empty() {
439 write!(f, "[]")
440 } else if self.0.len() == 1 {
441 write!(f, "{:?}", self.0[0])
442 } else {
443 write!(f, "[{:?}, ", self.0[0])?;
444 for name in &self.0[1..] {
445 write!(f, "{name:?}, ")?;
446 }
447 write!(f, "]")
448 }
449 }
450}
451impl fmt::Display for FontNames {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 let mut iter = self.0.iter();
454
455 if let Some(name) = iter.next() {
456 write!(f, "{name}")?;
457 for name in iter {
458 write!(f, ", {name}")?;
459 }
460 }
461
462 Ok(())
463 }
464}
465impl_from_and_into_var! {
466 fn from(font_name: &'static str) -> FontNames {
467 FontNames(vec![FontName::new(font_name)])
468 }
469
470 fn from(font_name: String) -> FontNames {
471 FontNames(vec![FontName::new(font_name)])
472 }
473
474 fn from(font_name: Txt) -> FontNames {
475 FontNames(vec![FontName::new(font_name)])
476 }
477
478 fn from(font_names: Vec<FontName>) -> FontNames {
479 FontNames(font_names)
480 }
481
482 fn from(font_names: Vec<&'static str>) -> FontNames {
483 FontNames(font_names.into_iter().map(FontName::new).collect())
484 }
485
486 fn from(font_names: Vec<String>) -> FontNames {
487 FontNames(font_names.into_iter().map(FontName::new).collect())
488 }
489
490 fn from(font_name: FontName) -> FontNames {
491 FontNames(vec![font_name])
492 }
493}
494impl ops::Deref for FontNames {
495 type Target = Vec<FontName>;
496
497 fn deref(&self) -> &Self::Target {
498 &self.0
499 }
500}
501impl ops::DerefMut for FontNames {
502 fn deref_mut(&mut self) -> &mut Self::Target {
503 &mut self.0
504 }
505}
506impl std::iter::Extend<FontName> for FontNames {
507 fn extend<T: IntoIterator<Item = FontName>>(&mut self, iter: T) {
508 self.0.extend(iter)
509 }
510}
511impl IntoIterator for FontNames {
512 type Item = FontName;
513
514 type IntoIter = std::vec::IntoIter<FontName>;
515
516 fn into_iter(self) -> Self::IntoIter {
517 self.0.into_iter()
518 }
519}
520impl<const N: usize> From<[FontName; N]> for FontNames {
521 fn from(font_names: [FontName; N]) -> Self {
522 FontNames(font_names.into())
523 }
524}
525impl<const N: usize> IntoVar<FontNames> for [FontName; N] {
526 fn into_var(self) -> Var<FontNames> {
527 const_var(self.into())
528 }
529}
530impl<const N: usize> From<[&'static str; N]> for FontNames {
531 fn from(font_names: [&'static str; N]) -> Self {
532 FontNames(font_names.into_iter().map(FontName::new).collect())
533 }
534}
535impl<const N: usize> IntoVar<FontNames> for [&'static str; N] {
536 fn into_var(self) -> Var<FontNames> {
537 const_var(self.into())
538 }
539}
540impl<const N: usize> From<[String; N]> for FontNames {
541 fn from(font_names: [String; N]) -> Self {
542 FontNames(font_names.into_iter().map(FontName::new).collect())
543 }
544}
545impl<const N: usize> IntoVar<FontNames> for [String; N] {
546 fn into_var(self) -> Var<FontNames> {
547 const_var(self.into())
548 }
549}
550impl<const N: usize> From<[Txt; N]> for FontNames {
551 fn from(font_names: [Txt; N]) -> Self {
552 FontNames(font_names.into_iter().map(FontName::new).collect())
553 }
554}
555impl<const N: usize> IntoVar<FontNames> for [Txt; N] {
556 fn into_var(self) -> Var<FontNames> {
557 const_var(self.into())
558 }
559}
560
561event! {
562 pub static FONT_CHANGED_EVENT: FontChangedArgs;
573}
574
575event_args! {
576 pub struct FontChangedArgs {
578 pub change: FontChange,
580
581 ..
582
583 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
585 list.search_all()
586 }
587 }
588}
589
590#[derive(Clone, Debug)]
592pub enum FontChange {
593 SystemFonts,
597
598 CustomFonts,
603
604 Refresh,
608
609 GenericFont(FontName, Lang),
615
616 Fallback(Lang),
618}
619
620#[derive(Default)]
631#[non_exhaustive]
632pub struct FontManager {}
633impl AppExtension for FontManager {
634 fn event_preview(&mut self, update: &mut EventUpdate) {
635 if RAW_FONT_CHANGED_EVENT.has(update) {
636 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::SystemFonts));
637 } else if let Some(args) = RAW_FONT_AA_CHANGED_EVENT.on(update) {
638 FONTS_SV.read().font_aa.set(args.aa);
639 } else if FONT_CHANGED_EVENT.has(update) {
640 FONTS_SV.write().on_fonts_changed();
641 } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
642 && args.is_respawn
643 {
644 let mut fonts = FONTS_SV.write();
645 fonts.loader.on_view_process_respawn();
646 }
647 }
648
649 fn update(&mut self) {
650 let mut fonts = FONTS_SV.write();
651
652 {
653 let mut f = GENERIC_FONTS_SV.write();
654 for request in std::mem::take(&mut f.requests) {
655 request(&mut f);
656 }
657 }
658
659 let mut changed = false;
660 for (request, responder) in std::mem::take(&mut fonts.loader.unregister_requests) {
661 let r = if let Some(removed) = fonts.loader.custom_fonts.remove(&request) {
662 for removed in removed {
666 removed.on_refresh();
667 }
668
669 changed = true;
670
671 true
672 } else {
673 false
674 };
675 responder.respond(r);
676 }
677 if changed {
678 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
679 }
680
681 if fonts.prune_requested {
682 fonts.on_prune();
683 }
684 }
685}
686
687app_local! {
688 static FONTS_SV: FontsService = FontsService {
689 loader: FontFaceLoader::new(),
690 prune_requested: false,
691 font_aa: var(FontAntiAliasing::Default),
692 };
693}
694
695struct FontsService {
696 loader: FontFaceLoader,
697 prune_requested: bool,
698 font_aa: Var<FontAntiAliasing>,
699}
700impl FontsService {
701 fn on_fonts_changed(&mut self) {
702 self.loader.on_refresh();
703 self.prune_requested = false;
704 }
705
706 fn on_prune(&mut self) {
707 self.loader.on_prune();
708 self.prune_requested = false;
709 }
710}
711
712pub struct FONTS;
714impl FONTS {
715 pub fn refresh(&self) {
719 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Refresh));
720 }
721
722 pub fn prune(&self) {
724 let mut ft = FONTS_SV.write();
725 if !ft.prune_requested {
726 ft.prune_requested = true;
727 UPDATES.update(None);
728 }
729 }
730
731 pub fn generics(&self) -> &'static GenericFonts {
733 &GenericFonts {}
734 }
735
736 pub fn register(&self, custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
745 FontFaceLoader::register(custom_font)
746 }
747
748 pub fn unregister(&self, custom_family: FontName) -> ResponseVar<bool> {
752 FONTS_SV.write().loader.unregister(custom_family)
753 }
754
755 pub fn list(
757 &self,
758 families: &[FontName],
759 style: FontStyle,
760 weight: FontWeight,
761 stretch: FontStretch,
762 lang: &Lang,
763 ) -> ResponseVar<FontFaceList> {
764 if let Some(cached) = FONTS_SV.read().loader.try_list(families, style, weight, stretch, lang) {
766 return cached;
767 }
768 FONTS_SV.write().loader.load_list(families, style, weight, stretch, lang)
770 }
771
772 pub fn find(
774 &self,
775 family: &FontName,
776 style: FontStyle,
777 weight: FontWeight,
778 stretch: FontStretch,
779 lang: &Lang,
780 ) -> ResponseVar<Option<FontFace>> {
781 if let Some(cached) = FONTS_SV.read().loader.try_cached(family, style, weight, stretch, lang) {
783 return cached;
784 }
785 FONTS_SV.write().loader.load(family, style, weight, stretch, lang)
787 }
788
789 pub fn normal(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
791 self.find(family, FontStyle::Normal, FontWeight::NORMAL, FontStretch::NORMAL, lang)
792 }
793
794 pub fn italic(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
796 self.find(family, FontStyle::Italic, FontWeight::NORMAL, FontStretch::NORMAL, lang)
797 }
798
799 pub fn bold(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
801 self.find(family, FontStyle::Normal, FontWeight::BOLD, FontStretch::NORMAL, lang)
802 }
803
804 pub fn custom_fonts(&self) -> Vec<FontName> {
806 FONTS_SV.read().loader.custom_fonts.keys().cloned().collect()
807 }
808
809 pub fn system_fonts(&self) -> ResponseVar<Vec<FontName>> {
813 query_util::system_all()
814 }
815
816 pub fn system_font_aa(&self) -> Var<FontAntiAliasing> {
820 FONTS_SV.read().font_aa.read_only()
821 }
822}
823
824impl<'a> From<ttf_parser::Face<'a>> for FontFaceMetrics {
825 fn from(f: ttf_parser::Face<'a>) -> Self {
826 let underline = f
827 .underline_metrics()
828 .unwrap_or(ttf_parser::LineMetrics { position: 0, thickness: 0 });
829 FontFaceMetrics {
830 units_per_em: f.units_per_em() as _,
831 ascent: f.ascender() as f32,
832 descent: f.descender() as f32,
833 line_gap: f.line_gap() as f32,
834 underline_position: underline.position as f32,
835 underline_thickness: underline.thickness as f32,
836 cap_height: f.capital_height().unwrap_or(0) as f32,
837 x_height: f.x_height().unwrap_or(0) as f32,
838 bounds: euclid::rect(
839 f.global_bounding_box().x_min as f32,
840 f.global_bounding_box().x_max as f32,
841 f.global_bounding_box().width() as f32,
842 f.global_bounding_box().height() as f32,
843 ),
844 }
845 }
846}
847
848#[derive(PartialEq, Eq, Hash)]
849struct FontInstanceKey(Px, Box<[(ttf_parser::Tag, i32)]>);
850impl FontInstanceKey {
851 pub fn new(size: Px, variations: &[rustybuzz::Variation]) -> Self {
853 let variations_key: Vec<_> = variations.iter().map(|p| (p.tag, (p.value * 1000.0) as i32)).collect();
854 FontInstanceKey(size, variations_key.into_boxed_slice())
855 }
856}
857
858#[derive(Clone)]
865pub struct FontFace(Arc<LoadedFontFace>);
866struct LoadedFontFace {
867 data: FontBytes,
868 face_index: u32,
869 display_name: FontName,
870 family_name: FontName,
871 postscript_name: Option<Txt>,
872 style: FontStyle,
873 weight: FontWeight,
874 stretch: FontStretch,
875 metrics: FontFaceMetrics,
876 lig_carets: LigatureCaretList,
877 flags: FontFaceFlags,
878 m: Mutex<FontFaceMut>,
879}
880bitflags! {
881 #[derive(Debug, Clone, Copy)]
882 struct FontFaceFlags: u8 {
883 const IS_MONOSPACE = 0b0000_0001;
884 const HAS_LIGATURES = 0b0000_0010;
885 const HAS_RASTER_IMAGES = 0b0000_0100;
886 const HAS_SVG_IMAGES = 0b0000_1000;
887 }
888}
889struct FontFaceMut {
890 instances: HashMap<FontInstanceKey, Font>,
891 render_ids: Vec<RenderFontFace>,
892 unregistered: bool,
893}
894
895impl fmt::Debug for FontFace {
896 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
897 let m = self.0.m.lock();
898 f.debug_struct("FontFace")
899 .field("display_name", &self.0.display_name)
900 .field("family_name", &self.0.family_name)
901 .field("postscript_name", &self.0.postscript_name)
902 .field("flags", &self.0.flags)
903 .field("style", &self.0.style)
904 .field("weight", &self.0.weight)
905 .field("stretch", &self.0.stretch)
906 .field("metrics", &self.0.metrics)
907 .field("instances.len()", &m.instances.len())
908 .field("render_keys.len()", &m.render_ids.len())
909 .field("unregistered", &m.unregistered)
910 .finish_non_exhaustive()
911 }
912}
913impl PartialEq for FontFace {
914 fn eq(&self, other: &Self) -> bool {
915 Arc::ptr_eq(&self.0, &other.0)
916 }
917}
918impl Eq for FontFace {}
919impl FontFace {
920 pub fn empty() -> Self {
922 FontFace(Arc::new(LoadedFontFace {
923 data: FontBytes::from_static(&[]),
924 face_index: 0,
925 display_name: FontName::from("<empty>"),
926 family_name: FontName::from("<empty>"),
927 postscript_name: None,
928 flags: FontFaceFlags::IS_MONOSPACE,
929 style: FontStyle::Normal,
930 weight: FontWeight::NORMAL,
931 stretch: FontStretch::NORMAL,
932 metrics: FontFaceMetrics {
934 units_per_em: 2048,
935 ascent: 1616.0,
936 descent: -432.0,
937 line_gap: 0.0,
938 underline_position: -205.0,
939 underline_thickness: 102.0,
940 cap_height: 1616.0,
941 x_height: 1616.0,
942 bounds: euclid::Box2D::new(euclid::point2(0.0, -432.0), euclid::point2(1291.0, 1616.0)).to_rect(),
944 },
945 lig_carets: LigatureCaretList::empty(),
946 m: Mutex::new(FontFaceMut {
947 instances: HashMap::default(),
948 render_ids: vec![],
949 unregistered: false,
950 }),
951 }))
952 }
953
954 pub fn is_empty(&self) -> bool {
956 self.0.data.is_empty()
957 }
958
959 async fn load_custom(custom_font: CustomFont) -> Result<Self, FontLoadingError> {
960 let bytes;
961 let mut face_index;
962
963 match custom_font.source {
964 FontSource::File(path, index) => {
965 bytes = task::wait(|| FontBytes::from_file(path)).await?;
966 face_index = index;
967 }
968 FontSource::Memory(arc, index) => {
969 bytes = arc;
970 face_index = index;
971 }
972 FontSource::Alias(other_font) => {
973 let result = FONTS_SV
974 .write()
975 .loader
976 .load_resolved(&other_font, custom_font.style, custom_font.weight, custom_font.stretch);
977 return match result.wait_rsp().await {
978 Some(other_font) => Ok(FontFace(Arc::new(LoadedFontFace {
979 data: other_font.0.data.clone(),
980 face_index: other_font.0.face_index,
981 display_name: custom_font.name.clone(),
982 family_name: custom_font.name,
983 postscript_name: None,
984 style: other_font.0.style,
985 weight: other_font.0.weight,
986 stretch: other_font.0.stretch,
987 metrics: other_font.0.metrics.clone(),
988 m: Mutex::new(FontFaceMut {
989 instances: Default::default(),
990 render_ids: Default::default(),
991 unregistered: Default::default(),
992 }),
993 lig_carets: other_font.0.lig_carets.clone(),
994 flags: other_font.0.flags,
995 }))),
996 None => Err(FontLoadingError::NoSuchFontInCollection),
997 };
998 }
999 }
1000
1001 let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1002 Ok(f) => f,
1003 Err(e) => {
1004 match e {
1005 ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1007 e => return Err(FontLoadingError::Parse(e)),
1008 }
1009
1010 match ttf_parser::Face::parse(&bytes, face_index) {
1011 Ok(f) => f,
1012 Err(_) => return Err(FontLoadingError::Parse(e)),
1013 }
1014 }
1015 };
1016
1017 let has_ligatures = ttf_face.tables().gsub.is_some();
1018 let lig_carets = if has_ligatures {
1019 LigatureCaretList::empty()
1020 } else {
1021 LigatureCaretList::load(ttf_face.raw_face())?
1022 };
1023
1024 let has_raster_images = {
1026 let t = ttf_face.tables();
1027 t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1028 };
1029
1030 let mut flags = FontFaceFlags::empty();
1031 flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1032 flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1033 flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1034 flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1035
1036 Ok(FontFace(Arc::new(LoadedFontFace {
1037 face_index,
1038 display_name: custom_font.name.clone(),
1039 family_name: custom_font.name,
1040 postscript_name: None,
1041 style: custom_font.style,
1042 weight: custom_font.weight,
1043 stretch: custom_font.stretch,
1044 metrics: ttf_face.into(),
1045 lig_carets,
1046 m: Mutex::new(FontFaceMut {
1047 instances: Default::default(),
1048 render_ids: Default::default(),
1049 unregistered: Default::default(),
1050 }),
1051 data: bytes,
1052 flags,
1053 })))
1054 }
1055
1056 fn load(bytes: FontBytes, mut face_index: u32) -> Result<Self, FontLoadingError> {
1057 let _span = tracing::trace_span!("FontFace::load").entered();
1058
1059 let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1060 Ok(f) => f,
1061 Err(e) => {
1062 match e {
1063 ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1065 e => return Err(FontLoadingError::Parse(e)),
1066 }
1067
1068 match ttf_parser::Face::parse(&bytes, face_index) {
1069 Ok(f) => f,
1070 Err(_) => return Err(FontLoadingError::Parse(e)),
1071 }
1072 }
1073 };
1074
1075 let has_ligatures = ttf_face.tables().gsub.is_some();
1076 let lig_carets = if has_ligatures {
1077 LigatureCaretList::empty()
1078 } else {
1079 LigatureCaretList::load(ttf_face.raw_face())?
1080 };
1081
1082 let mut display_name = None;
1083 let mut family_name = None;
1084 let mut postscript_name = None;
1085 let mut any_name = None::<String>;
1086 for name in ttf_face.names() {
1087 if let Some(n) = name.to_string() {
1088 match name.name_id {
1089 ttf_parser::name_id::FULL_NAME => display_name = Some(n),
1090 ttf_parser::name_id::FAMILY => family_name = Some(n),
1091 ttf_parser::name_id::POST_SCRIPT_NAME => postscript_name = Some(n),
1092 _ => match &mut any_name {
1093 Some(s) => {
1094 if n.len() > s.len() {
1095 *s = n;
1096 }
1097 }
1098 None => any_name = Some(n),
1099 },
1100 }
1101 }
1102 }
1103 let display_name = FontName::new(Txt::from_str(
1104 display_name
1105 .as_ref()
1106 .or(family_name.as_ref())
1107 .or(postscript_name.as_ref())
1108 .or(any_name.as_ref())
1109 .unwrap(),
1110 ));
1111 let family_name = family_name.map(FontName::from).unwrap_or_else(|| display_name.clone());
1112 let postscript_name = postscript_name.map(Txt::from);
1113
1114 if ttf_face.units_per_em() == 0 {
1115 tracing::debug!("font {display_name:?} units_per_em 0");
1117 return Err(FontLoadingError::UnknownFormat);
1118 }
1119
1120 let has_raster_images = {
1122 let t = ttf_face.tables();
1123 t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1124 };
1125
1126 let mut flags = FontFaceFlags::empty();
1127 flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1128 flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1129 flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1130 flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1131
1132 Ok(FontFace(Arc::new(LoadedFontFace {
1133 face_index,
1134 family_name,
1135 display_name,
1136 postscript_name,
1137 style: ttf_face.style().into(),
1138 weight: ttf_face.weight().into(),
1139 stretch: ttf_face.width().into(),
1140 metrics: ttf_face.into(),
1141 lig_carets,
1142 m: Mutex::new(FontFaceMut {
1143 instances: Default::default(),
1144 render_ids: Default::default(),
1145 unregistered: Default::default(),
1146 }),
1147 data: bytes,
1148 flags,
1149 })))
1150 }
1151
1152 fn on_refresh(&self) {
1153 let mut m = self.0.m.lock();
1154 m.instances.clear();
1155 m.unregistered = true;
1156 }
1157
1158 fn render_face(&self, renderer: &ViewRenderer) -> zng_view_api::font::FontFaceId {
1159 let mut m = self.0.m.lock();
1160 for r in m.render_ids.iter() {
1161 if &r.renderer == renderer {
1162 return r.face_id;
1163 }
1164 }
1165
1166 let key = match renderer.add_font_face(self.0.data.to_ipc(), self.0.face_index) {
1167 Ok(k) => k,
1168 Err(_) => {
1169 tracing::debug!("respawned calling `add_font`, will return dummy font key");
1170 return zng_view_api::font::FontFaceId::INVALID;
1171 }
1172 };
1173
1174 m.render_ids.push(RenderFontFace::new(renderer, key));
1175
1176 key
1177 }
1178
1179 pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1188 if self.is_empty() {
1189 None
1190 } else {
1191 Some(rustybuzz::Face::from_slice(&self.0.data, self.0.face_index).unwrap())
1192 }
1193 }
1194
1195 pub fn ttf(&self) -> Option<ttf_parser::Face<'_>> {
1204 if self.is_empty() {
1205 None
1206 } else {
1207 Some(ttf_parser::Face::parse(&self.0.data, self.0.face_index).unwrap())
1208 }
1209 }
1210
1211 pub fn bytes(&self) -> &FontBytes {
1213 &self.0.data
1214 }
1215 pub fn index(&self) -> u32 {
1217 self.0.face_index
1218 }
1219
1220 pub fn display_name(&self) -> &FontName {
1222 &self.0.display_name
1223 }
1224
1225 pub fn family_name(&self) -> &FontName {
1227 &self.0.family_name
1228 }
1229
1230 pub fn postscript_name(&self) -> Option<&str> {
1232 self.0.postscript_name.as_deref()
1233 }
1234
1235 pub fn style(&self) -> FontStyle {
1237 self.0.style
1238 }
1239
1240 pub fn weight(&self) -> FontWeight {
1242 self.0.weight
1243 }
1244
1245 pub fn stretch(&self) -> FontStretch {
1247 self.0.stretch
1248 }
1249
1250 pub fn is_monospace(&self) -> bool {
1252 self.0.flags.contains(FontFaceFlags::IS_MONOSPACE)
1253 }
1254
1255 pub fn metrics(&self) -> &FontFaceMetrics {
1257 &self.0.metrics
1258 }
1259
1260 pub fn sized(&self, font_size: Px, variations: RFontVariations) -> Font {
1269 let key = FontInstanceKey::new(font_size, &variations);
1270 let mut m = self.0.m.lock();
1271 if !m.unregistered {
1272 m.instances
1273 .entry(key)
1274 .or_insert_with(|| Font::new(self.clone(), font_size, variations))
1275 .clone()
1276 } else {
1277 tracing::debug!(target: "font_loading", "creating font from unregistered `{}`, will not cache", self.0.display_name);
1278 Font::new(self.clone(), font_size, variations)
1279 }
1280 }
1281
1282 pub fn synthesis_for(&self, style: FontStyle, weight: FontWeight) -> FontSynthesis {
1284 let mut synth = FontSynthesis::DISABLED;
1285
1286 if style != FontStyle::Normal && self.style() == FontStyle::Normal {
1287 synth |= FontSynthesis::OBLIQUE;
1289 }
1290 if weight > self.weight() {
1291 synth |= FontSynthesis::BOLD;
1294 }
1295
1296 synth
1297 }
1298
1299 pub fn is_cached(&self) -> bool {
1303 !self.0.m.lock().unregistered
1304 }
1305
1306 pub fn color_palettes(&self) -> ColorPalettes<'_> {
1310 match self.ttf() {
1311 Some(ttf) => ColorPalettes::new(*ttf.raw_face()),
1312 None => ColorPalettes::empty(),
1313 }
1314 }
1315
1316 pub fn color_glyphs(&self) -> ColorGlyphs<'_> {
1320 match self.ttf() {
1321 Some(ttf) => ColorGlyphs::new(*ttf.raw_face()),
1322 None => ColorGlyphs::empty(),
1323 }
1324 }
1325
1326 pub fn has_ligatures(&self) -> bool {
1328 self.0.flags.contains(FontFaceFlags::HAS_LIGATURES)
1329 }
1330
1331 pub fn has_ligature_caret_offsets(&self) -> bool {
1336 !self.0.lig_carets.is_empty()
1337 }
1338
1339 pub fn has_raster_images(&self) -> bool {
1341 self.0.flags.contains(FontFaceFlags::HAS_RASTER_IMAGES)
1342 }
1343
1344 pub fn has_svg_images(&self) -> bool {
1346 self.0.flags.contains(FontFaceFlags::HAS_SVG_IMAGES)
1347 }
1348}
1349
1350#[derive(Clone)]
1356pub struct Font(Arc<LoadedFont>);
1357struct LoadedFont {
1358 face: FontFace,
1359 size: Px,
1360 variations: RFontVariations,
1361 metrics: FontMetrics,
1362 render_keys: Mutex<Vec<RenderFont>>,
1363 small_word_cache: RwLock<HashMap<WordCacheKey<[u8; Font::SMALL_WORD_LEN]>, ShapedSegmentData>>,
1364 word_cache: RwLock<HashMap<WordCacheKey<String>, ShapedSegmentData>>,
1365}
1366impl fmt::Debug for Font {
1367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1368 f.debug_struct("Font")
1369 .field("face", &self.0.face)
1370 .field("size", &self.0.size)
1371 .field("metrics", &self.0.metrics)
1372 .field("render_keys.len()", &self.0.render_keys.lock().len())
1373 .field("small_word_cache.len()", &self.0.small_word_cache.read().len())
1374 .field("word_cache.len()", &self.0.word_cache.read().len())
1375 .finish()
1376 }
1377}
1378impl PartialEq for Font {
1379 fn eq(&self, other: &Self) -> bool {
1380 Arc::ptr_eq(&self.0, &other.0)
1381 }
1382}
1383impl Eq for Font {}
1384impl Font {
1385 const SMALL_WORD_LEN: usize = 8;
1386
1387 fn to_small_word(s: &str) -> Option<[u8; Self::SMALL_WORD_LEN]> {
1388 if s.len() <= Self::SMALL_WORD_LEN {
1389 let mut a = [b'\0'; Self::SMALL_WORD_LEN];
1390 a[..s.len()].copy_from_slice(s.as_bytes());
1391 Some(a)
1392 } else {
1393 None
1394 }
1395 }
1396
1397 fn new(face: FontFace, size: Px, variations: RFontVariations) -> Self {
1398 Font(Arc::new(LoadedFont {
1399 metrics: face.metrics().sized(size),
1400 face,
1401 size,
1402 variations,
1403 render_keys: Mutex::new(vec![]),
1404 small_word_cache: RwLock::default(),
1405 word_cache: RwLock::default(),
1406 }))
1407 }
1408
1409 fn render_font(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1410 let _span = tracing::trace_span!("Font::render_font").entered();
1411
1412 let mut render_keys = self.0.render_keys.lock();
1413 for r in render_keys.iter() {
1414 if &r.renderer == renderer && r.synthesis == synthesis {
1415 return r.font_id;
1416 }
1417 }
1418
1419 let font_key = self.0.face.render_face(renderer);
1420
1421 let mut opt = zng_view_api::font::FontOptions::default();
1422 opt.synthetic_oblique = synthesis.contains(FontSynthesis::OBLIQUE);
1423 opt.synthetic_bold = synthesis.contains(FontSynthesis::BOLD);
1424 let variations = self.0.variations.iter().map(|v| (v.tag.to_bytes(), v.value)).collect();
1425
1426 let key = match renderer.add_font(font_key, self.0.size, opt, variations) {
1427 Ok(k) => k,
1428 Err(_) => {
1429 tracing::debug!("respawned calling `add_font_instance`, will return dummy font key");
1430 return zng_view_api::font::FontId::INVALID;
1431 }
1432 };
1433
1434 render_keys.push(RenderFont::new(renderer, synthesis, key));
1435
1436 key
1437 }
1438
1439 pub fn face(&self) -> &FontFace {
1441 &self.0.face
1442 }
1443
1444 pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1446 let ppem = self.0.size.0 as u16;
1447
1448 let mut font = self.0.face.harfbuzz()?;
1449
1450 font.set_pixels_per_em(Some((ppem, ppem)));
1451 font.set_variations(&self.0.variations);
1452
1453 Some(font)
1454 }
1455
1456 pub fn size(&self) -> Px {
1460 self.0.size
1461 }
1462
1463 pub fn variations(&self) -> &RFontVariations {
1465 &self.0.variations
1466 }
1467
1468 pub fn metrics(&self) -> &FontMetrics {
1470 &self.0.metrics
1471 }
1472
1473 pub fn ligature_caret_offsets(
1479 &self,
1480 lig: zng_view_api::font::GlyphIndex,
1481 ) -> impl ExactSizeIterator<Item = f32> + DoubleEndedIterator + '_ {
1482 let face = &self.0.face.0;
1483 face.lig_carets.carets(lig).iter().map(move |&o| match o {
1484 ligature_util::LigatureCaret::Coordinate(o) => {
1485 let size_scale = 1.0 / face.metrics.units_per_em as f32 * self.0.size.0 as f32;
1486 o as f32 * size_scale
1487 }
1488 ligature_util::LigatureCaret::GlyphContourPoint(i) => {
1489 if let Some(f) = self.harfbuzz() {
1490 struct Search {
1491 i: u16,
1492 s: u16,
1493 x: f32,
1494 }
1495 impl Search {
1496 fn check(&mut self, x: f32) {
1497 self.s = self.s.saturating_add(1);
1498 if self.s == self.i {
1499 self.x = x;
1500 }
1501 }
1502 }
1503 impl ttf_parser::OutlineBuilder for Search {
1504 fn move_to(&mut self, x: f32, _y: f32) {
1505 self.check(x);
1506 }
1507
1508 fn line_to(&mut self, x: f32, _y: f32) {
1509 self.check(x);
1510 }
1511
1512 fn quad_to(&mut self, _x1: f32, _y1: f32, x: f32, _y: f32) {
1513 self.check(x)
1514 }
1515
1516 fn curve_to(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, x: f32, _y: f32) {
1517 self.check(x);
1518 }
1519
1520 fn close(&mut self) {}
1521 }
1522 let mut search = Search { i, s: 0, x: 0.0 };
1523 if f.outline_glyph(ttf_parser::GlyphId(lig as _), &mut search).is_some() && search.s >= search.i {
1524 return search.x * self.0.metrics.size_scale;
1525 }
1526 }
1527 0.0
1528 }
1529 })
1530 }
1531}
1532impl zng_app::render::Font for Font {
1533 fn is_empty_fallback(&self) -> bool {
1534 self.face().is_empty()
1535 }
1536
1537 fn renderer_id(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1538 self.render_font(renderer, synthesis)
1539 }
1540}
1541
1542#[derive(Debug, Clone)]
1546pub struct FontFaceList {
1547 fonts: Box<[FontFace]>,
1548 requested_style: FontStyle,
1549 requested_weight: FontWeight,
1550 requested_stretch: FontStretch,
1551}
1552impl FontFaceList {
1553 pub fn empty() -> Self {
1555 Self {
1556 fonts: Box::new([FontFace::empty()]),
1557 requested_style: FontStyle::Normal,
1558 requested_weight: FontWeight::NORMAL,
1559 requested_stretch: FontStretch::NORMAL,
1560 }
1561 }
1562
1563 pub fn requested_style(&self) -> FontStyle {
1565 self.requested_style
1566 }
1567
1568 pub fn requested_weight(&self) -> FontWeight {
1570 self.requested_weight
1571 }
1572
1573 pub fn requested_stretch(&self) -> FontStretch {
1575 self.requested_stretch
1576 }
1577
1578 pub fn best(&self) -> &FontFace {
1580 &self.fonts[0]
1581 }
1582
1583 pub fn face_synthesis(&self, face_index: usize) -> FontSynthesis {
1585 if let Some(face) = self.fonts.get(face_index) {
1586 face.synthesis_for(self.requested_style, self.requested_weight)
1587 } else {
1588 FontSynthesis::DISABLED
1589 }
1590 }
1591
1592 pub fn iter(&self) -> std::slice::Iter<'_, FontFace> {
1594 self.fonts.iter()
1595 }
1596
1597 pub fn len(&self) -> usize {
1601 self.fonts.len()
1602 }
1603
1604 pub fn is_empty(&self) -> bool {
1606 self.fonts[0].is_empty() && self.fonts.len() == 1
1607 }
1608
1609 pub fn sized(&self, font_size: Px, variations: RFontVariations) -> FontList {
1613 FontList {
1614 fonts: self.fonts.iter().map(|f| f.sized(font_size, variations.clone())).collect(),
1615 requested_style: self.requested_style,
1616 requested_weight: self.requested_weight,
1617 requested_stretch: self.requested_stretch,
1618 }
1619 }
1620}
1621impl PartialEq for FontFaceList {
1622 fn eq(&self, other: &Self) -> bool {
1624 self.requested_style == other.requested_style
1625 && self.requested_weight == other.requested_weight
1626 && self.requested_stretch == other.requested_stretch
1627 && self.fonts.len() == other.fonts.len()
1628 && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1629 }
1630}
1631impl Eq for FontFaceList {}
1632impl std::ops::Deref for FontFaceList {
1633 type Target = [FontFace];
1634
1635 fn deref(&self) -> &Self::Target {
1636 &self.fonts
1637 }
1638}
1639impl<'a> std::iter::IntoIterator for &'a FontFaceList {
1640 type Item = &'a FontFace;
1641
1642 type IntoIter = std::slice::Iter<'a, FontFace>;
1643
1644 fn into_iter(self) -> Self::IntoIter {
1645 self.iter()
1646 }
1647}
1648impl std::ops::Index<usize> for FontFaceList {
1649 type Output = FontFace;
1650
1651 fn index(&self, index: usize) -> &Self::Output {
1652 &self.fonts[index]
1653 }
1654}
1655
1656#[derive(Debug, Clone)]
1658pub struct FontList {
1659 fonts: Box<[Font]>,
1660 requested_style: FontStyle,
1661 requested_weight: FontWeight,
1662 requested_stretch: FontStretch,
1663}
1664#[expect(clippy::len_without_is_empty)] impl FontList {
1666 pub fn best(&self) -> &Font {
1668 &self.fonts[0]
1669 }
1670
1671 pub fn requested_size(&self) -> Px {
1673 self.fonts[0].size()
1674 }
1675
1676 pub fn requested_style(&self) -> FontStyle {
1678 self.requested_style
1679 }
1680
1681 pub fn requested_weight(&self) -> FontWeight {
1683 self.requested_weight
1684 }
1685
1686 pub fn requested_stretch(&self) -> FontStretch {
1688 self.requested_stretch
1689 }
1690
1691 pub fn face_synthesis(&self, font_index: usize) -> FontSynthesis {
1693 if let Some(font) = self.fonts.get(font_index) {
1694 font.0.face.synthesis_for(self.requested_style, self.requested_weight)
1695 } else {
1696 FontSynthesis::DISABLED
1697 }
1698 }
1699
1700 pub fn iter(&self) -> std::slice::Iter<'_, Font> {
1702 self.fonts.iter()
1703 }
1704
1705 pub fn len(&self) -> usize {
1709 self.fonts.len()
1710 }
1711
1712 pub fn is_sized_from(&self, faces: &FontFaceList) -> bool {
1714 if self.len() != faces.len() {
1715 return false;
1716 }
1717
1718 for (font, face) in self.iter().zip(faces.iter()) {
1719 if font.face() != face {
1720 return false;
1721 }
1722 }
1723
1724 true
1725 }
1726}
1727impl PartialEq for FontList {
1728 fn eq(&self, other: &Self) -> bool {
1730 self.requested_style == other.requested_style
1731 && self.requested_weight == other.requested_weight
1732 && self.requested_stretch == other.requested_stretch
1733 && self.fonts.len() == other.fonts.len()
1734 && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1735 }
1736}
1737impl Eq for FontList {}
1738impl std::ops::Deref for FontList {
1739 type Target = [Font];
1740
1741 fn deref(&self) -> &Self::Target {
1742 &self.fonts
1743 }
1744}
1745impl<'a> std::iter::IntoIterator for &'a FontList {
1746 type Item = &'a Font;
1747
1748 type IntoIter = std::slice::Iter<'a, Font>;
1749
1750 fn into_iter(self) -> Self::IntoIter {
1751 self.iter()
1752 }
1753}
1754impl<I: SliceIndex<[Font]>> std::ops::Index<I> for FontList {
1755 type Output = I::Output;
1756
1757 fn index(&self, index: I) -> &I::Output {
1758 &self.fonts[index]
1759 }
1760}
1761
1762struct FontFaceLoader {
1763 custom_fonts: HashMap<FontName, Vec<FontFace>>,
1764 unregister_requests: Vec<(FontName, ResponderVar<bool>)>,
1765
1766 system_fonts_cache: HashMap<FontName, Vec<SystemFontFace>>,
1767 list_cache: HashMap<Box<[FontName]>, Vec<FontFaceListQuery>>,
1768}
1769struct SystemFontFace {
1770 properties: (FontStyle, FontWeight, FontStretch),
1771 result: ResponseVar<Option<FontFace>>,
1772}
1773struct FontFaceListQuery {
1774 properties: (FontStyle, FontWeight, FontStretch),
1775 lang: Lang,
1776 result: ResponseVar<FontFaceList>,
1777}
1778impl FontFaceLoader {
1779 fn new() -> Self {
1780 FontFaceLoader {
1781 custom_fonts: HashMap::new(),
1782 unregister_requests: vec![],
1783 system_fonts_cache: HashMap::new(),
1784 list_cache: HashMap::new(),
1785 }
1786 }
1787
1788 fn on_view_process_respawn(&mut self) {
1789 let sys_fonts = self.system_fonts_cache.values().flatten().filter_map(|f| f.result.rsp().flatten());
1790 for face in self.custom_fonts.values().flatten().cloned().chain(sys_fonts) {
1791 let mut m = face.0.m.lock();
1792 m.render_ids.clear();
1793 for inst in m.instances.values() {
1794 inst.0.render_keys.lock().clear();
1795 }
1796 }
1797 }
1798
1799 fn on_refresh(&mut self) {
1800 for (_, sys_family) in self.system_fonts_cache.drain() {
1801 for sys_font in sys_family {
1802 sys_font.result.with(|r| {
1803 if let Some(Some(face)) = r.done() {
1804 face.on_refresh();
1805 }
1806 });
1807 }
1808 }
1809 }
1810 fn on_prune(&mut self) {
1811 self.system_fonts_cache.retain(|_, v| {
1812 v.retain(|sff| {
1813 if sff.result.strong_count() == 1 {
1814 sff.result.with(|r| {
1815 match r.done() {
1816 Some(Some(face)) => Arc::strong_count(&face.0) > 1, Some(None) => false, None => true, }
1820 })
1821 } else {
1822 true
1824 }
1825 });
1826 !v.is_empty()
1827 });
1828
1829 self.list_cache.clear();
1830 }
1831
1832 fn register(custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
1833 let resp = task::respond(FontFace::load_custom(custom_font));
1835
1836 resp.hook(|args| {
1838 if let Some(done) = args.value().done() {
1839 if let Ok(face) = done {
1840 let mut fonts = FONTS_SV.write();
1841 let family = fonts.loader.custom_fonts.entry(face.0.family_name.clone()).or_default();
1842 let existing = family
1843 .iter()
1844 .position(|f| f.0.weight == face.0.weight && f.0.style == face.0.style && f.0.stretch == face.0.stretch);
1845
1846 if let Some(i) = existing {
1847 family[i] = face.clone();
1848 } else {
1849 family.push(face.clone());
1850 }
1851
1852 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
1853 }
1854 false
1855 } else {
1856 true
1857 }
1858 })
1859 .perm();
1860 resp
1861 }
1862
1863 fn unregister(&mut self, custom_family: FontName) -> ResponseVar<bool> {
1864 let (responder, response) = response_var();
1865
1866 if !self.unregister_requests.is_empty() {
1867 UPDATES.update(None);
1868 }
1869 self.unregister_requests.push((custom_family, responder));
1870
1871 response
1872 }
1873
1874 fn try_list(
1875 &self,
1876 families: &[FontName],
1877 style: FontStyle,
1878 weight: FontWeight,
1879 stretch: FontStretch,
1880 lang: &Lang,
1881 ) -> Option<ResponseVar<FontFaceList>> {
1882 if let Some(queries) = self.list_cache.get(families) {
1883 for q in queries {
1884 if q.properties == (style, weight, stretch) && &q.lang == lang {
1885 return Some(q.result.clone());
1886 }
1887 }
1888 }
1889 None
1890 }
1891
1892 fn load_list(
1893 &mut self,
1894 families: &[FontName],
1895 style: FontStyle,
1896 weight: FontWeight,
1897 stretch: FontStretch,
1898 lang: &Lang,
1899 ) -> ResponseVar<FontFaceList> {
1900 if let Some(r) = self.try_list(families, style, weight, stretch, lang) {
1901 return r;
1902 }
1903
1904 let mut list = Vec::with_capacity(families.len() + 1);
1905 let mut pending = vec![];
1906
1907 {
1908 let fallback = [GenericFonts {}.fallback(lang)];
1909 let mut used = HashSet::with_capacity(families.len());
1910 for name in families.iter().chain(&fallback) {
1911 if !used.insert(name) {
1912 continue;
1913 }
1914
1915 let face = self.load(name, style, weight, stretch, lang);
1916 if face.is_done() {
1917 if let Some(face) = face.rsp().unwrap() {
1918 list.push(face);
1919 }
1920 } else {
1921 pending.push((list.len(), face));
1922 }
1923 }
1924 }
1925
1926 let r = if pending.is_empty() {
1927 if list.is_empty() {
1928 tracing::error!(target: "font_loading", "failed to load fallback font");
1929 list.push(FontFace::empty());
1930 }
1931 response_done_var(FontFaceList {
1932 fonts: list.into_boxed_slice(),
1933 requested_style: style,
1934 requested_weight: weight,
1935 requested_stretch: stretch,
1936 })
1937 } else {
1938 task::respond(async move {
1939 for (i, pending) in pending.into_iter().rev() {
1940 if let Some(rsp) = pending.wait_rsp().await {
1941 list.insert(i, rsp);
1942 }
1943 }
1944
1945 if list.is_empty() {
1946 tracing::error!(target: "font_loading", "failed to load fallback font");
1947 list.push(FontFace::empty());
1948 }
1949
1950 FontFaceList {
1951 fonts: list.into_boxed_slice(),
1952 requested_style: style,
1953 requested_weight: weight,
1954 requested_stretch: stretch,
1955 }
1956 })
1957 };
1958
1959 self.list_cache
1960 .entry(families.iter().cloned().collect())
1961 .or_insert_with(|| Vec::with_capacity(1))
1962 .push(FontFaceListQuery {
1963 properties: (style, weight, stretch),
1964 lang: lang.clone(),
1965 result: r.clone(),
1966 });
1967
1968 r
1969 }
1970
1971 fn try_cached(
1972 &self,
1973 font_name: &FontName,
1974 style: FontStyle,
1975 weight: FontWeight,
1976 stretch: FontStretch,
1977 lang: &Lang,
1978 ) -> Option<ResponseVar<Option<FontFace>>> {
1979 let resolved = GenericFonts {}.resolve(font_name, lang);
1980 let font_name = resolved.as_ref().unwrap_or(font_name);
1981 self.try_resolved(font_name, style, weight, stretch)
1982 }
1983
1984 fn load(
1986 &mut self,
1987 font_name: &FontName,
1988 style: FontStyle,
1989 weight: FontWeight,
1990 stretch: FontStretch,
1991 lang: &Lang,
1992 ) -> ResponseVar<Option<FontFace>> {
1993 let resolved = GenericFonts {}.resolve(font_name, lang);
1994 let font_name = resolved.as_ref().unwrap_or(font_name);
1995 self.load_resolved(font_name, style, weight, stretch)
1996 }
1997
1998 fn try_resolved(
2000 &self,
2001 font_name: &FontName,
2002 style: FontStyle,
2003 weight: FontWeight,
2004 stretch: FontStretch,
2005 ) -> Option<ResponseVar<Option<FontFace>>> {
2006 if let Some(custom_family) = self.custom_fonts.get(font_name) {
2007 let custom = Self::match_custom(custom_family, style, weight, stretch);
2008 return Some(response_done_var(Some(custom)));
2009 }
2010
2011 if let Some(cached_sys_family) = self.system_fonts_cache.get(font_name) {
2012 for sys_face in cached_sys_family.iter() {
2013 if sys_face.properties == (style, weight, stretch) {
2014 return Some(sys_face.result.clone());
2015 }
2016 }
2017 }
2018
2019 None
2020 }
2021
2022 fn load_resolved(
2024 &mut self,
2025 font_name: &FontName,
2026 style: FontStyle,
2027 weight: FontWeight,
2028 stretch: FontStretch,
2029 ) -> ResponseVar<Option<FontFace>> {
2030 if let Some(cached) = self.try_resolved(font_name, style, weight, stretch) {
2031 return cached;
2032 }
2033
2034 let load = task::wait(clmv!(font_name, || {
2035 let (bytes, face_index) = match Self::get_system(&font_name, style, weight, stretch) {
2036 Some(h) => h,
2037 None => {
2038 #[cfg(debug_assertions)]
2039 static NOT_FOUND: Mutex<Option<HashSet<FontName>>> = Mutex::new(None);
2040
2041 #[cfg(debug_assertions)]
2042 if NOT_FOUND.lock().get_or_insert_with(HashSet::default).insert(font_name.clone()) {
2043 tracing::debug!(r#"font "{font_name}" not found"#);
2044 }
2045
2046 return None;
2047 }
2048 };
2049 match FontFace::load(bytes, face_index) {
2050 Ok(f) => Some(f),
2051 Err(FontLoadingError::UnknownFormat) => None,
2052 Err(e) => {
2053 tracing::error!(target: "font_loading", "failed to load system font, {e}\nquery: {:?}", (font_name, style, weight, stretch));
2054 None
2055 }
2056 }
2057 }));
2058 let result = task::respond(async_clmv!(font_name, {
2059 match task::with_deadline(load, 10.secs()).await {
2060 Ok(r) => r,
2061 Err(_) => {
2062 tracing::error!(target: "font_loading", "timeout loading {font_name:?}");
2063 None
2064 }
2065 }
2066 }));
2067
2068 self.system_fonts_cache
2069 .entry(font_name.clone())
2070 .or_insert_with(|| Vec::with_capacity(1))
2071 .push(SystemFontFace {
2072 properties: (style, weight, stretch),
2073 result: result.clone(),
2074 });
2075
2076 result
2077 }
2078
2079 fn get_system(font_name: &FontName, style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Option<(FontBytes, u32)> {
2080 let _span = tracing::trace_span!("FontFaceLoader::get_system").entered();
2081 match query_util::best(font_name, style, weight, stretch) {
2082 Ok(r) => r,
2083 Err(e) => {
2084 tracing::error!("cannot get `{font_name}` system font, {e}");
2085 None
2086 }
2087 }
2088 }
2089
2090 fn match_custom(faces: &[FontFace], style: FontStyle, weight: FontWeight, stretch: FontStretch) -> FontFace {
2091 if faces.len() == 1 {
2092 return faces[0].clone();
2094 }
2095
2096 let mut set = Vec::with_capacity(faces.len());
2097 let mut set_dist = 0.0f64; let wrong_side = if stretch <= FontStretch::NORMAL {
2104 |s| s > FontStretch::NORMAL
2105 } else {
2106 |s| s <= FontStretch::NORMAL
2107 };
2108 for face in faces {
2109 let mut dist = (face.stretch().0 - stretch.0).abs() as f64;
2110 if wrong_side(face.stretch()) {
2111 dist += f32::MAX as f64 + 1.0;
2112 }
2113
2114 if set.is_empty() {
2115 set.push(face);
2116 set_dist = dist;
2117 } else if dist < set_dist {
2118 set_dist = dist;
2120 set.clear();
2121 set.push(face);
2122 } else if (dist - set_dist).abs() < 0.0001 {
2123 set.push(face);
2125 }
2126 }
2127 if set.len() == 1 {
2128 return set[0].clone();
2129 }
2130
2131 let style_pref = match style {
2136 FontStyle::Normal => [FontStyle::Normal, FontStyle::Oblique, FontStyle::Italic],
2137 FontStyle::Italic => [FontStyle::Italic, FontStyle::Oblique, FontStyle::Normal],
2138 FontStyle::Oblique => [FontStyle::Oblique, FontStyle::Italic, FontStyle::Normal],
2139 };
2140 let mut best_style = style_pref.len();
2141 for face in &set {
2142 let i = style_pref.iter().position(|&s| s == face.style()).unwrap();
2143 if i < best_style {
2144 best_style = i;
2145 }
2146 }
2147 set.retain(|f| f.style() == style_pref[best_style]);
2148 if set.len() == 1 {
2149 return set[0].clone();
2150 }
2151
2152 let add_penalty = if weight.0 >= 400.0 && weight.0 <= 500.0 {
2160 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2162 if face.weight() < weight {
2164 *dist += 100.0;
2166 } else if face.weight().0 > 500.0 {
2167 *dist += 600.0;
2169 }
2170 }
2171 } else if weight.0 < 400.0 {
2172 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2174 if face.weight() > weight {
2175 *dist += weight.0 as f64;
2176 }
2177 }
2178 } else {
2179 debug_assert!(weight.0 > 500.0);
2180 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2182 if face.weight() < weight {
2183 *dist += f32::MAX as f64;
2184 }
2185 }
2186 };
2187
2188 let mut best = set[0];
2189 let mut best_dist = f64::MAX;
2190
2191 for face in &set {
2192 let mut dist = (face.weight().0 - weight.0).abs() as f64;
2193
2194 add_penalty(face, weight, &mut dist);
2195
2196 if dist < best_dist {
2197 best_dist = dist;
2198 best = face;
2199 }
2200 }
2201
2202 best.clone()
2203 }
2204}
2205
2206struct RenderFontFace {
2207 renderer: ViewRenderer,
2208 face_id: zng_view_api::font::FontFaceId,
2209}
2210impl RenderFontFace {
2211 fn new(renderer: &ViewRenderer, face_id: zng_view_api::font::FontFaceId) -> Self {
2212 RenderFontFace {
2213 renderer: renderer.clone(),
2214 face_id,
2215 }
2216 }
2217}
2218impl Drop for RenderFontFace {
2219 fn drop(&mut self) {
2220 let _ = self.renderer.delete_font_face(self.face_id);
2222 }
2223}
2224
2225struct RenderFont {
2226 renderer: ViewRenderer,
2227 synthesis: FontSynthesis,
2228 font_id: zng_view_api::font::FontId,
2229}
2230impl RenderFont {
2231 fn new(renderer: &ViewRenderer, synthesis: FontSynthesis, font_id: zng_view_api::font::FontId) -> RenderFont {
2232 RenderFont {
2233 renderer: renderer.clone(),
2234 synthesis,
2235 font_id,
2236 }
2237 }
2238}
2239impl Drop for RenderFont {
2240 fn drop(&mut self) {
2241 let _ = self.renderer.delete_font(self.font_id);
2243 }
2244}
2245
2246app_local! {
2247 static GENERIC_FONTS_SV: GenericFontsService = GenericFontsService::new();
2248}
2249
2250struct GenericFontsService {
2251 serif: LangMap<FontName>,
2252 sans_serif: LangMap<FontName>,
2253 monospace: LangMap<FontName>,
2254 cursive: LangMap<FontName>,
2255 fantasy: LangMap<FontName>,
2256 fallback: LangMap<FontName>,
2257
2258 requests: Vec<Box<dyn FnOnce(&mut GenericFontsService) + Send + Sync>>,
2259}
2260impl GenericFontsService {
2261 fn new() -> Self {
2262 fn default(name: impl Into<FontName>) -> LangMap<FontName> {
2263 let mut f = LangMap::with_capacity(1);
2264 f.insert(lang!(und), name.into());
2265 f
2266 }
2267
2268 let serif = "serif";
2269 let sans_serif = "sans-serif";
2270 let monospace = "monospace";
2271 let cursive = "cursive";
2272 let fantasy = "fantasy";
2273 let fallback = if cfg!(windows) {
2274 "Segoe UI Symbol"
2275 } else if cfg!(target_os = "linux") {
2276 "Standard Symbols PS"
2277 } else {
2278 "sans-serif"
2279 };
2280
2281 GenericFontsService {
2282 serif: default(serif),
2283 sans_serif: default(sans_serif),
2284 monospace: default(monospace),
2285 cursive: default(cursive),
2286 fantasy: default(fantasy),
2287
2288 fallback: default(fallback),
2289
2290 requests: vec![],
2291 }
2292 }
2293}
2294
2295#[non_exhaustive]
2310pub struct GenericFonts {}
2311macro_rules! impl_fallback_accessors {
2312 ($($name:ident=$name_str:tt),+ $(,)?) => {$($crate::paste! {
2313 #[doc = "Gets the fallback *"$name_str "* font for the given language."]
2314 #[doc = "Note that the returned name can still be the generic `\""$name_str "\"`, this delegates the resolution to the operating system."]
2318
2319 pub fn $name(&self, lang: &Lang) -> FontName {
2320 GENERIC_FONTS_SV.read().$name.get(lang).unwrap().clone()
2321 }
2322
2323 #[doc = "Sets the fallback *"$name_str "* font for the given language."]
2324 pub fn [<set_ $name>]<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2329 let mut g = GENERIC_FONTS_SV.write();
2330 let font_name = font_name.into();
2331 if g.requests.is_empty() {
2332 UPDATES.update(None);
2333 }
2334 g.requests.push(Box::new(move |g| {
2335 g.$name.insert(lang.clone(), font_name);
2336 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::GenericFont(FontName::$name(), lang)));
2337 }));
2338 }
2339 })+};
2340}
2341impl GenericFonts {
2342 #[rustfmt::skip] impl_fallback_accessors! {
2344 serif="serif", sans_serif="sans-serif", monospace="monospace", cursive="cursive", fantasy="fantasy"
2345 }
2346
2347 pub fn fallback(&self, lang: &Lang) -> FontName {
2351 GENERIC_FONTS_SV.read().fallback.get(lang).unwrap().clone()
2352 }
2353
2354 pub fn set_fallback<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2360 let mut g = GENERIC_FONTS_SV.write();
2361 if g.requests.is_empty() {
2362 UPDATES.update(None);
2363 }
2364 let font_name = font_name.into();
2365 g.requests.push(Box::new(move |g| {
2366 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Fallback(lang.clone())));
2367 g.fallback.insert(lang, font_name);
2368 }));
2369 }
2370
2371 pub fn resolve(&self, name: &FontName, lang: &Lang) -> Option<FontName> {
2375 if name == &FontName::serif() {
2376 Some(self.serif(lang))
2377 } else if name == &FontName::sans_serif() {
2378 Some(self.sans_serif(lang))
2379 } else if name == &FontName::monospace() {
2380 Some(self.monospace(lang))
2381 } else if name == &FontName::cursive() {
2382 Some(self.cursive(lang))
2383 } else if name == &FontName::fantasy() {
2384 Some(self.fantasy(lang))
2385 } else {
2386 None
2387 }
2388 }
2389}
2390
2391#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2392pub(crate) enum WeakFontBytes {
2393 Ipc(std::sync::Weak<IpcBytes>),
2394 Arc(std::sync::Weak<Vec<u8>>),
2395 Static(&'static [u8]),
2396 Mmap(std::sync::Weak<FontBytesMmap>),
2397}
2398#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2399impl WeakFontBytes {
2400 pub(crate) fn upgrade(&self) -> Option<FontBytes> {
2401 match self {
2402 WeakFontBytes::Ipc(weak) => Some(FontBytes(FontBytesImpl::Ipc(weak.upgrade()?))),
2403 WeakFontBytes::Arc(weak) => Some(FontBytes(FontBytesImpl::Arc(weak.upgrade()?))),
2404 WeakFontBytes::Static(b) => Some(FontBytes(FontBytesImpl::Static(b))),
2405 WeakFontBytes::Mmap(weak) => Some(FontBytes(FontBytesImpl::Mmap(weak.upgrade()?))),
2406 }
2407 }
2408
2409 pub(crate) fn strong_count(&self) -> usize {
2410 match self {
2411 WeakFontBytes::Ipc(weak) => weak.strong_count(),
2412 WeakFontBytes::Arc(weak) => weak.strong_count(),
2413 WeakFontBytes::Static(_) => 1,
2414 WeakFontBytes::Mmap(weak) => weak.strong_count(),
2415 }
2416 }
2417}
2418
2419#[cfg(not(target_arch = "wasm32"))]
2420struct FontBytesMmap {
2421 path: std::path::PathBuf,
2422 _read_lock: std::fs::File,
2423 mmap: memmap2::Mmap,
2424}
2425
2426#[derive(Clone)]
2427enum FontBytesImpl {
2428 Ipc(Arc<IpcBytes>),
2430 Arc(Arc<Vec<u8>>),
2431 Static(&'static [u8]),
2432 #[cfg(not(target_arch = "wasm32"))]
2433 Mmap(Arc<FontBytesMmap>),
2434}
2435#[derive(Clone)]
2437pub struct FontBytes(FontBytesImpl);
2438impl FontBytes {
2439 pub fn from_ipc(bytes: IpcBytes) -> Self {
2441 Self(FontBytesImpl::Ipc(Arc::new(bytes)))
2442 }
2443
2444 pub fn from_vec(bytes: Vec<u8>) -> Self {
2446 Self(FontBytesImpl::Ipc(Arc::new(IpcBytes::from_vec(bytes))))
2447 }
2448
2449 pub fn from_static(bytes: &'static [u8]) -> Self {
2451 Self(FontBytesImpl::Static(bytes))
2452 }
2453
2454 pub fn from_arc(bytes: Arc<Vec<u8>>) -> Self {
2458 Self(FontBytesImpl::Arc(bytes))
2459 }
2460
2461 pub fn from_file(path: PathBuf) -> std::io::Result<Self> {
2463 let path = dunce::canonicalize(path)?;
2464
2465 #[cfg(windows)]
2466 {
2467 use windows::Win32::{Foundation::MAX_PATH, System::SystemInformation::GetSystemWindowsDirectoryW};
2468 let mut buffer = [0u16; MAX_PATH as usize];
2469 let len = unsafe { GetSystemWindowsDirectoryW(Some(&mut buffer)) };
2471 let fonts_dir = String::from_utf16_lossy(&buffer[..len as usize]);
2472 if path.starts_with(fonts_dir) {
2474 return unsafe { Self::from_file_mmap(path) };
2476 }
2477 }
2478 #[cfg(target_os = "macos")]
2479 if path.starts_with("/System/Library/Fonts/") || path.starts_with("/Library/Fonts/") {
2480 return unsafe { Self::from_file_mmap(path) };
2482 }
2483 #[cfg(target_os = "android")]
2484 if path.starts_with("/system/fonts/") || path.starts_with("/system/font/") || path.starts_with("/system/product/fonts/") {
2485 return unsafe { Self::from_file_mmap(path) };
2487 }
2488 #[cfg(unix)]
2489 if path.starts_with("/usr/share/fonts/") {
2490 return unsafe { Self::from_file_mmap(path) };
2492 }
2493
2494 std::fs::read(path).map(Self::from_vec)
2495 }
2496
2497 #[cfg(not(target_arch = "wasm32"))]
2504 pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2505 let file = std::fs::File::open(&path)?;
2506 file.try_lock_shared()?;
2507 let mmap = unsafe { memmap2::Mmap::map(&file) }?;
2508 Ok(Self(FontBytesImpl::Mmap(Arc::new(FontBytesMmap {
2509 path,
2510 _read_lock: file,
2511 mmap,
2512 }))))
2513 }
2514
2515 #[cfg(target_arch = "wasm32")]
2517 pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2518 Self::from_file(path)
2519 }
2520
2521 #[cfg(not(target_arch = "wasm32"))]
2525 pub fn mmap_path(&self) -> Option<&Path> {
2526 if let FontBytesImpl::Mmap(m) = &self.0 {
2527 Some(&m.path)
2528 } else {
2529 None
2530 }
2531 }
2532 #[cfg(target_arch = "wasm32")]
2534 pub fn mmap_path(&self) -> Option<&Path> {
2535 None
2536 }
2537
2538 #[cfg(not(target_arch = "wasm32"))]
2540 pub fn to_ipc(&self) -> IpcFontBytes {
2541 if let FontBytesImpl::Mmap(m) = &self.0 {
2542 IpcFontBytes::System(m.path.clone())
2543 } else {
2544 IpcFontBytes::Bytes(self.to_ipc_bytes())
2545 }
2546 }
2547 #[cfg(target_arch = "wasm32")]
2548 pub fn to_ipc(&self) -> IpcFontBytes {
2550 IpcFontBytes::Bytes(self.to_ipc_bytes())
2551 }
2552
2553 pub fn to_ipc_bytes(&self) -> IpcBytes {
2555 match &self.0 {
2556 FontBytesImpl::Ipc(b) => (**b).clone(),
2557 FontBytesImpl::Arc(b) => IpcBytes::from_vec((**b).clone()),
2558 FontBytesImpl::Static(b) => IpcBytes::from_slice(b),
2559 #[cfg(not(target_arch = "wasm32"))]
2560 FontBytesImpl::Mmap(m) => IpcBytes::from_slice(&m.mmap[..]),
2561 }
2562 }
2563
2564 #[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2565 pub(crate) fn downgrade(&self) -> WeakFontBytes {
2566 match &self.0 {
2567 FontBytesImpl::Ipc(arc) => WeakFontBytes::Ipc(Arc::downgrade(arc)),
2568 FontBytesImpl::Arc(arc) => WeakFontBytes::Arc(Arc::downgrade(arc)),
2569 FontBytesImpl::Static(b) => WeakFontBytes::Static(b),
2570 #[cfg(not(target_arch = "wasm32"))]
2571 FontBytesImpl::Mmap(arc) => WeakFontBytes::Mmap(Arc::downgrade(arc)),
2572 }
2573 }
2574}
2575impl std::ops::Deref for FontBytes {
2576 type Target = [u8];
2577
2578 fn deref(&self) -> &Self::Target {
2579 match &self.0 {
2580 FontBytesImpl::Ipc(b) => &b[..],
2581 FontBytesImpl::Arc(b) => &b[..],
2582 FontBytesImpl::Static(b) => b,
2583 #[cfg(not(target_arch = "wasm32"))]
2584 FontBytesImpl::Mmap(m) => &m.mmap[..],
2585 }
2586 }
2587}
2588impl fmt::Debug for FontBytes {
2589 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2590 let mut b = f.debug_struct("FontBytes");
2591 b.field(
2592 ".kind",
2593 &match &self.0 {
2594 FontBytesImpl::Ipc(_) => "IpcBytes",
2595 FontBytesImpl::Arc(_) => "Arc",
2596 FontBytesImpl::Static(_) => "Static",
2597 #[cfg(not(target_arch = "wasm32"))]
2598 FontBytesImpl::Mmap { .. } => "Mmap",
2599 },
2600 );
2601 b.field(".len", &self.len().bytes());
2602 #[cfg(not(target_arch = "wasm32"))]
2603 if let FontBytesImpl::Mmap(m) = &self.0 {
2604 b.field(".path", &m.path);
2605 }
2606
2607 b.finish()
2608 }
2609}
2610
2611#[derive(Debug, Clone)]
2612enum FontSource {
2613 File(PathBuf, u32),
2614 Memory(FontBytes, u32),
2615 Alias(FontName),
2616}
2617
2618#[derive(Debug, Clone)]
2620pub struct CustomFont {
2621 name: FontName,
2622 source: FontSource,
2623 stretch: FontStretch,
2624 style: FontStyle,
2625 weight: FontWeight,
2626}
2627impl CustomFont {
2628 pub fn from_file<N: Into<FontName>, P: Into<PathBuf>>(name: N, path: P, font_index: u32) -> Self {
2636 CustomFont {
2637 name: name.into(),
2638 source: FontSource::File(path.into(), font_index),
2639 stretch: FontStretch::NORMAL,
2640 style: FontStyle::Normal,
2641 weight: FontWeight::NORMAL,
2642 }
2643 }
2644
2645 pub fn from_bytes<N: Into<FontName>>(name: N, data: FontBytes, font_index: u32) -> Self {
2653 CustomFont {
2654 name: name.into(),
2655 source: FontSource::Memory(data, font_index),
2656 stretch: FontStretch::NORMAL,
2657 style: FontStyle::Normal,
2658 weight: FontWeight::NORMAL,
2659 }
2660 }
2661
2662 pub fn from_other<N: Into<FontName>, O: Into<FontName>>(name: N, other_font: O) -> Self {
2668 CustomFont {
2669 name: name.into(),
2670 source: FontSource::Alias(other_font.into()),
2671 stretch: FontStretch::NORMAL,
2672 style: FontStyle::Normal,
2673 weight: FontWeight::NORMAL,
2674 }
2675 }
2676
2677 pub fn stretch(mut self, stretch: FontStretch) -> Self {
2681 self.stretch = stretch;
2682 self
2683 }
2684
2685 pub fn style(mut self, style: FontStyle) -> Self {
2689 self.style = style;
2690 self
2691 }
2692
2693 pub fn weight(mut self, weight: FontWeight) -> Self {
2697 self.weight = weight;
2698 self
2699 }
2700}
2701
2702#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Transitionable)]
2706#[serde(transparent)]
2707pub struct FontStretch(pub f32);
2708impl fmt::Debug for FontStretch {
2709 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2710 let name = self.name();
2711 if name.is_empty() {
2712 f.debug_tuple("FontStretch").field(&self.0).finish()
2713 } else {
2714 if f.alternate() {
2715 write!(f, "FontStretch::")?;
2716 }
2717 write!(f, "{name}")
2718 }
2719 }
2720}
2721impl PartialOrd for FontStretch {
2722 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2723 Some(self.cmp(other))
2724 }
2725}
2726impl Ord for FontStretch {
2727 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2728 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
2729 }
2730}
2731impl PartialEq for FontStretch {
2732 fn eq(&self, other: &Self) -> bool {
2733 about_eq(self.0, other.0, EQ_GRANULARITY)
2734 }
2735}
2736impl Eq for FontStretch {}
2737impl std::hash::Hash for FontStretch {
2738 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2739 about_eq_hash(self.0, EQ_GRANULARITY, state)
2740 }
2741}
2742impl Default for FontStretch {
2743 fn default() -> FontStretch {
2744 FontStretch::NORMAL
2745 }
2746}
2747impl FontStretch {
2748 pub const ULTRA_CONDENSED: FontStretch = FontStretch(0.5);
2750 pub const EXTRA_CONDENSED: FontStretch = FontStretch(0.625);
2752 pub const CONDENSED: FontStretch = FontStretch(0.75);
2754 pub const SEMI_CONDENSED: FontStretch = FontStretch(0.875);
2756 pub const NORMAL: FontStretch = FontStretch(1.0);
2758 pub const SEMI_EXPANDED: FontStretch = FontStretch(1.125);
2760 pub const EXPANDED: FontStretch = FontStretch(1.25);
2762 pub const EXTRA_EXPANDED: FontStretch = FontStretch(1.5);
2764 pub const ULTRA_EXPANDED: FontStretch = FontStretch(2.0);
2766
2767 pub fn name(self) -> &'static str {
2769 macro_rules! name {
2770 ($($CONST:ident;)+) => {$(
2771 if self == Self::$CONST {
2772 return stringify!($CONST);
2773 }
2774 )+}
2775 }
2776 name! {
2777 ULTRA_CONDENSED;
2778 EXTRA_CONDENSED;
2779 CONDENSED;
2780 SEMI_CONDENSED;
2781 NORMAL;
2782 SEMI_EXPANDED;
2783 EXPANDED;
2784 EXTRA_EXPANDED;
2785 ULTRA_EXPANDED;
2786 }
2787 ""
2788 }
2789}
2790impl_from_and_into_var! {
2791 fn from(fct: Factor) -> FontStretch {
2792 FontStretch(fct.0)
2793 }
2794 fn from(pct: FactorPercent) -> FontStretch {
2795 FontStretch(pct.fct().0)
2796 }
2797 fn from(fct: f32) -> FontStretch {
2798 FontStretch(fct)
2799 }
2800}
2801impl From<ttf_parser::Width> for FontStretch {
2802 fn from(value: ttf_parser::Width) -> Self {
2803 use ttf_parser::Width::*;
2804 match value {
2805 UltraCondensed => FontStretch::ULTRA_CONDENSED,
2806 ExtraCondensed => FontStretch::EXTRA_CONDENSED,
2807 Condensed => FontStretch::CONDENSED,
2808 SemiCondensed => FontStretch::SEMI_CONDENSED,
2809 Normal => FontStretch::NORMAL,
2810 SemiExpanded => FontStretch::SEMI_EXPANDED,
2811 Expanded => FontStretch::EXPANDED,
2812 ExtraExpanded => FontStretch::EXTRA_EXPANDED,
2813 UltraExpanded => FontStretch::ULTRA_EXPANDED,
2814 }
2815 }
2816}
2817impl From<FontStretch> for ttf_parser::Width {
2818 fn from(value: FontStretch) -> Self {
2819 if value <= FontStretch::ULTRA_CONDENSED {
2820 ttf_parser::Width::UltraCondensed
2821 } else if value <= FontStretch::EXTRA_CONDENSED {
2822 ttf_parser::Width::ExtraCondensed
2823 } else if value <= FontStretch::CONDENSED {
2824 ttf_parser::Width::Condensed
2825 } else if value <= FontStretch::SEMI_CONDENSED {
2826 ttf_parser::Width::SemiCondensed
2827 } else if value <= FontStretch::NORMAL {
2828 ttf_parser::Width::Normal
2829 } else if value <= FontStretch::SEMI_EXPANDED {
2830 ttf_parser::Width::SemiExpanded
2831 } else if value <= FontStretch::EXPANDED {
2832 ttf_parser::Width::Expanded
2833 } else if value <= FontStretch::EXTRA_EXPANDED {
2834 ttf_parser::Width::ExtraExpanded
2835 } else {
2836 ttf_parser::Width::UltraExpanded
2837 }
2838 }
2839}
2840
2841#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
2843pub enum FontStyle {
2844 #[default]
2846 Normal,
2847 Italic,
2849 Oblique,
2851}
2852impl fmt::Debug for FontStyle {
2853 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2854 if f.alternate() {
2855 write!(f, "FontStyle::")?;
2856 }
2857 match self {
2858 Self::Normal => write!(f, "Normal"),
2859 Self::Italic => write!(f, "Italic"),
2860 Self::Oblique => write!(f, "Oblique"),
2861 }
2862 }
2863}
2864impl From<ttf_parser::Style> for FontStyle {
2865 fn from(value: ttf_parser::Style) -> Self {
2866 use ttf_parser::Style::*;
2867 match value {
2868 Normal => FontStyle::Normal,
2869 Italic => FontStyle::Italic,
2870 Oblique => FontStyle::Oblique,
2871 }
2872 }
2873}
2874
2875impl From<FontStyle> for ttf_parser::Style {
2876 fn from(value: FontStyle) -> Self {
2877 match value {
2878 FontStyle::Normal => Self::Normal,
2879 FontStyle::Italic => Self::Italic,
2880 FontStyle::Oblique => Self::Oblique,
2881 }
2882 }
2883}
2884
2885#[derive(Clone, Copy, Transitionable, serde::Serialize, serde::Deserialize)]
2888pub struct FontWeight(pub f32);
2889impl Default for FontWeight {
2890 fn default() -> FontWeight {
2891 FontWeight::NORMAL
2892 }
2893}
2894impl fmt::Debug for FontWeight {
2895 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2896 let name = self.name();
2897 if name.is_empty() {
2898 f.debug_tuple("FontWeight").field(&self.0).finish()
2899 } else {
2900 if f.alternate() {
2901 write!(f, "FontWeight::")?;
2902 }
2903 write!(f, "{name}")
2904 }
2905 }
2906}
2907impl PartialOrd for FontWeight {
2908 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2909 Some(self.cmp(other))
2910 }
2911}
2912impl Ord for FontWeight {
2913 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2914 about_eq_ord(self.0, other.0, EQ_GRANULARITY_100)
2915 }
2916}
2917impl PartialEq for FontWeight {
2918 fn eq(&self, other: &Self) -> bool {
2919 about_eq(self.0, other.0, EQ_GRANULARITY_100)
2920 }
2921}
2922impl Eq for FontWeight {}
2923impl std::hash::Hash for FontWeight {
2924 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2925 about_eq_hash(self.0, EQ_GRANULARITY_100, state)
2926 }
2927}
2928impl FontWeight {
2929 pub const THIN: FontWeight = FontWeight(100.0);
2931 pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
2933 pub const LIGHT: FontWeight = FontWeight(300.0);
2935 pub const NORMAL: FontWeight = FontWeight(400.0);
2937 pub const MEDIUM: FontWeight = FontWeight(500.0);
2939 pub const SEMIBOLD: FontWeight = FontWeight(600.0);
2941 pub const BOLD: FontWeight = FontWeight(700.0);
2943 pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
2945 pub const BLACK: FontWeight = FontWeight(900.0);
2947
2948 pub fn name(self) -> &'static str {
2950 macro_rules! name {
2951 ($($CONST:ident;)+) => {$(
2952 if self == Self::$CONST {
2953 return stringify!($CONST);
2954 }
2955 )+}
2956 }
2957 name! {
2958 THIN;
2959 EXTRA_LIGHT;
2960 LIGHT;
2961 NORMAL;
2962 MEDIUM;
2963 SEMIBOLD;
2964 BOLD;
2965 EXTRA_BOLD;
2966 BLACK;
2967 }
2968 ""
2969 }
2970}
2971impl_from_and_into_var! {
2972 fn from(weight: u32) -> FontWeight {
2973 FontWeight(weight as f32)
2974 }
2975 fn from(weight: f32) -> FontWeight {
2976 FontWeight(weight)
2977 }
2978}
2979impl From<ttf_parser::Weight> for FontWeight {
2980 fn from(value: ttf_parser::Weight) -> Self {
2981 use ttf_parser::Weight::*;
2982 match value {
2983 Thin => FontWeight::THIN,
2984 ExtraLight => FontWeight::EXTRA_LIGHT,
2985 Light => FontWeight::LIGHT,
2986 Normal => FontWeight::NORMAL,
2987 Medium => FontWeight::MEDIUM,
2988 SemiBold => FontWeight::SEMIBOLD,
2989 Bold => FontWeight::BOLD,
2990 ExtraBold => FontWeight::EXTRA_BOLD,
2991 Black => FontWeight::BLACK,
2992 Other(o) => FontWeight(o as f32),
2993 }
2994 }
2995}
2996impl From<FontWeight> for ttf_parser::Weight {
2997 fn from(value: FontWeight) -> Self {
2998 ttf_parser::Weight::from(value.0 as u16)
2999 }
3000}
3001
3002#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3004pub enum LineBreak {
3005 Auto,
3007 Loose,
3009 Normal,
3011 Strict,
3013 Anywhere,
3015}
3016impl Default for LineBreak {
3017 fn default() -> Self {
3019 LineBreak::Auto
3020 }
3021}
3022impl fmt::Debug for LineBreak {
3023 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3024 if f.alternate() {
3025 write!(f, "LineBreak::")?;
3026 }
3027 match self {
3028 LineBreak::Auto => write!(f, "Auto"),
3029 LineBreak::Loose => write!(f, "Loose"),
3030 LineBreak::Normal => write!(f, "Normal"),
3031 LineBreak::Strict => write!(f, "Strict"),
3032 LineBreak::Anywhere => write!(f, "Anywhere"),
3033 }
3034 }
3035}
3036
3037#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3042#[non_exhaustive]
3043pub enum ParagraphBreak {
3044 #[default]
3046 None,
3047 Line,
3049}
3050impl fmt::Debug for ParagraphBreak {
3051 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3052 if f.alternate() {
3053 write!(f, "ParagraphBreak::")?;
3054 }
3055 match self {
3056 ParagraphBreak::None => write!(f, "None"),
3057 ParagraphBreak::Line => write!(f, "Line"),
3058 }
3059 }
3060}
3061
3062#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3064pub enum Hyphens {
3065 None,
3067 Manual,
3072 Auto,
3074}
3075impl Default for Hyphens {
3076 fn default() -> Self {
3078 Hyphens::Auto
3079 }
3080}
3081impl fmt::Debug for Hyphens {
3082 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3083 if f.alternate() {
3084 write!(f, "Hyphens::")?;
3085 }
3086 match self {
3087 Hyphens::None => write!(f, "None"),
3088 Hyphens::Manual => write!(f, "Manual"),
3089 Hyphens::Auto => write!(f, "Auto"),
3090 }
3091 }
3092}
3093
3094#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3100pub enum WordBreak {
3101 Normal,
3103 BreakAll,
3105 KeepAll,
3107}
3108impl Default for WordBreak {
3109 fn default() -> Self {
3111 WordBreak::Normal
3112 }
3113}
3114impl fmt::Debug for WordBreak {
3115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3116 if f.alternate() {
3117 write!(f, "WordBreak::")?;
3118 }
3119 match self {
3120 WordBreak::Normal => write!(f, "Normal"),
3121 WordBreak::BreakAll => write!(f, "BreakAll"),
3122 WordBreak::KeepAll => write!(f, "KeepAll"),
3123 }
3124 }
3125}
3126
3127#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3129pub enum Justify {
3130 Auto,
3134 InterWord,
3136 InterLetter,
3138}
3139impl Default for Justify {
3140 fn default() -> Self {
3142 Justify::Auto
3143 }
3144}
3145impl Justify {
3146 pub fn resolve(self, lang: &Lang) -> Self {
3148 match self {
3149 Self::Auto => match lang.language.as_str() {
3150 "zh" | "ja" | "ko" => Self::InterLetter,
3151 _ => Self::InterWord,
3152 },
3153 m => m,
3154 }
3155 }
3156}
3157impl fmt::Debug for Justify {
3158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3159 if f.alternate() {
3160 write!(f, "Justify::")?;
3161 }
3162 match self {
3163 Justify::Auto => write!(f, "Auto"),
3164 Justify::InterWord => write!(f, "InterWord"),
3165 Justify::InterLetter => write!(f, "InterLetter"),
3166 }
3167 }
3168}
3169
3170#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3178#[non_exhaustive]
3179pub struct FontFaceMetrics {
3180 pub units_per_em: u32,
3184
3185 pub ascent: f32,
3187
3188 pub descent: f32,
3194
3195 pub line_gap: f32,
3197
3198 pub underline_position: f32,
3201
3202 pub underline_thickness: f32,
3204
3205 pub cap_height: f32,
3207
3208 pub x_height: f32,
3211
3212 pub bounds: euclid::Rect<f32, ()>,
3216}
3217impl FontFaceMetrics {
3218 pub fn sized(&self, font_size_px: Px) -> FontMetrics {
3220 let size_scale = 1.0 / self.units_per_em as f32 * font_size_px.0 as f32;
3221 let s = move |f: f32| Px((f * size_scale).round() as i32);
3222 FontMetrics {
3223 size_scale,
3224 ascent: s(self.ascent),
3225 descent: s(self.descent),
3226 line_gap: s(self.line_gap),
3227 underline_position: s(self.underline_position),
3228 underline_thickness: s(self.underline_thickness),
3229 cap_height: s(self.cap_height),
3230 x_height: (s(self.x_height)),
3231 bounds: {
3232 let b = self.bounds;
3233 PxRect::new(
3234 PxPoint::new(s(b.origin.x), s(b.origin.y)),
3235 PxSize::new(s(b.size.width), s(b.size.height)),
3236 )
3237 },
3238 }
3239 }
3240}
3241
3242#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3246#[non_exhaustive]
3247pub struct FontMetrics {
3248 pub size_scale: f32,
3250
3251 pub ascent: Px,
3253
3254 pub descent: Px,
3260
3261 pub line_gap: Px,
3263
3264 pub underline_position: Px,
3267
3268 pub underline_thickness: Px,
3270
3271 pub cap_height: Px,
3273
3274 pub x_height: Px,
3276
3277 pub bounds: PxRect,
3281}
3282impl FontMetrics {
3283 pub fn line_height(&self) -> Px {
3285 self.ascent - self.descent + self.line_gap
3286 }
3287}
3288
3289#[derive(Clone)]
3291pub enum TextTransformFn {
3292 None,
3294 Uppercase,
3296 Lowercase,
3298 Custom(Arc<dyn Fn(&Txt) -> Cow<Txt> + Send + Sync>),
3300}
3301impl TextTransformFn {
3302 pub fn transform<'t>(&self, text: &'t Txt) -> Cow<'t, Txt> {
3306 match self {
3307 TextTransformFn::None => Cow::Borrowed(text),
3308 TextTransformFn::Uppercase => {
3309 if text.chars().any(|c| !c.is_uppercase()) {
3310 Cow::Owned(text.to_uppercase().into())
3311 } else {
3312 Cow::Borrowed(text)
3313 }
3314 }
3315 TextTransformFn::Lowercase => {
3316 if text.chars().any(|c| !c.is_lowercase()) {
3317 Cow::Owned(text.to_lowercase().into())
3318 } else {
3319 Cow::Borrowed(text)
3320 }
3321 }
3322 TextTransformFn::Custom(fn_) => fn_(text),
3323 }
3324 }
3325
3326 pub fn custom(fn_: impl Fn(&Txt) -> Cow<Txt> + Send + Sync + 'static) -> Self {
3328 TextTransformFn::Custom(Arc::new(fn_))
3329 }
3330}
3331impl fmt::Debug for TextTransformFn {
3332 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3333 if f.alternate() {
3334 write!(f, "TextTransformFn::")?;
3335 }
3336 match self {
3337 TextTransformFn::None => write!(f, "None"),
3338 TextTransformFn::Uppercase => write!(f, "Uppercase"),
3339 TextTransformFn::Lowercase => write!(f, "Lowercase"),
3340 TextTransformFn::Custom(_) => write!(f, "Custom"),
3341 }
3342 }
3343}
3344impl PartialEq for TextTransformFn {
3345 fn eq(&self, other: &Self) -> bool {
3346 match (self, other) {
3347 (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
3348 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3349 }
3350 }
3351}
3352
3353#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3355pub enum WhiteSpace {
3356 Preserve,
3359 Merge,
3361 MergeAll,
3363}
3364impl Default for WhiteSpace {
3365 fn default() -> Self {
3367 WhiteSpace::Preserve
3368 }
3369}
3370impl WhiteSpace {
3371 pub fn transform(self, text: &Txt) -> Cow<'_, Txt> {
3375 match self {
3376 WhiteSpace::Preserve => Cow::Borrowed(text),
3377 WhiteSpace::Merge => {
3378 let is_white_space = |c: char| c.is_whitespace() && !"\n\r\u{85}".contains(c);
3379 let t = text.trim_matches(is_white_space);
3380
3381 let mut prev_space = false;
3382 for c in t.chars() {
3383 if is_white_space(c) {
3384 if prev_space || c != '\u{20}' {
3385 let mut r = String::new();
3388 let mut sep = "";
3389 for part in t.split(is_white_space).filter(|s| !s.is_empty()) {
3390 r.push_str(sep);
3391 r.push_str(part);
3392 sep = "\u{20}";
3393 }
3394 return Cow::Owned(Txt::from_str(&r));
3395 } else {
3396 prev_space = true;
3397 }
3398 } else {
3399 prev_space = false;
3400 }
3401 }
3402
3403 if t.len() != text.len() {
3404 Cow::Owned(Txt::from_str(t))
3405 } else {
3406 Cow::Borrowed(text)
3407 }
3408 }
3409 WhiteSpace::MergeAll => {
3410 let t = text.trim();
3411
3412 let mut prev_space = false;
3413 for c in t.chars() {
3414 if c.is_whitespace() {
3415 if prev_space || c != '\u{20}' {
3416 let mut r = String::new();
3419 let mut sep = "";
3420 for part in t.split_whitespace() {
3421 r.push_str(sep);
3422 r.push_str(part);
3423 sep = "\u{20}";
3424 }
3425 return Cow::Owned(Txt::from_str(&r));
3426 } else {
3427 prev_space = true;
3428 }
3429 } else {
3430 prev_space = false;
3431 }
3432 }
3433
3434 if t.len() != text.len() {
3435 Cow::Owned(Txt::from_str(t))
3436 } else {
3437 Cow::Borrowed(text)
3438 }
3439 }
3440 }
3441 }
3442}
3443impl fmt::Debug for WhiteSpace {
3444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3445 if f.alternate() {
3446 write!(f, "WhiteSpace::")?;
3447 }
3448 match self {
3449 WhiteSpace::Preserve => write!(f, "Preserve"),
3450 WhiteSpace::Merge => write!(f, "Merge"),
3451 WhiteSpace::MergeAll => write!(f, "MergeAll"),
3452 }
3453 }
3454}
3455
3456#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
3458pub struct CaretIndex {
3459 pub index: usize,
3463 pub line: usize,
3473}
3474
3475impl PartialEq for CaretIndex {
3476 fn eq(&self, other: &Self) -> bool {
3477 self.index == other.index
3478 }
3479}
3480impl Eq for CaretIndex {}
3481impl CaretIndex {
3482 pub const ZERO: CaretIndex = CaretIndex { index: 0, line: 0 };
3484}
3485impl PartialOrd for CaretIndex {
3486 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3487 Some(self.cmp(other))
3488 }
3489}
3490impl Ord for CaretIndex {
3491 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3492 self.index.cmp(&other.index)
3493 }
3494}
3495
3496#[derive(Debug, Clone)]
3498#[non_exhaustive]
3499pub enum FontLoadingError {
3500 UnknownFormat,
3502 NoSuchFontInCollection,
3507 Parse(ttf_parser::FaceParsingError),
3509 NoFilesystem,
3512 Io(Arc<std::io::Error>),
3514}
3515impl PartialEq for FontLoadingError {
3516 fn eq(&self, other: &Self) -> bool {
3517 match (self, other) {
3518 (Self::Io(l0), Self::Io(r0)) => Arc::ptr_eq(l0, r0),
3519 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3520 }
3521 }
3522}
3523impl From<std::io::Error> for FontLoadingError {
3524 fn from(error: std::io::Error) -> FontLoadingError {
3525 Self::Io(Arc::new(error))
3526 }
3527}
3528impl fmt::Display for FontLoadingError {
3529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3530 match self {
3531 Self::UnknownFormat => write!(f, "unknown format"),
3532 Self::NoSuchFontInCollection => write!(f, "no such font in the collection"),
3533 Self::NoFilesystem => write!(f, "no filesystem present"),
3534 Self::Parse(e) => fmt::Display::fmt(e, f),
3535 Self::Io(e) => fmt::Display::fmt(e, f),
3536 }
3537 }
3538}
3539impl std::error::Error for FontLoadingError {
3540 fn cause(&self) -> Option<&dyn std::error::Error> {
3541 match self {
3542 FontLoadingError::Parse(e) => Some(e),
3543 FontLoadingError::Io(e) => Some(e),
3544 _ => None,
3545 }
3546 }
3547}
3548
3549#[cfg(test)]
3550mod tests {
3551 use zng_app::APP;
3552
3553 use super::*;
3554
3555 #[test]
3556 fn generic_fonts_default() {
3557 let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
3558
3559 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(und)))
3560 }
3561
3562 #[test]
3563 fn generic_fonts_fallback() {
3564 let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
3565
3566 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(en_US)));
3567 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(es)));
3568 }
3569
3570 #[test]
3571 fn generic_fonts_get1() {
3572 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3573 GenericFonts {}.set_sans_serif(lang!(en_US), "Test Value");
3574 app.update(false).assert_wait();
3575
3576 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3577 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3578 }
3579
3580 #[test]
3581 fn generic_fonts_get2() {
3582 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3583 GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3584 app.update(false).assert_wait();
3585
3586 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3587 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3588 }
3589
3590 #[test]
3591 fn generic_fonts_get_best() {
3592 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3593 GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3594 GenericFonts {}.set_sans_serif(lang!(en_US), "Best");
3595 app.update(false).assert_wait();
3596
3597 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Best");
3598 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3599 assert_eq!(&GenericFonts {}.sans_serif(&lang!("und")), "sans-serif");
3600 }
3601
3602 #[test]
3603 fn generic_fonts_get_no_lang_match() {
3604 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3605 GenericFonts {}.set_sans_serif(lang!(es_US), "Test Value");
3606 app.update(false).assert_wait();
3607
3608 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "sans-serif");
3609 assert_eq!(&GenericFonts {}.sans_serif(&lang!("es")), "Test Value");
3610 }
3611}