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")))]
22#![expect(clippy::type_complexity)]
24#![warn(unused_extern_crates)]
25#![warn(missing_docs)]
26#![cfg_attr(not(ipc), allow(unused))]
27
28use font_features::RFontVariations;
29use hashbrown::{HashMap, HashSet};
30use std::{borrow::Cow, fmt, io, ops, path::PathBuf, slice::SliceIndex, sync::Arc};
31#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
32use zng_task::channel::WeakIpcBytes;
33
34#[macro_use]
35extern crate bitflags;
36
37pub mod font_features;
38
39mod query_util;
40
41mod emoji_util;
42pub use emoji_util::*;
43
44mod ligature_util;
45use ligature_util::*;
46
47mod unicode_bidi_util;
48
49mod segmenting;
50pub use segmenting::*;
51
52mod shaping;
53pub use shaping::*;
54use zng_clone_move::{async_clmv, clmv};
55
56mod hyphenation;
57pub use self::hyphenation::*;
58
59mod unit;
60pub use unit::*;
61
62use parking_lot::{Mutex, RwLock};
63use pastey::paste;
64use zng_app::{
65 event::{event, event_args},
66 render::FontSynthesis,
67 update::UPDATES,
68 view_process::{
69 VIEW_PROCESS_INITED_EVENT, ViewRenderer,
70 raw_events::{RAW_FONT_AA_CHANGED_EVENT, RAW_FONT_CHANGED_EVENT},
71 },
72};
73use zng_app_context::app_local;
74use zng_ext_l10n::{Lang, LangMap, lang};
75use zng_layout::unit::{
76 ByteUnits as _, EQ_GRANULARITY, EQ_GRANULARITY_100, Factor, FactorPercent, Px, PxPoint, PxRect, PxSize, TimeUnits as _, about_eq,
77 about_eq_hash, about_eq_ord, euclid,
78};
79use zng_task::{self as task, channel::IpcBytes};
80use zng_txt::Txt;
81use zng_var::{IntoVar, ResponseVar, Var, animation::Transitionable, const_var, impl_from_and_into_var, response_done_var, response_var};
82use zng_view_api::{config::FontAntiAliasing, font::IpcFontBytes};
83
84#[derive(Clone)]
92pub struct FontName {
93 txt: Txt,
94 is_ascii: bool,
95}
96impl fmt::Debug for FontName {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 if f.alternate() {
99 f.debug_struct("FontName")
100 .field("txt", &self.txt)
101 .field("is_ascii", &self.is_ascii)
102 .finish()
103 } else {
104 write!(f, "{:?}", self.txt)
105 }
106 }
107}
108impl PartialEq for FontName {
109 fn eq(&self, other: &Self) -> bool {
110 self.unicase() == other.unicase()
111 }
112}
113impl Eq for FontName {}
114impl PartialEq<str> for FontName {
115 fn eq(&self, other: &str) -> bool {
116 self.unicase() == unicase::UniCase::<&str>::from(other)
117 }
118}
119impl std::hash::Hash for FontName {
120 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
121 std::hash::Hash::hash(&self.unicase(), state)
122 }
123}
124impl Ord for FontName {
125 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
126 if self == other {
127 return std::cmp::Ordering::Equal;
129 }
130 self.txt.cmp(&other.txt)
131 }
132}
133impl PartialOrd for FontName {
134 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
135 Some(self.cmp(other))
136 }
137}
138impl FontName {
139 fn unicase(&self) -> unicase::UniCase<&str> {
140 if self.is_ascii {
141 unicase::UniCase::ascii(self)
142 } else {
143 unicase::UniCase::unicode(self)
144 }
145 }
146
147 pub const fn from_static(name: &'static str) -> Self {
149 FontName {
150 txt: Txt::from_static(name),
151 is_ascii: {
152 let name_bytes = name.as_bytes();
154 let mut i = name_bytes.len();
155 let mut is_ascii = true;
156 while i > 0 {
157 i -= 1;
158 if !name_bytes[i].is_ascii() {
159 is_ascii = false;
160 break;
161 }
162 }
163 is_ascii
164 },
165 }
166 }
167
168 pub fn new(name: impl Into<Txt>) -> Self {
177 let txt = name.into();
178 FontName {
179 is_ascii: txt.is_ascii(),
180 txt,
181 }
182 }
183
184 pub fn serif() -> Self {
188 Self::new("serif")
189 }
190
191 pub fn sans_serif() -> Self {
196 Self::new("sans-serif")
197 }
198
199 pub fn monospace() -> Self {
203 Self::new("monospace")
204 }
205
206 pub fn cursive() -> Self {
211 Self::new("cursive")
212 }
213
214 pub fn fantasy() -> Self {
218 Self::new("fantasy")
219 }
220
221 pub fn name(&self) -> &str {
223 &self.txt
224 }
225
226 pub fn into_text(self) -> Txt {
230 self.txt
231 }
232}
233impl_from_and_into_var! {
234 fn from(s: &'static str) -> FontName {
235 FontName::new(s)
236 }
237 fn from(s: String) -> FontName {
238 FontName::new(s)
239 }
240 fn from(s: Cow<'static, str>) -> FontName {
241 FontName::new(s)
242 }
243 fn from(f: FontName) -> Txt {
244 f.into_text()
245 }
246}
247impl fmt::Display for FontName {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 f.write_str(self.name())
250 }
251}
252impl std::ops::Deref for FontName {
253 type Target = str;
254
255 fn deref(&self) -> &Self::Target {
256 self.txt.deref()
257 }
258}
259impl AsRef<str> for FontName {
260 fn as_ref(&self) -> &str {
261 self.txt.as_ref()
262 }
263}
264impl serde::Serialize for FontName {
265 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
266 where
267 S: serde::Serializer,
268 {
269 self.txt.serialize(serializer)
270 }
271}
272impl<'de> serde::Deserialize<'de> for FontName {
273 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
274 where
275 D: serde::Deserializer<'de>,
276 {
277 Txt::deserialize(deserializer).map(FontName::new)
278 }
279}
280
281#[derive(Eq, PartialEq, Hash, Clone, serde::Serialize, serde::Deserialize)]
310#[serde(transparent)]
311pub struct FontNames(pub Vec<FontName>);
312impl FontNames {
313 pub fn empty() -> Self {
315 FontNames(vec![])
316 }
317
318 pub fn windows_ui(lang: &Lang) -> Self {
320 if lang!("zh-Hans").matches(lang, true, false) {
324 ["Segoe UI", "Microsoft YaHei", "Segoe Ui Emoji", "sans-serif"].into()
325 } else if lang!("zh-Hant").matches(lang, true, false) {
326 ["Segoe UI", "Microsoft Jhenghei", "Segoe Ui Emoji", "sans-serif"].into()
327 } else if lang!(ja).matches(lang, true, false) {
328 ["Segoe UI", "Yu Gothic UI", "Meiryo UI", "Segoe Ui Emoji", "sans-serif"].into()
329 } else if lang!(ko).matches(lang, true, false) {
330 ["Segoe UI", "Malgun Gothic", "Dotom", "Segoe Ui Emoji", "sans-serif"].into()
331 } else {
332 ["Segoe UI", "Segoe Ui Emoji", "sans-serif"].into()
333 }
334 }
335
336 pub fn mac_ui(lang: &Lang) -> Self {
338 if lang!("zh-Hans").matches(lang, true, false) {
341 ["PingFang SC", "Hiragino Sans GB", "Apple Color Emoji", "sans-serif"].into()
342 } else if lang!("zh-Hant").matches(lang, true, false) {
343 ["PingFang TC", "Apple Color Emoji", "sans-serif"].into()
344 } else if lang!(ja).matches(lang, true, false) {
345 ["Hiragino Kaku Gothic Pro", "Apple Color Emoji", "sans-serif"].into()
346 } else if lang!(ko).matches(lang, true, false) {
347 [
348 "Nanum Gothic",
349 "Apple SD Gothic Neo",
350 "AppleGothic",
351 "Apple Color Emoji",
352 "sans-serif",
353 ]
354 .into()
355 } else {
356 ["Neue Helvetica", "Lucida Grande", "Apple Color Emoji", "sans-serif"].into()
357 }
358 }
359
360 pub fn linux_ui(lang: &Lang) -> Self {
362 if lang!("zh-Hans").matches(lang, true, false) {
365 [
366 "Ubuntu",
367 "Droid Sans",
368 "Source Han Sans SC",
369 "Source Han Sans CN",
370 "Source Han Sans",
371 "Noto Color Emoji",
372 "sans-serif",
373 ]
374 .into()
375 } else if lang!("zh-Hant").matches(lang, true, false) {
376 [
377 "Ubuntu",
378 "Droid Sans",
379 "Source Han Sans TC",
380 "Source Han Sans TW",
381 "Source Han Sans",
382 "Noto Color Emoji",
383 "sans-serif",
384 ]
385 .into()
386 } else if lang!(ja).matches(lang, true, false) {
387 [
388 "system-ui",
389 "Ubuntu",
390 "Droid Sans",
391 "Source Han Sans J",
392 "Source Han Sans JP",
393 "Source Han Sans",
394 "Noto Color Emoji",
395 "sans-serif",
396 ]
397 .into()
398 } else if lang!(ko).matches(lang, true, false) {
399 [
400 "system-ui",
401 "Ubuntu",
402 "Droid Sans",
403 "Source Han Sans K",
404 "Source Han Sans JR",
405 "Source Han Sans",
406 "UnDotum",
407 "FBaekmuk Gulim",
408 "Noto Color Emoji",
409 "sans-serif",
410 ]
411 .into()
412 } else {
413 ["system-ui", "Ubuntu", "Droid Sans", "Noto Color Emoji", "sans-serif"].into()
414 }
415 }
416
417 pub fn system_ui(lang: &Lang) -> Self {
419 if cfg!(windows) {
420 Self::windows_ui(lang)
421 } else if cfg!(target_os = "linux") {
422 Self::linux_ui(lang)
423 } else if cfg!(target_os = "macos") {
424 Self::mac_ui(lang)
425 } else {
426 [FontName::sans_serif()].into()
427 }
428 }
429
430 pub fn push(&mut self, font_name: impl Into<FontName>) {
432 self.0.push(font_name.into())
433 }
434}
435impl Default for FontNames {
436 fn default() -> Self {
437 Self::system_ui(&Lang::default())
438 }
439}
440impl fmt::Debug for FontNames {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 if f.alternate() {
443 f.debug_tuple("FontNames").field(&self.0).finish()
444 } else if self.0.is_empty() {
445 write!(f, "[]")
446 } else if self.0.len() == 1 {
447 write!(f, "{:?}", self.0[0])
448 } else {
449 write!(f, "[{:?}, ", self.0[0])?;
450 for name in &self.0[1..] {
451 write!(f, "{name:?}, ")?;
452 }
453 write!(f, "]")
454 }
455 }
456}
457impl fmt::Display for FontNames {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459 let mut iter = self.0.iter();
460
461 if let Some(name) = iter.next() {
462 write!(f, "{name}")?;
463 for name in iter {
464 write!(f, ", {name}")?;
465 }
466 }
467
468 Ok(())
469 }
470}
471impl_from_and_into_var! {
472 fn from(font_name: &'static str) -> FontNames {
473 FontNames(vec![FontName::new(font_name)])
474 }
475
476 fn from(font_name: String) -> FontNames {
477 FontNames(vec![FontName::new(font_name)])
478 }
479
480 fn from(font_name: Txt) -> FontNames {
481 FontNames(vec![FontName::new(font_name)])
482 }
483
484 fn from(font_names: Vec<FontName>) -> FontNames {
485 FontNames(font_names)
486 }
487
488 fn from(font_names: Vec<&'static str>) -> FontNames {
489 FontNames(font_names.into_iter().map(FontName::new).collect())
490 }
491
492 fn from(font_names: Vec<String>) -> FontNames {
493 FontNames(font_names.into_iter().map(FontName::new).collect())
494 }
495
496 fn from(font_name: FontName) -> FontNames {
497 FontNames(vec![font_name])
498 }
499}
500impl ops::Deref for FontNames {
501 type Target = Vec<FontName>;
502
503 fn deref(&self) -> &Self::Target {
504 &self.0
505 }
506}
507impl ops::DerefMut for FontNames {
508 fn deref_mut(&mut self) -> &mut Self::Target {
509 &mut self.0
510 }
511}
512impl std::iter::Extend<FontName> for FontNames {
513 fn extend<T: IntoIterator<Item = FontName>>(&mut self, iter: T) {
514 self.0.extend(iter)
515 }
516}
517impl IntoIterator for FontNames {
518 type Item = FontName;
519
520 type IntoIter = std::vec::IntoIter<FontName>;
521
522 fn into_iter(self) -> Self::IntoIter {
523 self.0.into_iter()
524 }
525}
526impl<const N: usize> From<[FontName; N]> for FontNames {
527 fn from(font_names: [FontName; N]) -> Self {
528 FontNames(font_names.into())
529 }
530}
531impl<const N: usize> IntoVar<FontNames> for [FontName; N] {
532 fn into_var(self) -> Var<FontNames> {
533 const_var(self.into())
534 }
535}
536impl<const N: usize> From<[&'static str; N]> for FontNames {
537 fn from(font_names: [&'static str; N]) -> Self {
538 FontNames(font_names.into_iter().map(FontName::new).collect())
539 }
540}
541impl<const N: usize> IntoVar<FontNames> for [&'static str; N] {
542 fn into_var(self) -> Var<FontNames> {
543 const_var(self.into())
544 }
545}
546impl<const N: usize> From<[String; N]> for FontNames {
547 fn from(font_names: [String; N]) -> Self {
548 FontNames(font_names.into_iter().map(FontName::new).collect())
549 }
550}
551impl<const N: usize> IntoVar<FontNames> for [String; N] {
552 fn into_var(self) -> Var<FontNames> {
553 const_var(self.into())
554 }
555}
556impl<const N: usize> From<[Txt; N]> for FontNames {
557 fn from(font_names: [Txt; N]) -> Self {
558 FontNames(font_names.into_iter().map(FontName::new).collect())
559 }
560}
561impl<const N: usize> IntoVar<FontNames> for [Txt; N] {
562 fn into_var(self) -> Var<FontNames> {
563 const_var(self.into())
564 }
565}
566
567event! {
568 pub static FONT_CHANGED_EVENT: FontChangedArgs;
579}
580
581event_args! {
582 pub struct FontChangedArgs {
584 pub change: FontChange,
586
587 ..
588
589 fn is_in_target(&self, id: WidgetId) -> bool {
591 true
592 }
593 }
594}
595
596#[derive(Clone, Debug, PartialEq)]
598pub enum FontChange {
599 SystemFonts,
603
604 CustomFonts,
609
610 Refresh,
614
615 GenericFont(FontName, Lang),
621
622 Fallback(Lang),
624}
625
626app_local! {
627 static FONTS_SV: FontsService = FontsService::new();
628}
629
630struct FontsService {
631 loader: FontFaceLoader,
632}
633impl FontsService {
634 fn new() -> Self {
635 let s = FontsService {
636 loader: FontFaceLoader::new(),
637 };
638
639 RAW_FONT_CHANGED_EVENT
641 .hook(|args| {
642 FONT_CHANGED_EVENT.notify(FontChangedArgs::new(
643 args.timestamp,
644 args.propagation.clone(),
645 FontChange::SystemFonts,
646 ));
647 true
648 })
649 .perm();
650
651 FONT_CHANGED_EVENT
653 .hook(|_| {
654 let mut s = FONTS_SV.write();
655 s.loader.on_refresh();
656 true
657 })
658 .perm();
659
660 VIEW_PROCESS_INITED_EVENT
662 .hook(|args| {
663 if args.is_respawn {
664 FONTS_SV.write().loader.on_view_process_respawn();
665 }
666 true
667 })
668 .perm();
669
670 s
671 }
672}
673
674pub struct FONTS;
676impl FONTS {
677 pub fn refresh(&self) {
681 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Refresh));
682 }
683
684 pub fn prune(&self) {
686 UPDATES.once_update("FONTS.prune", move || {
687 FONTS_SV.write().loader.on_prune();
688 });
689 }
690
691 pub fn generics(&self) -> &'static GenericFonts {
693 &GenericFonts {}
694 }
695
696 pub fn register(&self, custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
705 let resp = task::respond(FontFace::load_custom(custom_font));
707
708 resp.hook(|args| {
710 if let Some(done) = args.value().done() {
711 if let Ok(face) = done {
712 let mut fonts = FONTS_SV.write();
713 let family = fonts.loader.custom_fonts.entry(face.0.family_name.clone()).or_default();
714 let existing = family
715 .iter()
716 .position(|f| f.0.weight == face.0.weight && f.0.style == face.0.style && f.0.stretch == face.0.stretch);
717
718 if let Some(i) = existing {
719 family[i] = face.clone();
720 } else {
721 family.push(face.clone());
722 }
723
724 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
725 }
726 false
727 } else {
728 true
729 }
730 })
731 .perm();
732
733 resp
734 }
735
736 pub fn unregister(&self, custom_family: FontName) -> ResponseVar<bool> {
740 let (responder, response) = response_var();
741
742 UPDATES.once_update("FONTS.unregister", move || {
743 let mut fonts = FONTS_SV.write();
744 let r = if let Some(removed) = fonts.loader.custom_fonts.remove(&custom_family) {
745 for removed in removed {
749 removed.on_refresh();
750 }
751
752 true
753 } else {
754 false
755 };
756 responder.respond(r);
757
758 if r {
759 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
760 }
761 });
762
763 response
764 }
765
766 pub fn list(
768 &self,
769 families: &[FontName],
770 style: FontStyle,
771 weight: FontWeight,
772 stretch: FontStretch,
773 lang: &Lang,
774 ) -> ResponseVar<FontFaceList> {
775 if let Some(cached) = FONTS_SV.read().loader.try_list(families, style, weight, stretch, lang) {
777 tracing::trace!("font list ({families:?} {style:?} {weight:?} {stretch:?} {lang:?}) found cached");
778 return cached;
779 }
780 tracing::trace!("font list ({families:?} {style:?} {weight:?} {stretch:?} {lang:?}) not cached, load");
781 FONTS_SV.write().loader.load_list(families, style, weight, stretch, lang)
783 }
784
785 pub fn find(
787 &self,
788 family: &FontName,
789 style: FontStyle,
790 weight: FontWeight,
791 stretch: FontStretch,
792 lang: &Lang,
793 ) -> ResponseVar<Option<FontFace>> {
794 if let Some(cached) = FONTS_SV.read().loader.try_cached(family, style, weight, stretch, lang) {
796 return cached;
797 }
798 FONTS_SV.write().loader.load(family, style, weight, stretch, lang)
800 }
801
802 pub fn normal(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
804 self.find(family, FontStyle::Normal, FontWeight::NORMAL, FontStretch::NORMAL, lang)
805 }
806
807 pub fn italic(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
809 self.find(family, FontStyle::Italic, FontWeight::NORMAL, FontStretch::NORMAL, lang)
810 }
811
812 pub fn bold(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
814 self.find(family, FontStyle::Normal, FontWeight::BOLD, FontStretch::NORMAL, lang)
815 }
816
817 pub fn custom_fonts(&self) -> Vec<FontName> {
819 FONTS_SV.read().loader.custom_fonts.keys().cloned().collect()
820 }
821
822 pub fn system_fonts(&self) -> ResponseVar<Vec<FontName>> {
826 query_util::system_all()
827 }
828
829 pub fn system_font_aa(&self) -> Var<FontAntiAliasing> {
833 RAW_FONT_AA_CHANGED_EVENT.var_map(|a| Some(a.aa), || FontAntiAliasing::Default)
834 }
835}
836
837impl<'a> From<ttf_parser::Face<'a>> for FontFaceMetrics {
838 fn from(f: ttf_parser::Face<'a>) -> Self {
839 let underline = f
840 .underline_metrics()
841 .unwrap_or(ttf_parser::LineMetrics { position: 0, thickness: 0 });
842 FontFaceMetrics {
843 units_per_em: f.units_per_em() as _,
844 ascent: f.ascender() as f32,
845 descent: f.descender() as f32,
846 line_gap: f.line_gap() as f32,
847 underline_position: underline.position as f32,
848 underline_thickness: underline.thickness as f32,
849 cap_height: f.capital_height().unwrap_or(0) as f32,
850 x_height: f.x_height().unwrap_or(0) as f32,
851 bounds: euclid::rect(
852 f.global_bounding_box().x_min as f32,
853 f.global_bounding_box().x_max as f32,
854 f.global_bounding_box().width() as f32,
855 f.global_bounding_box().height() as f32,
856 ),
857 }
858 }
859}
860
861#[derive(PartialEq, Eq, Hash)]
862struct FontInstanceKey(Px, Box<[(ttf_parser::Tag, i32)]>);
863impl FontInstanceKey {
864 pub fn new(size: Px, variations: &[rustybuzz::Variation]) -> Self {
866 let variations_key: Vec<_> = variations.iter().map(|p| (p.tag, (p.value * 1000.0) as i32)).collect();
867 FontInstanceKey(size, variations_key.into_boxed_slice())
868 }
869}
870
871#[derive(Clone)]
878pub struct FontFace(Arc<LoadedFontFace>);
879struct LoadedFontFace {
880 data: FontBytes,
881 face_index: u32,
882 display_name: FontName,
883 family_name: FontName,
884 postscript_name: Option<Txt>,
885 style: FontStyle,
886 weight: FontWeight,
887 stretch: FontStretch,
888 metrics: FontFaceMetrics,
889 lig_carets: LigatureCaretList,
890 flags: FontFaceFlags,
891 m: Mutex<FontFaceMut>,
892}
893bitflags! {
894 #[derive(Debug, Clone, Copy)]
895 struct FontFaceFlags: u8 {
896 const IS_MONOSPACE = 0b0000_0001;
897 const HAS_LIGATURES = 0b0000_0010;
898 const HAS_RASTER_IMAGES = 0b0000_0100;
899 const HAS_SVG_IMAGES = 0b0000_1000;
900 }
901}
902struct FontFaceMut {
903 instances: HashMap<FontInstanceKey, Font>,
904 render_ids: Vec<RenderFontFace>,
905 unregistered: bool,
906}
907
908impl fmt::Debug for FontFace {
909 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910 let m = self.0.m.lock();
911 f.debug_struct("FontFace")
912 .field("display_name", &self.0.display_name)
913 .field("family_name", &self.0.family_name)
914 .field("postscript_name", &self.0.postscript_name)
915 .field("flags", &self.0.flags)
916 .field("style", &self.0.style)
917 .field("weight", &self.0.weight)
918 .field("stretch", &self.0.stretch)
919 .field("metrics", &self.0.metrics)
920 .field("instances.len()", &m.instances.len())
921 .field("render_keys.len()", &m.render_ids.len())
922 .field("unregistered", &m.unregistered)
923 .finish_non_exhaustive()
924 }
925}
926impl PartialEq for FontFace {
927 fn eq(&self, other: &Self) -> bool {
928 Arc::ptr_eq(&self.0, &other.0)
929 }
930}
931impl Eq for FontFace {}
932impl FontFace {
933 pub fn empty() -> Self {
935 FontFace(Arc::new(LoadedFontFace {
936 data: FontBytes::from_static(&[]),
937 face_index: 0,
938 display_name: FontName::from("<empty>"),
939 family_name: FontName::from("<empty>"),
940 postscript_name: None,
941 flags: FontFaceFlags::IS_MONOSPACE,
942 style: FontStyle::Normal,
943 weight: FontWeight::NORMAL,
944 stretch: FontStretch::NORMAL,
945 metrics: FontFaceMetrics {
947 units_per_em: 2048,
948 ascent: 1616.0,
949 descent: -432.0,
950 line_gap: 0.0,
951 underline_position: -205.0,
952 underline_thickness: 102.0,
953 cap_height: 1616.0,
954 x_height: 1616.0,
955 bounds: euclid::Box2D::new(euclid::point2(0.0, -432.0), euclid::point2(1291.0, 1616.0)).to_rect(),
957 },
958 lig_carets: LigatureCaretList::empty(),
959 m: Mutex::new(FontFaceMut {
960 instances: HashMap::default(),
961 render_ids: vec![],
962 unregistered: false,
963 }),
964 }))
965 }
966
967 pub fn is_empty(&self) -> bool {
969 self.0.data.is_empty()
970 }
971
972 async fn load_custom(custom_font: CustomFont) -> Result<Self, FontLoadingError> {
973 let bytes;
974 let mut face_index;
975
976 match custom_font.source {
977 FontSource::File(path, index) => {
978 bytes = task::wait(|| FontBytes::from_file(path)).await?;
979 face_index = index;
980 }
981 FontSource::Memory(arc, index) => {
982 bytes = arc;
983 face_index = index;
984 }
985 FontSource::Alias(other_font) => {
986 let result = FONTS_SV
987 .write()
988 .loader
989 .load_resolved(&other_font, custom_font.style, custom_font.weight, custom_font.stretch);
990 return match result.wait_rsp().await {
991 Some(other_font) => Ok(FontFace(Arc::new(LoadedFontFace {
992 data: other_font.0.data.clone(),
993 face_index: other_font.0.face_index,
994 display_name: custom_font.name.clone(),
995 family_name: custom_font.name,
996 postscript_name: None,
997 style: other_font.0.style,
998 weight: other_font.0.weight,
999 stretch: other_font.0.stretch,
1000 metrics: other_font.0.metrics.clone(),
1001 m: Mutex::new(FontFaceMut {
1002 instances: Default::default(),
1003 render_ids: Default::default(),
1004 unregistered: Default::default(),
1005 }),
1006 lig_carets: other_font.0.lig_carets.clone(),
1007 flags: other_font.0.flags,
1008 }))),
1009 None => Err(FontLoadingError::NoSuchFontInCollection),
1010 };
1011 }
1012 }
1013
1014 let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1015 Ok(f) => f,
1016 Err(e) => {
1017 match e {
1018 ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1020 e => return Err(FontLoadingError::Parse(e)),
1021 }
1022
1023 match ttf_parser::Face::parse(&bytes, face_index) {
1024 Ok(f) => f,
1025 Err(_) => return Err(FontLoadingError::Parse(e)),
1026 }
1027 }
1028 };
1029
1030 let has_ligatures = ttf_face.tables().gsub.is_some();
1031 let lig_carets = if has_ligatures {
1032 LigatureCaretList::empty()
1033 } else {
1034 LigatureCaretList::load(ttf_face.raw_face())?
1035 };
1036
1037 let has_raster_images = {
1039 let t = ttf_face.tables();
1040 t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1041 };
1042
1043 let mut flags = FontFaceFlags::empty();
1044 flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1045 flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1046 flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1047 flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1048
1049 Ok(FontFace(Arc::new(LoadedFontFace {
1050 face_index,
1051 display_name: custom_font.name.clone(),
1052 family_name: custom_font.name,
1053 postscript_name: None,
1054 style: custom_font.style,
1055 weight: custom_font.weight,
1056 stretch: custom_font.stretch,
1057 metrics: ttf_face.into(),
1058 lig_carets,
1059 m: Mutex::new(FontFaceMut {
1060 instances: Default::default(),
1061 render_ids: Default::default(),
1062 unregistered: Default::default(),
1063 }),
1064 data: bytes,
1065 flags,
1066 })))
1067 }
1068
1069 fn load(bytes: FontBytes, mut face_index: u32) -> Result<Self, FontLoadingError> {
1070 let _span = tracing::trace_span!("FontFace::load").entered();
1071
1072 let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1073 Ok(f) => f,
1074 Err(e) => {
1075 match e {
1076 ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1078 e => return Err(FontLoadingError::Parse(e)),
1079 }
1080
1081 match ttf_parser::Face::parse(&bytes, face_index) {
1082 Ok(f) => f,
1083 Err(_) => return Err(FontLoadingError::Parse(e)),
1084 }
1085 }
1086 };
1087
1088 let has_ligatures = ttf_face.tables().gsub.is_some();
1089 let lig_carets = if has_ligatures {
1090 LigatureCaretList::empty()
1091 } else {
1092 LigatureCaretList::load(ttf_face.raw_face())?
1093 };
1094
1095 let mut display_name = None;
1096 let mut family_name = None;
1097 let mut postscript_name = None;
1098 let mut any_name = None::<String>;
1099 for name in ttf_face.names() {
1100 if let Some(n) = name.to_string() {
1101 match name.name_id {
1102 ttf_parser::name_id::FULL_NAME => display_name = Some(n),
1103 ttf_parser::name_id::FAMILY => family_name = Some(n),
1104 ttf_parser::name_id::POST_SCRIPT_NAME => postscript_name = Some(n),
1105 _ => match &mut any_name {
1106 Some(s) => {
1107 if n.len() > s.len() {
1108 *s = n;
1109 }
1110 }
1111 None => any_name = Some(n),
1112 },
1113 }
1114 }
1115 }
1116 let display_name = FontName::new(Txt::from_str(
1117 display_name
1118 .as_ref()
1119 .or(family_name.as_ref())
1120 .or(postscript_name.as_ref())
1121 .or(any_name.as_ref())
1122 .unwrap(),
1123 ));
1124 let family_name = family_name.map(FontName::from).unwrap_or_else(|| display_name.clone());
1125 let postscript_name = postscript_name.map(Txt::from);
1126
1127 if ttf_face.units_per_em() == 0 {
1128 tracing::debug!("font {display_name:?} units_per_em 0");
1130 return Err(FontLoadingError::UnknownFormat);
1131 }
1132
1133 let has_raster_images = {
1135 let t = ttf_face.tables();
1136 t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1137 };
1138
1139 let mut flags = FontFaceFlags::empty();
1140 flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1141 flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1142 flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1143 flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1144
1145 Ok(FontFace(Arc::new(LoadedFontFace {
1146 face_index,
1147 family_name,
1148 display_name,
1149 postscript_name,
1150 style: ttf_face.style().into(),
1151 weight: ttf_face.weight().into(),
1152 stretch: ttf_face.width().into(),
1153 metrics: ttf_face.into(),
1154 lig_carets,
1155 m: Mutex::new(FontFaceMut {
1156 instances: Default::default(),
1157 render_ids: Default::default(),
1158 unregistered: Default::default(),
1159 }),
1160 data: bytes,
1161 flags,
1162 })))
1163 }
1164
1165 fn on_refresh(&self) {
1166 let mut m = self.0.m.lock();
1167 m.instances.clear();
1168 m.unregistered = true;
1169 }
1170
1171 fn render_face(&self, renderer: &ViewRenderer) -> zng_view_api::font::FontFaceId {
1172 let mut m = self.0.m.lock();
1173 for r in m.render_ids.iter() {
1174 if &r.renderer == renderer {
1175 return r.face_id;
1176 }
1177 }
1178
1179 let data = match self.0.data.to_ipc() {
1180 Ok(d) => d,
1181 Err(e) => {
1182 tracing::error!("cannot allocate ipc font data, {e}");
1183 return zng_view_api::font::FontFaceId::INVALID;
1184 }
1185 };
1186
1187 let key = match renderer.add_font_face(data, self.0.face_index) {
1188 Ok(k) => k,
1189 Err(_) => {
1190 tracing::debug!("respawned calling `add_font`, will return dummy font key");
1191 return zng_view_api::font::FontFaceId::INVALID;
1192 }
1193 };
1194
1195 m.render_ids.push(RenderFontFace::new(renderer, key));
1196
1197 key
1198 }
1199
1200 pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1209 if self.is_empty() {
1210 None
1211 } else {
1212 Some(rustybuzz::Face::from_slice(&self.0.data, self.0.face_index).unwrap())
1213 }
1214 }
1215
1216 pub fn ttf(&self) -> Option<ttf_parser::Face<'_>> {
1225 if self.is_empty() {
1226 None
1227 } else {
1228 Some(ttf_parser::Face::parse(&self.0.data, self.0.face_index).unwrap())
1229 }
1230 }
1231
1232 pub fn bytes(&self) -> &FontBytes {
1234 &self.0.data
1235 }
1236 pub fn index(&self) -> u32 {
1238 self.0.face_index
1239 }
1240
1241 pub fn display_name(&self) -> &FontName {
1243 &self.0.display_name
1244 }
1245
1246 pub fn family_name(&self) -> &FontName {
1248 &self.0.family_name
1249 }
1250
1251 pub fn postscript_name(&self) -> Option<&str> {
1253 self.0.postscript_name.as_deref()
1254 }
1255
1256 pub fn style(&self) -> FontStyle {
1258 self.0.style
1259 }
1260
1261 pub fn weight(&self) -> FontWeight {
1263 self.0.weight
1264 }
1265
1266 pub fn stretch(&self) -> FontStretch {
1268 self.0.stretch
1269 }
1270
1271 pub fn is_monospace(&self) -> bool {
1273 self.0.flags.contains(FontFaceFlags::IS_MONOSPACE)
1274 }
1275
1276 pub fn metrics(&self) -> &FontFaceMetrics {
1278 &self.0.metrics
1279 }
1280
1281 pub fn sized(&self, font_size: Px, variations: RFontVariations) -> Font {
1290 let key = FontInstanceKey::new(font_size, &variations);
1291 let mut m = self.0.m.lock();
1292 if !m.unregistered {
1293 m.instances
1294 .entry(key)
1295 .or_insert_with(|| Font::new(self.clone(), font_size, variations))
1296 .clone()
1297 } else {
1298 tracing::debug!(target: "font_loading", "creating font from unregistered `{}`, will not cache", self.0.display_name);
1299 Font::new(self.clone(), font_size, variations)
1300 }
1301 }
1302
1303 pub fn synthesis_for(&self, style: FontStyle, weight: FontWeight) -> FontSynthesis {
1305 let mut synth = FontSynthesis::DISABLED;
1306
1307 if style != FontStyle::Normal && self.style() == FontStyle::Normal {
1308 synth |= FontSynthesis::OBLIQUE;
1310 }
1311 if weight > self.weight() {
1312 synth |= FontSynthesis::BOLD;
1315 }
1316
1317 synth
1318 }
1319
1320 pub fn is_cached(&self) -> bool {
1324 !self.0.m.lock().unregistered
1325 }
1326
1327 pub fn color_palettes(&self) -> ColorPalettes<'_> {
1331 match self.ttf() {
1332 Some(ttf) => ColorPalettes::new(*ttf.raw_face()),
1333 None => ColorPalettes::empty(),
1334 }
1335 }
1336
1337 pub fn color_glyphs(&self) -> ColorGlyphs<'_> {
1341 match self.ttf() {
1342 Some(ttf) => ColorGlyphs::new(*ttf.raw_face()),
1343 None => ColorGlyphs::empty(),
1344 }
1345 }
1346
1347 pub fn has_ligatures(&self) -> bool {
1349 self.0.flags.contains(FontFaceFlags::HAS_LIGATURES)
1350 }
1351
1352 pub fn has_ligature_caret_offsets(&self) -> bool {
1357 !self.0.lig_carets.is_empty()
1358 }
1359
1360 pub fn has_raster_images(&self) -> bool {
1362 self.0.flags.contains(FontFaceFlags::HAS_RASTER_IMAGES)
1363 }
1364
1365 pub fn has_svg_images(&self) -> bool {
1367 self.0.flags.contains(FontFaceFlags::HAS_SVG_IMAGES)
1368 }
1369}
1370
1371#[derive(Clone)]
1377pub struct Font(Arc<LoadedFont>);
1378struct LoadedFont {
1379 face: FontFace,
1380 size: Px,
1381 variations: RFontVariations,
1382 metrics: FontMetrics,
1383 render_keys: Mutex<Vec<RenderFont>>,
1384 small_word_cache: RwLock<HashMap<WordCacheKey<[u8; Font::SMALL_WORD_LEN]>, ShapedSegmentData>>,
1385 word_cache: RwLock<HashMap<WordCacheKey<String>, ShapedSegmentData>>,
1386}
1387impl fmt::Debug for Font {
1388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1389 f.debug_struct("Font")
1390 .field("face", &self.0.face)
1391 .field("size", &self.0.size)
1392 .field("metrics", &self.0.metrics)
1393 .field("render_keys.len()", &self.0.render_keys.lock().len())
1394 .field("small_word_cache.len()", &self.0.small_word_cache.read().len())
1395 .field("word_cache.len()", &self.0.word_cache.read().len())
1396 .finish()
1397 }
1398}
1399impl PartialEq for Font {
1400 fn eq(&self, other: &Self) -> bool {
1401 Arc::ptr_eq(&self.0, &other.0)
1402 }
1403}
1404impl Eq for Font {}
1405impl Font {
1406 const SMALL_WORD_LEN: usize = 8;
1407
1408 fn to_small_word(s: &str) -> Option<[u8; Self::SMALL_WORD_LEN]> {
1409 if s.len() <= Self::SMALL_WORD_LEN {
1410 let mut a = [b'\0'; Self::SMALL_WORD_LEN];
1411 a[..s.len()].copy_from_slice(s.as_bytes());
1412 Some(a)
1413 } else {
1414 None
1415 }
1416 }
1417
1418 fn new(face: FontFace, size: Px, variations: RFontVariations) -> Self {
1419 Font(Arc::new(LoadedFont {
1420 metrics: face.metrics().sized(size),
1421 face,
1422 size,
1423 variations,
1424 render_keys: Mutex::new(vec![]),
1425 small_word_cache: RwLock::default(),
1426 word_cache: RwLock::default(),
1427 }))
1428 }
1429
1430 fn render_font(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1431 let _span = tracing::trace_span!("Font::render_font").entered();
1432
1433 let mut render_keys = self.0.render_keys.lock();
1434 for r in render_keys.iter() {
1435 if &r.renderer == renderer && r.synthesis == synthesis {
1436 return r.font_id;
1437 }
1438 }
1439
1440 let font_key = self.0.face.render_face(renderer);
1441
1442 let mut opt = zng_view_api::font::FontOptions::default();
1443 opt.synthetic_oblique = synthesis.contains(FontSynthesis::OBLIQUE);
1444 opt.synthetic_bold = synthesis.contains(FontSynthesis::BOLD);
1445 let variations = self.0.variations.iter().map(|v| (v.tag.to_bytes(), v.value)).collect();
1446
1447 let key = match renderer.add_font(font_key, self.0.size, opt, variations) {
1448 Ok(k) => k,
1449 Err(_) => {
1450 tracing::debug!("respawned calling `add_font_instance`, will return dummy font key");
1451 return zng_view_api::font::FontId::INVALID;
1452 }
1453 };
1454
1455 render_keys.push(RenderFont::new(renderer, synthesis, key));
1456
1457 key
1458 }
1459
1460 pub fn face(&self) -> &FontFace {
1462 &self.0.face
1463 }
1464
1465 pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1467 let ppem = self.0.size.0 as u16;
1468
1469 let mut font = self.0.face.harfbuzz()?;
1470
1471 font.set_pixels_per_em(Some((ppem, ppem)));
1472 font.set_variations(&self.0.variations);
1473
1474 Some(font)
1475 }
1476
1477 pub fn size(&self) -> Px {
1481 self.0.size
1482 }
1483
1484 pub fn variations(&self) -> &RFontVariations {
1486 &self.0.variations
1487 }
1488
1489 pub fn metrics(&self) -> &FontMetrics {
1491 &self.0.metrics
1492 }
1493
1494 pub fn ligature_caret_offsets(
1500 &self,
1501 lig: zng_view_api::font::GlyphIndex,
1502 ) -> impl ExactSizeIterator<Item = f32> + DoubleEndedIterator + '_ {
1503 let face = &self.0.face.0;
1504 face.lig_carets.carets(lig).iter().map(move |&o| match o {
1505 ligature_util::LigatureCaret::Coordinate(o) => {
1506 let size_scale = 1.0 / face.metrics.units_per_em as f32 * self.0.size.0 as f32;
1507 o as f32 * size_scale
1508 }
1509 ligature_util::LigatureCaret::GlyphContourPoint(i) => {
1510 if let Some(f) = self.harfbuzz() {
1511 struct Search {
1512 i: u16,
1513 s: u16,
1514 x: f32,
1515 }
1516 impl Search {
1517 fn check(&mut self, x: f32) {
1518 self.s = self.s.saturating_add(1);
1519 if self.s == self.i {
1520 self.x = x;
1521 }
1522 }
1523 }
1524 impl ttf_parser::OutlineBuilder for Search {
1525 fn move_to(&mut self, x: f32, _y: f32) {
1526 self.check(x);
1527 }
1528
1529 fn line_to(&mut self, x: f32, _y: f32) {
1530 self.check(x);
1531 }
1532
1533 fn quad_to(&mut self, _x1: f32, _y1: f32, x: f32, _y: f32) {
1534 self.check(x)
1535 }
1536
1537 fn curve_to(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, x: f32, _y: f32) {
1538 self.check(x);
1539 }
1540
1541 fn close(&mut self) {}
1542 }
1543 let mut search = Search { i, s: 0, x: 0.0 };
1544 if f.outline_glyph(ttf_parser::GlyphId(lig as _), &mut search).is_some() && search.s >= search.i {
1545 return search.x * self.0.metrics.size_scale;
1546 }
1547 }
1548 0.0
1549 }
1550 })
1551 }
1552}
1553impl zng_app::render::Font for Font {
1554 fn is_empty_fallback(&self) -> bool {
1555 self.face().is_empty()
1556 }
1557
1558 fn renderer_id(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1559 self.render_font(renderer, synthesis)
1560 }
1561}
1562
1563#[derive(Debug, Clone)]
1567pub struct FontFaceList {
1568 fonts: Box<[FontFace]>,
1569 requested_style: FontStyle,
1570 requested_weight: FontWeight,
1571 requested_stretch: FontStretch,
1572}
1573impl FontFaceList {
1574 pub fn empty() -> Self {
1576 Self {
1577 fonts: Box::new([FontFace::empty()]),
1578 requested_style: FontStyle::Normal,
1579 requested_weight: FontWeight::NORMAL,
1580 requested_stretch: FontStretch::NORMAL,
1581 }
1582 }
1583
1584 pub fn requested_style(&self) -> FontStyle {
1586 self.requested_style
1587 }
1588
1589 pub fn requested_weight(&self) -> FontWeight {
1591 self.requested_weight
1592 }
1593
1594 pub fn requested_stretch(&self) -> FontStretch {
1596 self.requested_stretch
1597 }
1598
1599 pub fn best(&self) -> &FontFace {
1601 &self.fonts[0]
1602 }
1603
1604 pub fn face_synthesis(&self, face_index: usize) -> FontSynthesis {
1606 if let Some(face) = self.fonts.get(face_index) {
1607 face.synthesis_for(self.requested_style, self.requested_weight)
1608 } else {
1609 FontSynthesis::DISABLED
1610 }
1611 }
1612
1613 pub fn iter(&self) -> std::slice::Iter<'_, FontFace> {
1615 self.fonts.iter()
1616 }
1617
1618 pub fn len(&self) -> usize {
1622 self.fonts.len()
1623 }
1624
1625 pub fn is_empty(&self) -> bool {
1627 self.fonts[0].is_empty() && self.fonts.len() == 1
1628 }
1629
1630 pub fn sized(&self, font_size: Px, variations: RFontVariations) -> FontList {
1634 FontList {
1635 fonts: self.fonts.iter().map(|f| f.sized(font_size, variations.clone())).collect(),
1636 requested_style: self.requested_style,
1637 requested_weight: self.requested_weight,
1638 requested_stretch: self.requested_stretch,
1639 }
1640 }
1641}
1642impl PartialEq for FontFaceList {
1643 fn eq(&self, other: &Self) -> bool {
1645 self.requested_style == other.requested_style
1646 && self.requested_weight == other.requested_weight
1647 && self.requested_stretch == other.requested_stretch
1648 && self.fonts.len() == other.fonts.len()
1649 && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1650 }
1651}
1652impl Eq for FontFaceList {}
1653impl std::ops::Deref for FontFaceList {
1654 type Target = [FontFace];
1655
1656 fn deref(&self) -> &Self::Target {
1657 &self.fonts
1658 }
1659}
1660impl<'a> std::iter::IntoIterator for &'a FontFaceList {
1661 type Item = &'a FontFace;
1662
1663 type IntoIter = std::slice::Iter<'a, FontFace>;
1664
1665 fn into_iter(self) -> Self::IntoIter {
1666 self.iter()
1667 }
1668}
1669impl std::ops::Index<usize> for FontFaceList {
1670 type Output = FontFace;
1671
1672 fn index(&self, index: usize) -> &Self::Output {
1673 &self.fonts[index]
1674 }
1675}
1676
1677#[derive(Debug, Clone)]
1679pub struct FontList {
1680 fonts: Box<[Font]>,
1681 requested_style: FontStyle,
1682 requested_weight: FontWeight,
1683 requested_stretch: FontStretch,
1684}
1685#[expect(clippy::len_without_is_empty)] impl FontList {
1687 pub fn best(&self) -> &Font {
1689 &self.fonts[0]
1690 }
1691
1692 pub fn requested_size(&self) -> Px {
1694 self.fonts[0].size()
1695 }
1696
1697 pub fn requested_style(&self) -> FontStyle {
1699 self.requested_style
1700 }
1701
1702 pub fn requested_weight(&self) -> FontWeight {
1704 self.requested_weight
1705 }
1706
1707 pub fn requested_stretch(&self) -> FontStretch {
1709 self.requested_stretch
1710 }
1711
1712 pub fn face_synthesis(&self, font_index: usize) -> FontSynthesis {
1714 if let Some(font) = self.fonts.get(font_index) {
1715 font.0.face.synthesis_for(self.requested_style, self.requested_weight)
1716 } else {
1717 FontSynthesis::DISABLED
1718 }
1719 }
1720
1721 pub fn iter(&self) -> std::slice::Iter<'_, Font> {
1723 self.fonts.iter()
1724 }
1725
1726 pub fn len(&self) -> usize {
1730 self.fonts.len()
1731 }
1732
1733 pub fn is_sized_from(&self, faces: &FontFaceList) -> bool {
1735 if self.len() != faces.len() {
1736 return false;
1737 }
1738
1739 for (font, face) in self.iter().zip(faces.iter()) {
1740 if font.face() != face {
1741 return false;
1742 }
1743 }
1744
1745 true
1746 }
1747}
1748impl PartialEq for FontList {
1749 fn eq(&self, other: &Self) -> bool {
1751 self.requested_style == other.requested_style
1752 && self.requested_weight == other.requested_weight
1753 && self.requested_stretch == other.requested_stretch
1754 && self.fonts.len() == other.fonts.len()
1755 && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1756 }
1757}
1758impl Eq for FontList {}
1759impl std::ops::Deref for FontList {
1760 type Target = [Font];
1761
1762 fn deref(&self) -> &Self::Target {
1763 &self.fonts
1764 }
1765}
1766impl<'a> std::iter::IntoIterator for &'a FontList {
1767 type Item = &'a Font;
1768
1769 type IntoIter = std::slice::Iter<'a, Font>;
1770
1771 fn into_iter(self) -> Self::IntoIter {
1772 self.iter()
1773 }
1774}
1775impl<I: SliceIndex<[Font]>> std::ops::Index<I> for FontList {
1776 type Output = I::Output;
1777
1778 fn index(&self, index: I) -> &I::Output {
1779 &self.fonts[index]
1780 }
1781}
1782
1783struct FontFaceLoader {
1784 custom_fonts: HashMap<FontName, Vec<FontFace>>,
1785
1786 system_fonts_cache: HashMap<FontName, Vec<SystemFontFace>>,
1787 list_cache: HashMap<Box<[FontName]>, Vec<FontFaceListQuery>>,
1788}
1789struct SystemFontFace {
1790 properties: (FontStyle, FontWeight, FontStretch),
1791 result: ResponseVar<Option<FontFace>>,
1792}
1793struct FontFaceListQuery {
1794 properties: (FontStyle, FontWeight, FontStretch),
1795 lang: Lang,
1796 result: ResponseVar<FontFaceList>,
1797}
1798impl FontFaceLoader {
1799 fn new() -> Self {
1800 FontFaceLoader {
1801 custom_fonts: HashMap::new(),
1802 system_fonts_cache: HashMap::new(),
1803 list_cache: HashMap::new(),
1804 }
1805 }
1806
1807 fn on_view_process_respawn(&mut self) {
1808 let sys_fonts = self.system_fonts_cache.values().flatten().filter_map(|f| f.result.rsp().flatten());
1809 for face in self.custom_fonts.values().flatten().cloned().chain(sys_fonts) {
1810 let mut m = face.0.m.lock();
1811 m.render_ids.clear();
1812 for inst in m.instances.values() {
1813 inst.0.render_keys.lock().clear();
1814 }
1815 }
1816 }
1817
1818 fn on_refresh(&mut self) {
1819 for (_, sys_family) in self.system_fonts_cache.drain() {
1820 for sys_font in sys_family {
1821 sys_font.result.with(|r| {
1822 if let Some(Some(face)) = r.done() {
1823 face.on_refresh();
1824 }
1825 });
1826 }
1827 }
1828 }
1829 fn on_prune(&mut self) {
1830 self.system_fonts_cache.retain(|_, v| {
1831 v.retain(|sff| {
1832 if sff.result.strong_count() == 1 {
1833 sff.result.with(|r| {
1834 match r.done() {
1835 Some(Some(face)) => Arc::strong_count(&face.0) > 1, Some(None) => false, None => true, }
1839 })
1840 } else {
1841 true
1843 }
1844 });
1845 !v.is_empty()
1846 });
1847
1848 self.list_cache.clear();
1849 }
1850
1851 fn try_list(
1852 &self,
1853 families: &[FontName],
1854 style: FontStyle,
1855 weight: FontWeight,
1856 stretch: FontStretch,
1857 lang: &Lang,
1858 ) -> Option<ResponseVar<FontFaceList>> {
1859 if let Some(queries) = self.list_cache.get(families) {
1860 for q in queries {
1861 if q.properties == (style, weight, stretch) && &q.lang == lang {
1862 return Some(q.result.clone());
1863 }
1864 }
1865 }
1866 None
1867 }
1868
1869 fn load_list(
1870 &mut self,
1871 families: &[FontName],
1872 style: FontStyle,
1873 weight: FontWeight,
1874 stretch: FontStretch,
1875 lang: &Lang,
1876 ) -> ResponseVar<FontFaceList> {
1877 if let Some(r) = self.try_list(families, style, weight, stretch, lang) {
1878 return r;
1879 }
1880
1881 let mut list = Vec::with_capacity(families.len() + 1);
1882 let mut pending = vec![];
1883
1884 {
1885 let fallback = [GenericFonts {}.fallback(lang)];
1886 let mut used = HashSet::with_capacity(families.len());
1887 for name in families.iter().chain(&fallback) {
1888 if !used.insert(name) {
1889 continue;
1890 }
1891
1892 let face = self.load(name, style, weight, stretch, lang);
1893 if face.is_done() {
1894 if let Some(face) = face.rsp().unwrap() {
1895 list.push(face);
1896 }
1897 } else {
1898 pending.push((list.len(), face));
1899 }
1900 }
1901 }
1902
1903 let r = if pending.is_empty() {
1904 if list.is_empty() {
1905 tracing::error!(target: "font_loading", "failed to load fallback font");
1906 list.push(FontFace::empty());
1907 }
1908 response_done_var(FontFaceList {
1909 fonts: list.into_boxed_slice(),
1910 requested_style: style,
1911 requested_weight: weight,
1912 requested_stretch: stretch,
1913 })
1914 } else {
1915 task::respond(async move {
1916 for (i, pending) in pending.into_iter().rev() {
1917 if let Some(rsp) = pending.wait_rsp().await {
1918 list.insert(i, rsp);
1919 }
1920 }
1921
1922 if list.is_empty() {
1923 tracing::error!(target: "font_loading", "failed to load fallback font");
1924 list.push(FontFace::empty());
1925 }
1926
1927 FontFaceList {
1928 fonts: list.into_boxed_slice(),
1929 requested_style: style,
1930 requested_weight: weight,
1931 requested_stretch: stretch,
1932 }
1933 })
1934 };
1935
1936 self.list_cache
1937 .entry(families.iter().cloned().collect())
1938 .or_insert_with(|| Vec::with_capacity(1))
1939 .push(FontFaceListQuery {
1940 properties: (style, weight, stretch),
1941 lang: lang.clone(),
1942 result: r.clone(),
1943 });
1944
1945 r
1946 }
1947
1948 fn try_cached(
1949 &self,
1950 font_name: &FontName,
1951 style: FontStyle,
1952 weight: FontWeight,
1953 stretch: FontStretch,
1954 lang: &Lang,
1955 ) -> Option<ResponseVar<Option<FontFace>>> {
1956 let resolved = GenericFonts {}.resolve(font_name, lang);
1957 let font_name = resolved.as_ref().unwrap_or(font_name);
1958 self.try_resolved(font_name, style, weight, stretch)
1959 }
1960
1961 fn load(
1963 &mut self,
1964 font_name: &FontName,
1965 style: FontStyle,
1966 weight: FontWeight,
1967 stretch: FontStretch,
1968 lang: &Lang,
1969 ) -> ResponseVar<Option<FontFace>> {
1970 let resolved = GenericFonts {}.resolve(font_name, lang);
1971 let font_name = resolved.as_ref().unwrap_or(font_name);
1972 self.load_resolved(font_name, style, weight, stretch)
1973 }
1974
1975 fn try_resolved(
1977 &self,
1978 font_name: &FontName,
1979 style: FontStyle,
1980 weight: FontWeight,
1981 stretch: FontStretch,
1982 ) -> Option<ResponseVar<Option<FontFace>>> {
1983 if let Some(custom_family) = self.custom_fonts.get(font_name) {
1984 let custom = Self::match_custom(custom_family, style, weight, stretch);
1985 return Some(response_done_var(Some(custom)));
1986 }
1987
1988 if let Some(cached_sys_family) = self.system_fonts_cache.get(font_name) {
1989 for sys_face in cached_sys_family.iter() {
1990 if sys_face.properties == (style, weight, stretch) {
1991 return Some(sys_face.result.clone());
1992 }
1993 }
1994 }
1995
1996 None
1997 }
1998
1999 fn load_resolved(
2001 &mut self,
2002 font_name: &FontName,
2003 style: FontStyle,
2004 weight: FontWeight,
2005 stretch: FontStretch,
2006 ) -> ResponseVar<Option<FontFace>> {
2007 if let Some(cached) = self.try_resolved(font_name, style, weight, stretch) {
2008 return cached;
2009 }
2010
2011 let load = task::wait(clmv!(font_name, || {
2012 let (bytes, face_index) = match Self::get_system(&font_name, style, weight, stretch) {
2013 Some(h) => h,
2014 None => {
2015 #[cfg(debug_assertions)]
2016 static NOT_FOUND: Mutex<Option<HashSet<FontName>>> = Mutex::new(None);
2017
2018 #[cfg(debug_assertions)]
2019 if NOT_FOUND.lock().get_or_insert_with(HashSet::default).insert(font_name.clone()) {
2020 tracing::debug!(r#"font "{font_name}" not found"#);
2021 }
2022
2023 return None;
2024 }
2025 };
2026 match FontFace::load(bytes, face_index) {
2027 Ok(f) => Some(f),
2028 Err(FontLoadingError::UnknownFormat) => None,
2029 Err(e) => {
2030 tracing::error!(target: "font_loading", "failed to load system font, {e}\nquery: {:?}", (font_name, style, weight, stretch));
2031 None
2032 }
2033 }
2034 }));
2035 let result = task::respond(async_clmv!(font_name, {
2036 match task::with_deadline(load, 10.secs()).await {
2037 Ok(r) => r,
2038 Err(_) => {
2039 tracing::error!(target: "font_loading", "timeout loading {font_name:?}");
2040 None
2041 }
2042 }
2043 }));
2044
2045 self.system_fonts_cache
2046 .entry(font_name.clone())
2047 .or_insert_with(|| Vec::with_capacity(1))
2048 .push(SystemFontFace {
2049 properties: (style, weight, stretch),
2050 result: result.clone(),
2051 });
2052
2053 result
2054 }
2055
2056 fn get_system(font_name: &FontName, style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Option<(FontBytes, u32)> {
2057 let _span = tracing::trace_span!("FontFaceLoader::get_system").entered();
2058 match query_util::best(font_name, style, weight, stretch) {
2059 Ok(r) => r,
2060 Err(e) => {
2061 tracing::error!("cannot get `{font_name}` system font, {e}");
2062 None
2063 }
2064 }
2065 }
2066
2067 fn match_custom(faces: &[FontFace], style: FontStyle, weight: FontWeight, stretch: FontStretch) -> FontFace {
2068 if faces.len() == 1 {
2069 return faces[0].clone();
2071 }
2072
2073 let mut set = Vec::with_capacity(faces.len());
2074 let mut set_dist = 0.0f64; let wrong_side = if stretch <= FontStretch::NORMAL {
2081 |s| s > FontStretch::NORMAL
2082 } else {
2083 |s| s <= FontStretch::NORMAL
2084 };
2085 for face in faces {
2086 let mut dist = (face.stretch().0 - stretch.0).abs() as f64;
2087 if wrong_side(face.stretch()) {
2088 dist += f32::MAX as f64 + 1.0;
2089 }
2090
2091 if set.is_empty() {
2092 set.push(face);
2093 set_dist = dist;
2094 } else if dist < set_dist {
2095 set_dist = dist;
2097 set.clear();
2098 set.push(face);
2099 } else if (dist - set_dist).abs() < 0.0001 {
2100 set.push(face);
2102 }
2103 }
2104 if set.len() == 1 {
2105 return set[0].clone();
2106 }
2107
2108 let style_pref = match style {
2113 FontStyle::Normal => [FontStyle::Normal, FontStyle::Oblique, FontStyle::Italic],
2114 FontStyle::Italic => [FontStyle::Italic, FontStyle::Oblique, FontStyle::Normal],
2115 FontStyle::Oblique => [FontStyle::Oblique, FontStyle::Italic, FontStyle::Normal],
2116 };
2117 let mut best_style = style_pref.len();
2118 for face in &set {
2119 let i = style_pref.iter().position(|&s| s == face.style()).unwrap();
2120 if i < best_style {
2121 best_style = i;
2122 }
2123 }
2124 set.retain(|f| f.style() == style_pref[best_style]);
2125 if set.len() == 1 {
2126 return set[0].clone();
2127 }
2128
2129 let add_penalty = if weight.0 >= 400.0 && weight.0 <= 500.0 {
2137 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2139 if face.weight() < weight {
2141 *dist += 100.0;
2143 } else if face.weight().0 > 500.0 {
2144 *dist += 600.0;
2146 }
2147 }
2148 } else if weight.0 < 400.0 {
2149 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2151 if face.weight() > weight {
2152 *dist += weight.0 as f64;
2153 }
2154 }
2155 } else {
2156 debug_assert!(weight.0 > 500.0);
2157 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2159 if face.weight() < weight {
2160 *dist += f32::MAX as f64;
2161 }
2162 }
2163 };
2164
2165 let mut best = set[0];
2166 let mut best_dist = f64::MAX;
2167
2168 for face in &set {
2169 let mut dist = (face.weight().0 - weight.0).abs() as f64;
2170
2171 add_penalty(face, weight, &mut dist);
2172
2173 if dist < best_dist {
2174 best_dist = dist;
2175 best = face;
2176 }
2177 }
2178
2179 best.clone()
2180 }
2181}
2182
2183struct RenderFontFace {
2184 renderer: ViewRenderer,
2185 face_id: zng_view_api::font::FontFaceId,
2186}
2187impl RenderFontFace {
2188 fn new(renderer: &ViewRenderer, face_id: zng_view_api::font::FontFaceId) -> Self {
2189 RenderFontFace {
2190 renderer: renderer.clone(),
2191 face_id,
2192 }
2193 }
2194}
2195impl Drop for RenderFontFace {
2196 fn drop(&mut self) {
2197 let _ = self.renderer.delete_font_face(self.face_id);
2199 }
2200}
2201
2202struct RenderFont {
2203 renderer: ViewRenderer,
2204 synthesis: FontSynthesis,
2205 font_id: zng_view_api::font::FontId,
2206}
2207impl RenderFont {
2208 fn new(renderer: &ViewRenderer, synthesis: FontSynthesis, font_id: zng_view_api::font::FontId) -> RenderFont {
2209 RenderFont {
2210 renderer: renderer.clone(),
2211 synthesis,
2212 font_id,
2213 }
2214 }
2215}
2216impl Drop for RenderFont {
2217 fn drop(&mut self) {
2218 let _ = self.renderer.delete_font(self.font_id);
2220 }
2221}
2222
2223app_local! {
2224 static GENERIC_FONTS_SV: GenericFontsService = GenericFontsService::new();
2225}
2226
2227struct GenericFontsService {
2228 serif: LangMap<FontName>,
2229 sans_serif: LangMap<FontName>,
2230 monospace: LangMap<FontName>,
2231 cursive: LangMap<FontName>,
2232 fantasy: LangMap<FontName>,
2233 fallback: LangMap<FontName>,
2234}
2235impl GenericFontsService {
2236 fn new() -> Self {
2237 fn default(name: impl Into<FontName>) -> LangMap<FontName> {
2238 let mut f = LangMap::with_capacity(1);
2239 f.insert(lang!(und), name.into());
2240 f
2241 }
2242
2243 let serif = "serif";
2244 let sans_serif = "sans-serif";
2245 let monospace = "monospace";
2246 let cursive = "cursive";
2247 let fantasy = "fantasy";
2248 let fallback = if cfg!(windows) {
2249 "Segoe UI Symbol"
2250 } else if cfg!(target_os = "linux") {
2251 "Standard Symbols PS"
2252 } else {
2253 "sans-serif"
2254 };
2255
2256 GenericFontsService {
2257 serif: default(serif),
2258 sans_serif: default(sans_serif),
2259 monospace: default(monospace),
2260 cursive: default(cursive),
2261 fantasy: default(fantasy),
2262
2263 fallback: default(fallback),
2264 }
2265 }
2266}
2267
2268#[non_exhaustive]
2283pub struct GenericFonts {}
2284macro_rules! impl_fallback_accessors {
2285 ($($name:ident=$name_str:tt),+ $(,)?) => {$($crate::paste! {
2286 #[doc = "Gets the fallback *"$name_str "* font for the given language."]
2287 #[doc = "Note that the returned name can still be the generic `\""$name_str "\"`, this delegates the resolution to the operating system."]
2291
2292 pub fn $name(&self, lang: &Lang) -> FontName {
2293 GENERIC_FONTS_SV.read().$name.get(lang).unwrap().clone()
2294 }
2295
2296 #[doc = "Sets the fallback *"$name_str "* font for the given language."]
2297 pub fn [<set_ $name>]<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2302 self.[<set_ $name _impl>](lang, font_name.into());
2303 }
2304 fn [<set_ $name _impl>](&self, lang: Lang, font_name: FontName) {
2305 UPDATES.once_update("GenericFonts.set", move || {
2306 GENERIC_FONTS_SV.write().$name.insert(lang.clone(), font_name);
2307 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::GenericFont(FontName::$name(), lang)));
2308 });
2309 }
2310 })+};
2311}
2312impl GenericFonts {
2313 #[rustfmt::skip] impl_fallback_accessors! {
2315 serif="serif", sans_serif="sans-serif", monospace="monospace", cursive="cursive", fantasy="fantasy"
2316 }
2317
2318 pub fn fallback(&self, lang: &Lang) -> FontName {
2322 GENERIC_FONTS_SV.read().fallback.get(lang).unwrap().clone()
2323 }
2324
2325 pub fn set_fallback<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2331 self.set_fallback_impl(lang, font_name.into());
2332 }
2333 fn set_fallback_impl(&self, lang: Lang, font_name: FontName) {
2334 UPDATES.once_update("GenericFonts.set", move || {
2335 GENERIC_FONTS_SV.write().fallback.insert(lang.clone(), font_name);
2336 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Fallback(lang)));
2337 });
2338 }
2339
2340 pub fn resolve(&self, name: &FontName, lang: &Lang) -> Option<FontName> {
2344 if name == &FontName::serif() {
2345 Some(self.serif(lang))
2346 } else if name == &FontName::sans_serif() {
2347 Some(self.sans_serif(lang))
2348 } else if name == &FontName::monospace() {
2349 Some(self.monospace(lang))
2350 } else if name == &FontName::cursive() {
2351 Some(self.cursive(lang))
2352 } else if name == &FontName::fantasy() {
2353 Some(self.fantasy(lang))
2354 } else {
2355 None
2356 }
2357 }
2358}
2359
2360#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2361pub(crate) enum WeakFontBytes {
2362 Ipc(WeakIpcBytes),
2363 Arc(std::sync::Weak<Vec<u8>>),
2364 Static(&'static [u8]),
2365 Mmap(std::sync::Weak<SystemFontBytes>),
2366}
2367#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2368impl WeakFontBytes {
2369 pub(crate) fn upgrade(&self) -> Option<FontBytes> {
2370 match self {
2371 WeakFontBytes::Ipc(weak) => Some(FontBytes(FontBytesImpl::Ipc(weak.upgrade()?))),
2372 WeakFontBytes::Arc(weak) => Some(FontBytes(FontBytesImpl::Arc(weak.upgrade()?))),
2373 WeakFontBytes::Static(b) => Some(FontBytes(FontBytesImpl::Static(b))),
2374 WeakFontBytes::Mmap(weak) => Some(FontBytes(FontBytesImpl::System(weak.upgrade()?))),
2375 }
2376 }
2377
2378 pub(crate) fn strong_count(&self) -> usize {
2379 match self {
2380 WeakFontBytes::Ipc(weak) => weak.strong_count(),
2381 WeakFontBytes::Arc(weak) => weak.strong_count(),
2382 WeakFontBytes::Static(_) => 1,
2383 WeakFontBytes::Mmap(weak) => weak.strong_count(),
2384 }
2385 }
2386}
2387
2388struct SystemFontBytes {
2389 path: std::path::PathBuf,
2390 mmap: IpcBytes,
2391}
2392
2393#[derive(Clone)]
2394enum FontBytesImpl {
2395 Ipc(IpcBytes),
2397 Arc(Arc<Vec<u8>>),
2398 Static(&'static [u8]),
2399 System(Arc<SystemFontBytes>),
2400}
2401#[derive(Clone)]
2403pub struct FontBytes(FontBytesImpl);
2404impl FontBytes {
2405 pub fn from_ipc(bytes: IpcBytes) -> Self {
2407 Self(FontBytesImpl::Ipc(bytes))
2408 }
2409
2410 pub fn from_vec(bytes: Vec<u8>) -> io::Result<Self> {
2412 Ok(Self(FontBytesImpl::Ipc(IpcBytes::from_vec_blocking(bytes)?)))
2413 }
2414
2415 pub fn from_static(bytes: &'static [u8]) -> Self {
2417 Self(FontBytesImpl::Static(bytes))
2418 }
2419
2420 pub fn from_arc(bytes: Arc<Vec<u8>>) -> Self {
2424 Self(FontBytesImpl::Arc(bytes))
2425 }
2426
2427 pub fn from_file(path: PathBuf) -> io::Result<Self> {
2429 let path = dunce::canonicalize(path)?;
2430
2431 #[cfg(windows)]
2432 {
2433 use windows::Win32::{Foundation::MAX_PATH, System::SystemInformation::GetSystemWindowsDirectoryW};
2434 let mut buffer = [0u16; MAX_PATH as usize];
2435 let len = unsafe { GetSystemWindowsDirectoryW(Some(&mut buffer)) };
2437 let fonts_dir = String::from_utf16_lossy(&buffer[..len as usize]);
2438 if path.starts_with(fonts_dir) {
2440 return unsafe { load_from_system(path) };
2442 }
2443 }
2444 #[cfg(target_os = "macos")]
2445 if path.starts_with("/System/Library/Fonts/") || path.starts_with("/Library/Fonts/") {
2446 return unsafe { load_from_system(path) };
2448 }
2449 #[cfg(target_os = "android")]
2450 if path.starts_with("/system/fonts/") || path.starts_with("/system/font/") || path.starts_with("/system/product/fonts/") {
2451 return unsafe { load_from_system(path) };
2453 }
2454 #[cfg(unix)]
2455 if path.starts_with("/usr/share/fonts/") {
2456 return unsafe { load_from_system(path) };
2458 }
2459
2460 #[cfg(ipc)]
2461 unsafe fn load_from_system(path: PathBuf) -> io::Result<FontBytes> {
2462 let mmap = unsafe { IpcBytes::open_memmap_blocking(path.clone(), None) }?;
2464 Ok(FontBytes(FontBytesImpl::System(Arc::new(SystemFontBytes { path, mmap }))))
2465 }
2466
2467 #[cfg(all(not(ipc), not(target_arch = "wasm32")))]
2468 unsafe fn load_from_system(path: PathBuf) -> io::Result<FontBytes> {
2469 let mmap = IpcBytes::from_path_blocking(&path)?;
2470 Ok(FontBytes(FontBytesImpl::System(Arc::new(SystemFontBytes { path, mmap }))))
2471 }
2472
2473 Ok(Self(FontBytesImpl::Ipc(IpcBytes::from_path_blocking(&path)?)))
2474 }
2475
2476 #[cfg(ipc)]
2483 pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2484 let ipc = unsafe { IpcBytes::open_memmap_blocking(path, None) }?;
2486 Ok(Self(FontBytesImpl::Ipc(ipc)))
2487 }
2488
2489 #[cfg(ipc)]
2493 pub fn mmap_path(&self) -> Option<&std::path::Path> {
2494 if let FontBytesImpl::System(m) = &self.0 {
2495 Some(&m.path)
2496 } else {
2497 None
2498 }
2499 }
2500
2501 pub fn to_ipc(&self) -> io::Result<IpcFontBytes> {
2503 Ok(if let FontBytesImpl::System(m) = &self.0 {
2504 IpcFontBytes::System(m.path.clone())
2505 } else {
2506 IpcFontBytes::Bytes(self.to_ipc_bytes()?)
2507 })
2508 }
2509
2510 pub fn to_ipc_bytes(&self) -> io::Result<IpcBytes> {
2512 match &self.0 {
2513 FontBytesImpl::Ipc(b) => Ok(b.clone()),
2514 FontBytesImpl::Arc(b) => IpcBytes::from_slice_blocking(b),
2515 FontBytesImpl::Static(b) => IpcBytes::from_slice_blocking(b),
2516 FontBytesImpl::System(m) => IpcBytes::from_slice_blocking(&m.mmap[..]),
2517 }
2518 }
2519
2520 #[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2521 pub(crate) fn downgrade(&self) -> WeakFontBytes {
2522 match &self.0 {
2523 FontBytesImpl::Ipc(ipc) => WeakFontBytes::Ipc(ipc.downgrade()),
2524 FontBytesImpl::Arc(arc) => WeakFontBytes::Arc(Arc::downgrade(arc)),
2525 FontBytesImpl::Static(b) => WeakFontBytes::Static(b),
2526 FontBytesImpl::System(arc) => WeakFontBytes::Mmap(Arc::downgrade(arc)),
2527 }
2528 }
2529}
2530impl std::ops::Deref for FontBytes {
2531 type Target = [u8];
2532
2533 fn deref(&self) -> &Self::Target {
2534 match &self.0 {
2535 FontBytesImpl::Ipc(b) => &b[..],
2536 FontBytesImpl::Arc(b) => &b[..],
2537 FontBytesImpl::Static(b) => b,
2538 FontBytesImpl::System(m) => &m.mmap[..],
2539 }
2540 }
2541}
2542impl fmt::Debug for FontBytes {
2543 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2544 let mut b = f.debug_struct("FontBytes");
2545 b.field(
2546 ".kind",
2547 &match &self.0 {
2548 FontBytesImpl::Ipc(_) => "IpcBytes",
2549 FontBytesImpl::Arc(_) => "Arc",
2550 FontBytesImpl::Static(_) => "Static",
2551 FontBytesImpl::System(_) => "Mmap",
2552 },
2553 );
2554 b.field(".len", &self.len().bytes());
2555 if let FontBytesImpl::System(m) = &self.0 {
2556 b.field(".path", &m.path);
2557 }
2558
2559 b.finish()
2560 }
2561}
2562
2563#[derive(Debug, Clone)]
2564enum FontSource {
2565 File(PathBuf, u32),
2566 Memory(FontBytes, u32),
2567 Alias(FontName),
2568}
2569
2570#[derive(Debug, Clone)]
2572pub struct CustomFont {
2573 name: FontName,
2574 source: FontSource,
2575 stretch: FontStretch,
2576 style: FontStyle,
2577 weight: FontWeight,
2578}
2579impl CustomFont {
2580 pub fn from_file<N: Into<FontName>, P: Into<PathBuf>>(name: N, path: P, font_index: u32) -> Self {
2588 CustomFont {
2589 name: name.into(),
2590 source: FontSource::File(path.into(), font_index),
2591 stretch: FontStretch::NORMAL,
2592 style: FontStyle::Normal,
2593 weight: FontWeight::NORMAL,
2594 }
2595 }
2596
2597 pub fn from_bytes<N: Into<FontName>>(name: N, data: FontBytes, font_index: u32) -> Self {
2605 CustomFont {
2606 name: name.into(),
2607 source: FontSource::Memory(data, font_index),
2608 stretch: FontStretch::NORMAL,
2609 style: FontStyle::Normal,
2610 weight: FontWeight::NORMAL,
2611 }
2612 }
2613
2614 pub fn from_other<N: Into<FontName>, O: Into<FontName>>(name: N, other_font: O) -> Self {
2620 CustomFont {
2621 name: name.into(),
2622 source: FontSource::Alias(other_font.into()),
2623 stretch: FontStretch::NORMAL,
2624 style: FontStyle::Normal,
2625 weight: FontWeight::NORMAL,
2626 }
2627 }
2628
2629 pub fn stretch(mut self, stretch: FontStretch) -> Self {
2633 self.stretch = stretch;
2634 self
2635 }
2636
2637 pub fn style(mut self, style: FontStyle) -> Self {
2641 self.style = style;
2642 self
2643 }
2644
2645 pub fn weight(mut self, weight: FontWeight) -> Self {
2649 self.weight = weight;
2650 self
2651 }
2652}
2653
2654#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Transitionable)]
2658#[serde(transparent)]
2659pub struct FontStretch(pub f32);
2660impl fmt::Debug for FontStretch {
2661 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2662 let name = self.name();
2663 if name.is_empty() {
2664 f.debug_tuple("FontStretch").field(&self.0).finish()
2665 } else {
2666 if f.alternate() {
2667 write!(f, "FontStretch::")?;
2668 }
2669 write!(f, "{name}")
2670 }
2671 }
2672}
2673impl PartialOrd for FontStretch {
2674 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2675 Some(self.cmp(other))
2676 }
2677}
2678impl Ord for FontStretch {
2679 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2680 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
2681 }
2682}
2683impl PartialEq for FontStretch {
2684 fn eq(&self, other: &Self) -> bool {
2685 about_eq(self.0, other.0, EQ_GRANULARITY)
2686 }
2687}
2688impl Eq for FontStretch {}
2689impl std::hash::Hash for FontStretch {
2690 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2691 about_eq_hash(self.0, EQ_GRANULARITY, state)
2692 }
2693}
2694impl Default for FontStretch {
2695 fn default() -> FontStretch {
2696 FontStretch::NORMAL
2697 }
2698}
2699impl FontStretch {
2700 pub const ULTRA_CONDENSED: FontStretch = FontStretch(0.5);
2702 pub const EXTRA_CONDENSED: FontStretch = FontStretch(0.625);
2704 pub const CONDENSED: FontStretch = FontStretch(0.75);
2706 pub const SEMI_CONDENSED: FontStretch = FontStretch(0.875);
2708 pub const NORMAL: FontStretch = FontStretch(1.0);
2710 pub const SEMI_EXPANDED: FontStretch = FontStretch(1.125);
2712 pub const EXPANDED: FontStretch = FontStretch(1.25);
2714 pub const EXTRA_EXPANDED: FontStretch = FontStretch(1.5);
2716 pub const ULTRA_EXPANDED: FontStretch = FontStretch(2.0);
2718
2719 pub fn name(self) -> &'static str {
2721 macro_rules! name {
2722 ($($CONST:ident;)+) => {$(
2723 if self == Self::$CONST {
2724 return stringify!($CONST);
2725 }
2726 )+}
2727 }
2728 name! {
2729 ULTRA_CONDENSED;
2730 EXTRA_CONDENSED;
2731 CONDENSED;
2732 SEMI_CONDENSED;
2733 NORMAL;
2734 SEMI_EXPANDED;
2735 EXPANDED;
2736 EXTRA_EXPANDED;
2737 ULTRA_EXPANDED;
2738 }
2739 ""
2740 }
2741}
2742impl_from_and_into_var! {
2743 fn from(fct: Factor) -> FontStretch {
2744 FontStretch(fct.0)
2745 }
2746 fn from(pct: FactorPercent) -> FontStretch {
2747 FontStretch(pct.fct().0)
2748 }
2749 fn from(fct: f32) -> FontStretch {
2750 FontStretch(fct)
2751 }
2752}
2753impl From<ttf_parser::Width> for FontStretch {
2754 fn from(value: ttf_parser::Width) -> Self {
2755 use ttf_parser::Width::*;
2756 match value {
2757 UltraCondensed => FontStretch::ULTRA_CONDENSED,
2758 ExtraCondensed => FontStretch::EXTRA_CONDENSED,
2759 Condensed => FontStretch::CONDENSED,
2760 SemiCondensed => FontStretch::SEMI_CONDENSED,
2761 Normal => FontStretch::NORMAL,
2762 SemiExpanded => FontStretch::SEMI_EXPANDED,
2763 Expanded => FontStretch::EXPANDED,
2764 ExtraExpanded => FontStretch::EXTRA_EXPANDED,
2765 UltraExpanded => FontStretch::ULTRA_EXPANDED,
2766 }
2767 }
2768}
2769impl From<FontStretch> for ttf_parser::Width {
2770 fn from(value: FontStretch) -> Self {
2771 if value <= FontStretch::ULTRA_CONDENSED {
2772 ttf_parser::Width::UltraCondensed
2773 } else if value <= FontStretch::EXTRA_CONDENSED {
2774 ttf_parser::Width::ExtraCondensed
2775 } else if value <= FontStretch::CONDENSED {
2776 ttf_parser::Width::Condensed
2777 } else if value <= FontStretch::SEMI_CONDENSED {
2778 ttf_parser::Width::SemiCondensed
2779 } else if value <= FontStretch::NORMAL {
2780 ttf_parser::Width::Normal
2781 } else if value <= FontStretch::SEMI_EXPANDED {
2782 ttf_parser::Width::SemiExpanded
2783 } else if value <= FontStretch::EXPANDED {
2784 ttf_parser::Width::Expanded
2785 } else if value <= FontStretch::EXTRA_EXPANDED {
2786 ttf_parser::Width::ExtraExpanded
2787 } else {
2788 ttf_parser::Width::UltraExpanded
2789 }
2790 }
2791}
2792
2793#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
2795pub enum FontStyle {
2796 #[default]
2798 Normal,
2799 Italic,
2801 Oblique,
2803}
2804impl fmt::Debug for FontStyle {
2805 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2806 if f.alternate() {
2807 write!(f, "FontStyle::")?;
2808 }
2809 match self {
2810 Self::Normal => write!(f, "Normal"),
2811 Self::Italic => write!(f, "Italic"),
2812 Self::Oblique => write!(f, "Oblique"),
2813 }
2814 }
2815}
2816impl From<ttf_parser::Style> for FontStyle {
2817 fn from(value: ttf_parser::Style) -> Self {
2818 use ttf_parser::Style::*;
2819 match value {
2820 Normal => FontStyle::Normal,
2821 Italic => FontStyle::Italic,
2822 Oblique => FontStyle::Oblique,
2823 }
2824 }
2825}
2826
2827impl From<FontStyle> for ttf_parser::Style {
2828 fn from(value: FontStyle) -> Self {
2829 match value {
2830 FontStyle::Normal => Self::Normal,
2831 FontStyle::Italic => Self::Italic,
2832 FontStyle::Oblique => Self::Oblique,
2833 }
2834 }
2835}
2836
2837#[derive(Clone, Copy, Transitionable, serde::Serialize, serde::Deserialize)]
2840pub struct FontWeight(pub f32);
2841impl Default for FontWeight {
2842 fn default() -> FontWeight {
2843 FontWeight::NORMAL
2844 }
2845}
2846impl fmt::Debug for FontWeight {
2847 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2848 let name = self.name();
2849 if name.is_empty() {
2850 f.debug_tuple("FontWeight").field(&self.0).finish()
2851 } else {
2852 if f.alternate() {
2853 write!(f, "FontWeight::")?;
2854 }
2855 write!(f, "{name}")
2856 }
2857 }
2858}
2859impl PartialOrd for FontWeight {
2860 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2861 Some(self.cmp(other))
2862 }
2863}
2864impl Ord for FontWeight {
2865 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2866 about_eq_ord(self.0, other.0, EQ_GRANULARITY_100)
2867 }
2868}
2869impl PartialEq for FontWeight {
2870 fn eq(&self, other: &Self) -> bool {
2871 about_eq(self.0, other.0, EQ_GRANULARITY_100)
2872 }
2873}
2874impl Eq for FontWeight {}
2875impl std::hash::Hash for FontWeight {
2876 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2877 about_eq_hash(self.0, EQ_GRANULARITY_100, state)
2878 }
2879}
2880impl FontWeight {
2881 pub const THIN: FontWeight = FontWeight(100.0);
2883 pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
2885 pub const LIGHT: FontWeight = FontWeight(300.0);
2887 pub const NORMAL: FontWeight = FontWeight(400.0);
2889 pub const MEDIUM: FontWeight = FontWeight(500.0);
2891 pub const SEMIBOLD: FontWeight = FontWeight(600.0);
2893 pub const BOLD: FontWeight = FontWeight(700.0);
2895 pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
2897 pub const BLACK: FontWeight = FontWeight(900.0);
2899
2900 pub fn name(self) -> &'static str {
2902 macro_rules! name {
2903 ($($CONST:ident;)+) => {$(
2904 if self == Self::$CONST {
2905 return stringify!($CONST);
2906 }
2907 )+}
2908 }
2909 name! {
2910 THIN;
2911 EXTRA_LIGHT;
2912 LIGHT;
2913 NORMAL;
2914 MEDIUM;
2915 SEMIBOLD;
2916 BOLD;
2917 EXTRA_BOLD;
2918 BLACK;
2919 }
2920 ""
2921 }
2922}
2923impl_from_and_into_var! {
2924 fn from(weight: u32) -> FontWeight {
2925 FontWeight(weight as f32)
2926 }
2927 fn from(weight: f32) -> FontWeight {
2928 FontWeight(weight)
2929 }
2930}
2931impl From<ttf_parser::Weight> for FontWeight {
2932 fn from(value: ttf_parser::Weight) -> Self {
2933 use ttf_parser::Weight::*;
2934 match value {
2935 Thin => FontWeight::THIN,
2936 ExtraLight => FontWeight::EXTRA_LIGHT,
2937 Light => FontWeight::LIGHT,
2938 Normal => FontWeight::NORMAL,
2939 Medium => FontWeight::MEDIUM,
2940 SemiBold => FontWeight::SEMIBOLD,
2941 Bold => FontWeight::BOLD,
2942 ExtraBold => FontWeight::EXTRA_BOLD,
2943 Black => FontWeight::BLACK,
2944 Other(o) => FontWeight(o as f32),
2945 }
2946 }
2947}
2948impl From<FontWeight> for ttf_parser::Weight {
2949 fn from(value: FontWeight) -> Self {
2950 ttf_parser::Weight::from(value.0 as u16)
2951 }
2952}
2953
2954#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2956pub enum LineBreak {
2957 Auto,
2959 Loose,
2961 Normal,
2963 Strict,
2965 Anywhere,
2967}
2968impl Default for LineBreak {
2969 fn default() -> Self {
2971 LineBreak::Auto
2972 }
2973}
2974impl fmt::Debug for LineBreak {
2975 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2976 if f.alternate() {
2977 write!(f, "LineBreak::")?;
2978 }
2979 match self {
2980 LineBreak::Auto => write!(f, "Auto"),
2981 LineBreak::Loose => write!(f, "Loose"),
2982 LineBreak::Normal => write!(f, "Normal"),
2983 LineBreak::Strict => write!(f, "Strict"),
2984 LineBreak::Anywhere => write!(f, "Anywhere"),
2985 }
2986 }
2987}
2988
2989#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2994#[non_exhaustive]
2995pub enum ParagraphBreak {
2996 #[default]
2998 None,
2999 Line,
3001}
3002impl fmt::Debug for ParagraphBreak {
3003 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3004 if f.alternate() {
3005 write!(f, "ParagraphBreak::")?;
3006 }
3007 match self {
3008 ParagraphBreak::None => write!(f, "None"),
3009 ParagraphBreak::Line => write!(f, "Line"),
3010 }
3011 }
3012}
3013
3014#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3016pub enum Hyphens {
3017 None,
3019 Manual,
3024 Auto,
3026}
3027impl Default for Hyphens {
3028 fn default() -> Self {
3030 Hyphens::Auto
3031 }
3032}
3033impl fmt::Debug for Hyphens {
3034 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3035 if f.alternate() {
3036 write!(f, "Hyphens::")?;
3037 }
3038 match self {
3039 Hyphens::None => write!(f, "None"),
3040 Hyphens::Manual => write!(f, "Manual"),
3041 Hyphens::Auto => write!(f, "Auto"),
3042 }
3043 }
3044}
3045
3046#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3052pub enum WordBreak {
3053 Normal,
3055 BreakAll,
3057 KeepAll,
3059}
3060impl Default for WordBreak {
3061 fn default() -> Self {
3063 WordBreak::Normal
3064 }
3065}
3066impl fmt::Debug for WordBreak {
3067 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3068 if f.alternate() {
3069 write!(f, "WordBreak::")?;
3070 }
3071 match self {
3072 WordBreak::Normal => write!(f, "Normal"),
3073 WordBreak::BreakAll => write!(f, "BreakAll"),
3074 WordBreak::KeepAll => write!(f, "KeepAll"),
3075 }
3076 }
3077}
3078
3079#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3081pub enum Justify {
3082 Auto,
3086 InterWord,
3088 InterLetter,
3090}
3091impl Default for Justify {
3092 fn default() -> Self {
3094 Justify::Auto
3095 }
3096}
3097impl Justify {
3098 pub fn resolve(self, lang: &Lang) -> Self {
3100 match self {
3101 Self::Auto => match lang.language.as_str() {
3102 "zh" | "ja" | "ko" => Self::InterLetter,
3103 _ => Self::InterWord,
3104 },
3105 m => m,
3106 }
3107 }
3108}
3109impl fmt::Debug for Justify {
3110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3111 if f.alternate() {
3112 write!(f, "Justify::")?;
3113 }
3114 match self {
3115 Justify::Auto => write!(f, "Auto"),
3116 Justify::InterWord => write!(f, "InterWord"),
3117 Justify::InterLetter => write!(f, "InterLetter"),
3118 }
3119 }
3120}
3121
3122#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3130#[non_exhaustive]
3131pub struct FontFaceMetrics {
3132 pub units_per_em: u32,
3136
3137 pub ascent: f32,
3139
3140 pub descent: f32,
3146
3147 pub line_gap: f32,
3149
3150 pub underline_position: f32,
3153
3154 pub underline_thickness: f32,
3156
3157 pub cap_height: f32,
3159
3160 pub x_height: f32,
3163
3164 pub bounds: euclid::Rect<f32, ()>,
3168}
3169impl FontFaceMetrics {
3170 pub fn sized(&self, font_size_px: Px) -> FontMetrics {
3172 let size_scale = 1.0 / self.units_per_em as f32 * font_size_px.0 as f32;
3173 let s = move |f: f32| Px((f * size_scale).round() as i32);
3174 FontMetrics {
3175 size_scale,
3176 ascent: s(self.ascent),
3177 descent: s(self.descent),
3178 line_gap: s(self.line_gap),
3179 underline_position: s(self.underline_position),
3180 underline_thickness: s(self.underline_thickness),
3181 cap_height: s(self.cap_height),
3182 x_height: (s(self.x_height)),
3183 bounds: {
3184 let b = self.bounds;
3185 PxRect::new(
3186 PxPoint::new(s(b.origin.x), s(b.origin.y)),
3187 PxSize::new(s(b.size.width), s(b.size.height)),
3188 )
3189 },
3190 }
3191 }
3192}
3193
3194#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3198#[non_exhaustive]
3199pub struct FontMetrics {
3200 pub size_scale: f32,
3202
3203 pub ascent: Px,
3205
3206 pub descent: Px,
3212
3213 pub line_gap: Px,
3215
3216 pub underline_position: Px,
3219
3220 pub underline_thickness: Px,
3222
3223 pub cap_height: Px,
3225
3226 pub x_height: Px,
3228
3229 pub bounds: PxRect,
3233}
3234impl FontMetrics {
3235 pub fn line_height(&self) -> Px {
3237 self.ascent - self.descent + self.line_gap
3238 }
3239}
3240
3241#[derive(Clone)]
3243pub enum TextTransformFn {
3244 None,
3246 Uppercase,
3248 Lowercase,
3250 Custom(Arc<dyn Fn(&Txt) -> Cow<Txt> + Send + Sync>),
3252}
3253impl TextTransformFn {
3254 pub fn transform<'t>(&self, text: &'t Txt) -> Cow<'t, Txt> {
3258 match self {
3259 TextTransformFn::None => Cow::Borrowed(text),
3260 TextTransformFn::Uppercase => {
3261 if text.chars().any(|c| !c.is_uppercase()) {
3262 Cow::Owned(text.to_uppercase().into())
3263 } else {
3264 Cow::Borrowed(text)
3265 }
3266 }
3267 TextTransformFn::Lowercase => {
3268 if text.chars().any(|c| !c.is_lowercase()) {
3269 Cow::Owned(text.to_lowercase().into())
3270 } else {
3271 Cow::Borrowed(text)
3272 }
3273 }
3274 TextTransformFn::Custom(fn_) => fn_(text),
3275 }
3276 }
3277
3278 pub fn custom(fn_: impl Fn(&Txt) -> Cow<Txt> + Send + Sync + 'static) -> Self {
3280 TextTransformFn::Custom(Arc::new(fn_))
3281 }
3282}
3283impl fmt::Debug for TextTransformFn {
3284 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3285 if f.alternate() {
3286 write!(f, "TextTransformFn::")?;
3287 }
3288 match self {
3289 TextTransformFn::None => write!(f, "None"),
3290 TextTransformFn::Uppercase => write!(f, "Uppercase"),
3291 TextTransformFn::Lowercase => write!(f, "Lowercase"),
3292 TextTransformFn::Custom(_) => write!(f, "Custom"),
3293 }
3294 }
3295}
3296impl PartialEq for TextTransformFn {
3297 fn eq(&self, other: &Self) -> bool {
3298 match (self, other) {
3299 (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
3300 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3301 }
3302 }
3303}
3304
3305#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3307pub enum WhiteSpace {
3308 #[default]
3310 Preserve,
3311 Merge,
3314 MergeParagraph,
3317 MergeAll,
3319}
3320impl WhiteSpace {
3321 pub fn transform(self, text: &Txt) -> Cow<'_, Txt> {
3325 match self {
3326 WhiteSpace::Preserve => Cow::Borrowed(text),
3327 WhiteSpace::Merge => {
3328 let mut prev_i = 0;
3330 for line in text.split_inclusive('\n') {
3331 let line_exclusive = line.trim_end_matches('\n').trim_end_matches('\r');
3333 let line_trim = line_exclusive.trim();
3334 let mut merge = line_trim.len() != line_exclusive.len() || line_trim.is_empty();
3335
3336 if !merge {
3338 let mut prev_is_space = true; for c in line.chars() {
3340 let is_space = c.is_whitespace();
3341 if prev_is_space && is_space {
3342 merge = true;
3343 break;
3344 }
3345 prev_is_space = is_space;
3346 }
3347 }
3348
3349 if !merge {
3350 prev_i += line.len();
3351 continue;
3352 }
3353
3354 let mut out = String::with_capacity(text.len() - 1);
3356 out.push_str(&text[..prev_i]);
3357
3358 let mut chars = text[prev_i..].chars();
3359 let mut prev_is_space = true;
3360 let mut prev_is_break = true;
3361 while let Some(c) = chars.next() {
3362 if c == '\r'
3363 && let Some(nc) = chars.next()
3364 {
3365 if nc == '\n' {
3366 if !prev_is_break && !out.is_empty() {
3367 out.push('\n');
3368 }
3369 prev_is_break = true;
3370 prev_is_space = true;
3371 } else {
3372 out.push(c);
3373 out.push(nc);
3374 prev_is_break = false;
3375 prev_is_space = nc.is_whitespace();
3376 }
3377 } else if c == '\n' {
3378 if !prev_is_break && !out.is_empty() {
3379 out.push('\n');
3380 }
3381 prev_is_break = true;
3382 prev_is_space = true;
3383 } else if c.is_whitespace() {
3384 if prev_is_space {
3385 continue;
3386 }
3387 out.push(' ');
3388 prev_is_space = true;
3389 } else {
3390 out.push(c);
3391 prev_is_space = false;
3392 prev_is_break = false;
3393 }
3394 }
3395
3396 if let Some((i, c)) = out.char_indices().rev().find(|(_, c)| !c.is_whitespace()) {
3398 out.truncate(i + c.len_utf8());
3399 }
3400
3401 return Cow::Owned(out.into());
3402 }
3403 Cow::Borrowed(text)
3404 }
3405 WhiteSpace::MergeParagraph => {
3406 let mut merge = text.contains('\n') || text.chars().last().unwrap_or('\0').is_whitespace();
3409 if !merge {
3410 let mut prev_is_space = true;
3411 for c in text.chars() {
3412 let is_space = c.is_whitespace();
3413 if prev_is_space && is_space {
3414 merge = true;
3415 break;
3416 }
3417 prev_is_space = is_space;
3418 }
3419 }
3420
3421 if merge {
3422 let mut out = String::with_capacity(text.len());
3423 let mut prev_is_break = false;
3424 for line in text.lines() {
3425 let line = line.trim();
3426 let is_break = line.is_empty();
3427 if !prev_is_break && is_break && !out.is_empty() {
3428 out.push('\n');
3429 }
3430 if !prev_is_break && !is_break && !out.is_empty() {
3431 out.push(' ');
3432 }
3433 prev_is_break = is_break;
3434
3435 let mut prev_is_space = false;
3436 for c in line.chars() {
3437 let is_space = c.is_whitespace();
3438 if is_space {
3439 if !prev_is_space {
3440 out.push(' ');
3441 }
3442 } else {
3443 out.push(c);
3444 }
3445 prev_is_space = is_space;
3446 }
3447 }
3448
3449 if let Some((i, c)) = out.char_indices().rev().find(|(_, c)| !c.is_whitespace()) {
3451 out.truncate(i + c.len_utf8());
3452 }
3453
3454 return Cow::Owned(out.into());
3455 }
3456 Cow::Borrowed(text)
3457 }
3458 WhiteSpace::MergeAll => {
3459 let mut prev_i = 0;
3461 let mut prev_is_space = true; for (i, c) in text.char_indices() {
3463 let is_space = c.is_whitespace();
3464 if prev_is_space && is_space || c == '\n' {
3465 if !prev_is_space {
3466 debug_assert_eq!(c, '\n');
3467 prev_i += c.len_utf8();
3468 prev_is_space = true;
3469 }
3470 let mut out = String::with_capacity(text.len() - 1);
3472 out.push_str(&text[..prev_i]);
3474 if !out.is_empty() {
3475 out.push(' ');
3476 }
3477 for c in text[(i + c.len_utf8())..].chars() {
3479 let is_space = c.is_whitespace();
3480 if prev_is_space && is_space {
3481 continue;
3482 }
3483 out.push(if is_space { ' ' } else { c });
3484 prev_is_space = is_space;
3485 }
3486
3487 if let Some((i, c)) = out.char_indices().rev().find(|(_, c)| !c.is_whitespace()) {
3489 out.truncate(i + c.len_utf8());
3490 }
3491
3492 return Cow::Owned(out.into());
3493 }
3494 prev_i = i;
3495 prev_is_space = is_space;
3496 }
3497
3498 let out = text.trim_end();
3502 if out.len() != text.len() {
3503 return Cow::Owned(Txt::from_str(out));
3504 }
3505
3506 Cow::Borrowed(text)
3507 }
3508 }
3509 }
3510}
3511impl fmt::Debug for WhiteSpace {
3512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3513 if f.alternate() {
3514 write!(f, "WhiteSpace::")?;
3515 }
3516 match self {
3517 WhiteSpace::Preserve => write!(f, "Preserve"),
3518 WhiteSpace::Merge => write!(f, "Merge"),
3519 WhiteSpace::MergeAll => write!(f, "MergeAll"),
3520 WhiteSpace::MergeParagraph => write!(f, "MergeParagraph"),
3521 }
3522 }
3523}
3524
3525#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
3527pub struct CaretIndex {
3528 pub index: usize,
3532 pub line: usize,
3542}
3543
3544impl PartialEq for CaretIndex {
3545 fn eq(&self, other: &Self) -> bool {
3546 self.index == other.index
3547 }
3548}
3549impl Eq for CaretIndex {}
3550impl CaretIndex {
3551 pub const ZERO: CaretIndex = CaretIndex { index: 0, line: 0 };
3553}
3554impl PartialOrd for CaretIndex {
3555 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3556 Some(self.cmp(other))
3557 }
3558}
3559impl Ord for CaretIndex {
3560 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3561 self.index.cmp(&other.index)
3562 }
3563}
3564
3565#[derive(Debug, Clone)]
3567#[non_exhaustive]
3568pub enum FontLoadingError {
3569 UnknownFormat,
3571 NoSuchFontInCollection,
3576 Parse(ttf_parser::FaceParsingError),
3578 NoFilesystem,
3581 Io(Arc<std::io::Error>),
3583}
3584impl PartialEq for FontLoadingError {
3585 fn eq(&self, other: &Self) -> bool {
3586 match (self, other) {
3587 (Self::Io(l0), Self::Io(r0)) => Arc::ptr_eq(l0, r0),
3588 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3589 }
3590 }
3591}
3592impl From<std::io::Error> for FontLoadingError {
3593 fn from(error: std::io::Error) -> FontLoadingError {
3594 Self::Io(Arc::new(error))
3595 }
3596}
3597impl fmt::Display for FontLoadingError {
3598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3599 match self {
3600 Self::UnknownFormat => write!(f, "unknown format"),
3601 Self::NoSuchFontInCollection => write!(f, "no such font in the collection"),
3602 Self::NoFilesystem => write!(f, "no filesystem present"),
3603 Self::Parse(e) => fmt::Display::fmt(e, f),
3604 Self::Io(e) => fmt::Display::fmt(e, f),
3605 }
3606 }
3607}
3608impl std::error::Error for FontLoadingError {
3609 fn cause(&self) -> Option<&dyn std::error::Error> {
3610 match self {
3611 FontLoadingError::Parse(e) => Some(e),
3612 FontLoadingError::Io(e) => Some(e),
3613 _ => None,
3614 }
3615 }
3616}
3617
3618#[cfg(test)]
3619mod tests {
3620 use zng_app::APP;
3621
3622 use super::*;
3623
3624 #[test]
3625 fn generic_fonts_default() {
3626 let _app = APP.minimal().run_headless(false);
3627
3628 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(und)))
3629 }
3630
3631 #[test]
3632 fn generic_fonts_fallback() {
3633 let _app = APP.minimal().run_headless(false);
3634
3635 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(en_US)));
3636 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(es)));
3637 }
3638
3639 #[test]
3640 fn generic_fonts_get1() {
3641 let mut app = APP.minimal().run_headless(false);
3642 GenericFonts {}.set_sans_serif(lang!(en_US), "Test Value");
3643 app.update(false).assert_wait();
3644
3645 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3646 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3647 }
3648
3649 #[test]
3650 fn generic_fonts_get2() {
3651 let mut app = APP.minimal().run_headless(false);
3652 GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3653 app.update(false).assert_wait();
3654
3655 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3656 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3657 }
3658
3659 #[test]
3660 fn generic_fonts_get_best() {
3661 let mut app = APP.minimal().run_headless(false);
3662 GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3663 GenericFonts {}.set_sans_serif(lang!(en_US), "Best");
3664 app.update(false).assert_wait();
3665
3666 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Best");
3667 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3668 assert_eq!(&GenericFonts {}.sans_serif(&lang!("und")), "sans-serif");
3669 }
3670
3671 #[test]
3672 fn generic_fonts_get_no_lang_match() {
3673 let mut app = APP.minimal().run_headless(false);
3674 GenericFonts {}.set_sans_serif(lang!(es_US), "Test Value");
3675 app.update(false).assert_wait();
3676
3677 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "sans-serif");
3678 assert_eq!(&GenericFonts {}.sans_serif(&lang!("es")), "Test Value");
3679 }
3680
3681 #[test]
3682 fn white_space_merge() {
3683 macro_rules! test {
3684 ($input:tt, $output:tt) => {
3685 let input = Txt::from($input);
3686 let output = WhiteSpace::Merge.transform(&input);
3687 assert_eq!($output, output.as_str());
3688
3689 let input = input.replace('\n', "\r\n");
3690 let output = WhiteSpace::Merge.transform(&Txt::from(input)).replace("\r\n", "\n");
3691 assert_eq!($output, output.as_str());
3692 };
3693 }
3694 test!("a b\n\nc", "a b\nc");
3695 test!("a b\nc", "a b\nc");
3696 test!(" a b\nc\n \n", "a b\nc");
3697 test!(" \n a b\nc", "a b\nc");
3698 test!("a\n \nb", "a\nb");
3699 }
3700
3701 #[test]
3702 fn white_space_merge_paragraph() {
3703 macro_rules! test {
3704 ($input:tt, $output:tt) => {
3705 let input = Txt::from($input);
3706 let output = WhiteSpace::MergeParagraph.transform(&input);
3707 assert_eq!($output, output.as_str());
3708
3709 let input = input.replace('\n', "\r\n");
3710 let output = WhiteSpace::MergeParagraph.transform(&Txt::from(input)).replace("\r\n", "\n");
3711 assert_eq!($output, output.as_str());
3712 };
3713 }
3714 test!("a b\n\nc", "a b\nc");
3715 test!("a b\nc", "a b c");
3716 test!(" a b\nc\n \n", "a b c");
3717 test!(" \n a b\nc", "a b c");
3718 test!("a\n \nb", "a\nb");
3719 }
3720
3721 #[test]
3722 fn white_space_merge_all() {
3723 macro_rules! test {
3724 ($input:tt, $output:tt) => {
3725 let input = Txt::from($input);
3726 let output = WhiteSpace::MergeAll.transform(&input);
3727 assert_eq!($output, output.as_str());
3728 };
3729 }
3730 test!("a b\n\nc", "a b c");
3731 test!("a b\nc", "a b c");
3732 test!(" a b\nc\n \n", "a b c");
3733 test!(" \n a b\nc", "a b c");
3734 test!("a\n \nb", "a b");
3735 }
3736}