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#![cfg_attr(not(ipc), allow(unused))]
14
15use font_features::RFontVariations;
16use hashbrown::{HashMap, HashSet};
17use std::{borrow::Cow, fmt, io, ops, path::PathBuf, slice::SliceIndex, sync::Arc};
18#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
19use zng_task::channel::WeakIpcBytes;
20
21#[macro_use]
22extern crate bitflags;
23
24pub mod font_features;
25
26mod query_util;
27
28mod emoji_util;
29pub use emoji_util::*;
30
31mod ligature_util;
32use ligature_util::*;
33
34mod unicode_bidi_util;
35
36mod segmenting;
37pub use segmenting::*;
38
39mod shaping;
40pub use shaping::*;
41use zng_clone_move::{async_clmv, clmv};
42
43mod hyphenation;
44pub use self::hyphenation::*;
45
46mod unit;
47pub use unit::*;
48
49use parking_lot::{Mutex, RwLock};
50use pastey::paste;
51use zng_app::{
52 AppExtension,
53 event::{event, event_args},
54 render::FontSynthesis,
55 update::{EventUpdate, UPDATES},
56 view_process::{
57 VIEW_PROCESS_INITED_EVENT, ViewRenderer,
58 raw_events::{RAW_FONT_AA_CHANGED_EVENT, RAW_FONT_CHANGED_EVENT},
59 },
60};
61use zng_app_context::app_local;
62use zng_ext_l10n::{Lang, LangMap, lang};
63use zng_layout::unit::{
64 ByteUnits as _, EQ_GRANULARITY, EQ_GRANULARITY_100, Factor, FactorPercent, Px, PxPoint, PxRect, PxSize, TimeUnits as _, about_eq,
65 about_eq_hash, about_eq_ord, euclid,
66};
67use zng_task::{self as task, channel::IpcBytes};
68use zng_txt::Txt;
69use zng_var::{
70 IntoVar, ResponderVar, ResponseVar, Var, animation::Transitionable, const_var, impl_from_and_into_var, response_done_var, response_var,
71 var,
72};
73use zng_view_api::{config::FontAntiAliasing, font::IpcFontBytes};
74
75#[derive(Clone)]
83pub struct FontName {
84 txt: Txt,
85 is_ascii: bool,
86}
87impl fmt::Debug for FontName {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 if f.alternate() {
90 f.debug_struct("FontName")
91 .field("txt", &self.txt)
92 .field("is_ascii", &self.is_ascii)
93 .finish()
94 } else {
95 write!(f, "{:?}", self.txt)
96 }
97 }
98}
99impl PartialEq for FontName {
100 fn eq(&self, other: &Self) -> bool {
101 self.unicase() == other.unicase()
102 }
103}
104impl Eq for FontName {}
105impl PartialEq<str> for FontName {
106 fn eq(&self, other: &str) -> bool {
107 self.unicase() == unicase::UniCase::<&str>::from(other)
108 }
109}
110impl std::hash::Hash for FontName {
111 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
112 std::hash::Hash::hash(&self.unicase(), state)
113 }
114}
115impl Ord for FontName {
116 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
117 if self == other {
118 return std::cmp::Ordering::Equal;
120 }
121 self.txt.cmp(&other.txt)
122 }
123}
124impl PartialOrd for FontName {
125 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
126 Some(self.cmp(other))
127 }
128}
129impl FontName {
130 fn unicase(&self) -> unicase::UniCase<&str> {
131 if self.is_ascii {
132 unicase::UniCase::ascii(self)
133 } else {
134 unicase::UniCase::unicode(self)
135 }
136 }
137
138 pub const fn from_static(name: &'static str) -> Self {
140 FontName {
141 txt: Txt::from_static(name),
142 is_ascii: {
143 let name_bytes = name.as_bytes();
145 let mut i = name_bytes.len();
146 let mut is_ascii = true;
147 while i > 0 {
148 i -= 1;
149 if !name_bytes[i].is_ascii() {
150 is_ascii = false;
151 break;
152 }
153 }
154 is_ascii
155 },
156 }
157 }
158
159 pub fn new(name: impl Into<Txt>) -> Self {
168 let txt = name.into();
169 FontName {
170 is_ascii: txt.is_ascii(),
171 txt,
172 }
173 }
174
175 pub fn serif() -> Self {
179 Self::new("serif")
180 }
181
182 pub fn sans_serif() -> Self {
187 Self::new("sans-serif")
188 }
189
190 pub fn monospace() -> Self {
194 Self::new("monospace")
195 }
196
197 pub fn cursive() -> Self {
202 Self::new("cursive")
203 }
204
205 pub fn fantasy() -> Self {
209 Self::new("fantasy")
210 }
211
212 pub fn name(&self) -> &str {
214 &self.txt
215 }
216
217 pub fn into_text(self) -> Txt {
221 self.txt
222 }
223}
224impl_from_and_into_var! {
225 fn from(s: &'static str) -> FontName {
226 FontName::new(s)
227 }
228 fn from(s: String) -> FontName {
229 FontName::new(s)
230 }
231 fn from(s: Cow<'static, str>) -> FontName {
232 FontName::new(s)
233 }
234 fn from(f: FontName) -> Txt {
235 f.into_text()
236 }
237}
238impl fmt::Display for FontName {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 f.write_str(self.name())
241 }
242}
243impl std::ops::Deref for FontName {
244 type Target = str;
245
246 fn deref(&self) -> &Self::Target {
247 self.txt.deref()
248 }
249}
250impl AsRef<str> for FontName {
251 fn as_ref(&self) -> &str {
252 self.txt.as_ref()
253 }
254}
255impl serde::Serialize for FontName {
256 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
257 where
258 S: serde::Serializer,
259 {
260 self.txt.serialize(serializer)
261 }
262}
263impl<'de> serde::Deserialize<'de> for FontName {
264 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265 where
266 D: serde::Deserializer<'de>,
267 {
268 Txt::deserialize(deserializer).map(FontName::new)
269 }
270}
271
272#[derive(Eq, PartialEq, Hash, Clone, serde::Serialize, serde::Deserialize)]
301#[serde(transparent)]
302pub struct FontNames(pub Vec<FontName>);
303impl FontNames {
304 pub fn empty() -> Self {
306 FontNames(vec![])
307 }
308
309 pub fn windows_ui(lang: &Lang) -> Self {
311 if lang!("zh-Hans").matches(lang, true, false) {
315 ["Segoe UI", "Microsoft YaHei", "Segoe Ui Emoji", "sans-serif"].into()
316 } else if lang!("zh-Hant").matches(lang, true, false) {
317 ["Segoe UI", "Microsoft Jhenghei", "Segoe Ui Emoji", "sans-serif"].into()
318 } else if lang!(ja).matches(lang, true, false) {
319 ["Segoe UI", "Yu Gothic UI", "Meiryo UI", "Segoe Ui Emoji", "sans-serif"].into()
320 } else if lang!(ko).matches(lang, true, false) {
321 ["Segoe UI", "Malgun Gothic", "Dotom", "Segoe Ui Emoji", "sans-serif"].into()
322 } else {
323 ["Segoe UI", "Segoe Ui Emoji", "sans-serif"].into()
324 }
325 }
326
327 pub fn mac_ui(lang: &Lang) -> Self {
329 if lang!("zh-Hans").matches(lang, true, false) {
332 ["PingFang SC", "Hiragino Sans GB", "Apple Color Emoji", "sans-serif"].into()
333 } else if lang!("zh-Hant").matches(lang, true, false) {
334 ["PingFang TC", "Apple Color Emoji", "sans-serif"].into()
335 } else if lang!(ja).matches(lang, true, false) {
336 ["Hiragino Kaku Gothic Pro", "Apple Color Emoji", "sans-serif"].into()
337 } else if lang!(ko).matches(lang, true, false) {
338 [
339 "Nanum Gothic",
340 "Apple SD Gothic Neo",
341 "AppleGothic",
342 "Apple Color Emoji",
343 "sans-serif",
344 ]
345 .into()
346 } else {
347 ["Neue Helvetica", "Lucida Grande", "Apple Color Emoji", "sans-serif"].into()
348 }
349 }
350
351 pub fn linux_ui(lang: &Lang) -> Self {
353 if lang!("zh-Hans").matches(lang, true, false) {
356 [
357 "Ubuntu",
358 "Droid Sans",
359 "Source Han Sans SC",
360 "Source Han Sans CN",
361 "Source Han Sans",
362 "Noto Color Emoji",
363 "sans-serif",
364 ]
365 .into()
366 } else if lang!("zh-Hant").matches(lang, true, false) {
367 [
368 "Ubuntu",
369 "Droid Sans",
370 "Source Han Sans TC",
371 "Source Han Sans TW",
372 "Source Han Sans",
373 "Noto Color Emoji",
374 "sans-serif",
375 ]
376 .into()
377 } else if lang!(ja).matches(lang, true, false) {
378 [
379 "system-ui",
380 "Ubuntu",
381 "Droid Sans",
382 "Source Han Sans J",
383 "Source Han Sans JP",
384 "Source Han Sans",
385 "Noto Color Emoji",
386 "sans-serif",
387 ]
388 .into()
389 } else if lang!(ko).matches(lang, true, false) {
390 [
391 "system-ui",
392 "Ubuntu",
393 "Droid Sans",
394 "Source Han Sans K",
395 "Source Han Sans JR",
396 "Source Han Sans",
397 "UnDotum",
398 "FBaekmuk Gulim",
399 "Noto Color Emoji",
400 "sans-serif",
401 ]
402 .into()
403 } else {
404 ["system-ui", "Ubuntu", "Droid Sans", "Noto Color Emoji", "sans-serif"].into()
405 }
406 }
407
408 pub fn system_ui(lang: &Lang) -> Self {
410 if cfg!(windows) {
411 Self::windows_ui(lang)
412 } else if cfg!(target_os = "linux") {
413 Self::linux_ui(lang)
414 } else if cfg!(target_os = "macos") {
415 Self::mac_ui(lang)
416 } else {
417 [FontName::sans_serif()].into()
418 }
419 }
420
421 pub fn push(&mut self, font_name: impl Into<FontName>) {
423 self.0.push(font_name.into())
424 }
425}
426impl Default for FontNames {
427 fn default() -> Self {
428 Self::system_ui(&Lang::default())
429 }
430}
431impl fmt::Debug for FontNames {
432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433 if f.alternate() {
434 f.debug_tuple("FontNames").field(&self.0).finish()
435 } else if self.0.is_empty() {
436 write!(f, "[]")
437 } else if self.0.len() == 1 {
438 write!(f, "{:?}", self.0[0])
439 } else {
440 write!(f, "[{:?}, ", self.0[0])?;
441 for name in &self.0[1..] {
442 write!(f, "{name:?}, ")?;
443 }
444 write!(f, "]")
445 }
446 }
447}
448impl fmt::Display for FontNames {
449 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450 let mut iter = self.0.iter();
451
452 if let Some(name) = iter.next() {
453 write!(f, "{name}")?;
454 for name in iter {
455 write!(f, ", {name}")?;
456 }
457 }
458
459 Ok(())
460 }
461}
462impl_from_and_into_var! {
463 fn from(font_name: &'static str) -> FontNames {
464 FontNames(vec![FontName::new(font_name)])
465 }
466
467 fn from(font_name: String) -> FontNames {
468 FontNames(vec![FontName::new(font_name)])
469 }
470
471 fn from(font_name: Txt) -> FontNames {
472 FontNames(vec![FontName::new(font_name)])
473 }
474
475 fn from(font_names: Vec<FontName>) -> FontNames {
476 FontNames(font_names)
477 }
478
479 fn from(font_names: Vec<&'static str>) -> FontNames {
480 FontNames(font_names.into_iter().map(FontName::new).collect())
481 }
482
483 fn from(font_names: Vec<String>) -> FontNames {
484 FontNames(font_names.into_iter().map(FontName::new).collect())
485 }
486
487 fn from(font_name: FontName) -> FontNames {
488 FontNames(vec![font_name])
489 }
490}
491impl ops::Deref for FontNames {
492 type Target = Vec<FontName>;
493
494 fn deref(&self) -> &Self::Target {
495 &self.0
496 }
497}
498impl ops::DerefMut for FontNames {
499 fn deref_mut(&mut self) -> &mut Self::Target {
500 &mut self.0
501 }
502}
503impl std::iter::Extend<FontName> for FontNames {
504 fn extend<T: IntoIterator<Item = FontName>>(&mut self, iter: T) {
505 self.0.extend(iter)
506 }
507}
508impl IntoIterator for FontNames {
509 type Item = FontName;
510
511 type IntoIter = std::vec::IntoIter<FontName>;
512
513 fn into_iter(self) -> Self::IntoIter {
514 self.0.into_iter()
515 }
516}
517impl<const N: usize> From<[FontName; N]> for FontNames {
518 fn from(font_names: [FontName; N]) -> Self {
519 FontNames(font_names.into())
520 }
521}
522impl<const N: usize> IntoVar<FontNames> for [FontName; N] {
523 fn into_var(self) -> Var<FontNames> {
524 const_var(self.into())
525 }
526}
527impl<const N: usize> From<[&'static str; N]> for FontNames {
528 fn from(font_names: [&'static str; N]) -> Self {
529 FontNames(font_names.into_iter().map(FontName::new).collect())
530 }
531}
532impl<const N: usize> IntoVar<FontNames> for [&'static str; N] {
533 fn into_var(self) -> Var<FontNames> {
534 const_var(self.into())
535 }
536}
537impl<const N: usize> From<[String; N]> for FontNames {
538 fn from(font_names: [String; N]) -> Self {
539 FontNames(font_names.into_iter().map(FontName::new).collect())
540 }
541}
542impl<const N: usize> IntoVar<FontNames> for [String; N] {
543 fn into_var(self) -> Var<FontNames> {
544 const_var(self.into())
545 }
546}
547impl<const N: usize> From<[Txt; N]> for FontNames {
548 fn from(font_names: [Txt; N]) -> Self {
549 FontNames(font_names.into_iter().map(FontName::new).collect())
550 }
551}
552impl<const N: usize> IntoVar<FontNames> for [Txt; N] {
553 fn into_var(self) -> Var<FontNames> {
554 const_var(self.into())
555 }
556}
557
558event! {
559 pub static FONT_CHANGED_EVENT: FontChangedArgs;
570}
571
572event_args! {
573 pub struct FontChangedArgs {
575 pub change: FontChange,
577
578 ..
579
580 fn delivery_list(&self, list: &mut UpdateDeliveryList) {
582 list.search_all()
583 }
584 }
585}
586
587#[derive(Clone, Debug)]
589pub enum FontChange {
590 SystemFonts,
594
595 CustomFonts,
600
601 Refresh,
605
606 GenericFont(FontName, Lang),
612
613 Fallback(Lang),
615}
616
617#[derive(Default)]
628#[non_exhaustive]
629pub struct FontManager {}
630impl AppExtension for FontManager {
631 fn event_preview(&mut self, update: &mut EventUpdate) {
632 if RAW_FONT_CHANGED_EVENT.has(update) {
633 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::SystemFonts));
634 } else if let Some(args) = RAW_FONT_AA_CHANGED_EVENT.on(update) {
635 FONTS_SV.read().font_aa.set(args.aa);
636 } else if FONT_CHANGED_EVENT.has(update) {
637 FONTS_SV.write().on_fonts_changed();
638 } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
639 && args.is_respawn
640 {
641 let mut fonts = FONTS_SV.write();
642 fonts.loader.on_view_process_respawn();
643 }
644 }
645
646 fn update(&mut self) {
647 let mut fonts = FONTS_SV.write();
648
649 {
650 let mut f = GENERIC_FONTS_SV.write();
651 for request in std::mem::take(&mut f.requests) {
652 request(&mut f);
653 }
654 }
655
656 let mut changed = false;
657 for (request, responder) in std::mem::take(&mut fonts.loader.unregister_requests) {
658 let r = if let Some(removed) = fonts.loader.custom_fonts.remove(&request) {
659 for removed in removed {
663 removed.on_refresh();
664 }
665
666 changed = true;
667
668 true
669 } else {
670 false
671 };
672 responder.respond(r);
673 }
674 if changed {
675 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
676 }
677
678 if fonts.prune_requested {
679 fonts.on_prune();
680 }
681 }
682}
683
684app_local! {
685 static FONTS_SV: FontsService = FontsService {
686 loader: FontFaceLoader::new(),
687 prune_requested: false,
688 font_aa: var(FontAntiAliasing::Default),
689 };
690}
691
692struct FontsService {
693 loader: FontFaceLoader,
694 prune_requested: bool,
695 font_aa: Var<FontAntiAliasing>,
696}
697impl FontsService {
698 fn on_fonts_changed(&mut self) {
699 self.loader.on_refresh();
700 self.prune_requested = false;
701 }
702
703 fn on_prune(&mut self) {
704 self.loader.on_prune();
705 self.prune_requested = false;
706 }
707}
708
709pub struct FONTS;
711impl FONTS {
712 pub fn refresh(&self) {
716 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Refresh));
717 }
718
719 pub fn prune(&self) {
721 let mut ft = FONTS_SV.write();
722 if !ft.prune_requested {
723 ft.prune_requested = true;
724 UPDATES.update(None);
725 }
726 }
727
728 pub fn generics(&self) -> &'static GenericFonts {
730 &GenericFonts {}
731 }
732
733 pub fn register(&self, custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
742 FontFaceLoader::register(custom_font)
743 }
744
745 pub fn unregister(&self, custom_family: FontName) -> ResponseVar<bool> {
749 FONTS_SV.write().loader.unregister(custom_family)
750 }
751
752 pub fn list(
754 &self,
755 families: &[FontName],
756 style: FontStyle,
757 weight: FontWeight,
758 stretch: FontStretch,
759 lang: &Lang,
760 ) -> ResponseVar<FontFaceList> {
761 if let Some(cached) = FONTS_SV.read().loader.try_list(families, style, weight, stretch, lang) {
763 return cached;
764 }
765 FONTS_SV.write().loader.load_list(families, style, weight, stretch, lang)
767 }
768
769 pub fn find(
771 &self,
772 family: &FontName,
773 style: FontStyle,
774 weight: FontWeight,
775 stretch: FontStretch,
776 lang: &Lang,
777 ) -> ResponseVar<Option<FontFace>> {
778 if let Some(cached) = FONTS_SV.read().loader.try_cached(family, style, weight, stretch, lang) {
780 return cached;
781 }
782 FONTS_SV.write().loader.load(family, style, weight, stretch, lang)
784 }
785
786 pub fn normal(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
788 self.find(family, FontStyle::Normal, FontWeight::NORMAL, FontStretch::NORMAL, lang)
789 }
790
791 pub fn italic(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
793 self.find(family, FontStyle::Italic, FontWeight::NORMAL, FontStretch::NORMAL, lang)
794 }
795
796 pub fn bold(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
798 self.find(family, FontStyle::Normal, FontWeight::BOLD, FontStretch::NORMAL, lang)
799 }
800
801 pub fn custom_fonts(&self) -> Vec<FontName> {
803 FONTS_SV.read().loader.custom_fonts.keys().cloned().collect()
804 }
805
806 pub fn system_fonts(&self) -> ResponseVar<Vec<FontName>> {
810 query_util::system_all()
811 }
812
813 pub fn system_font_aa(&self) -> Var<FontAntiAliasing> {
817 FONTS_SV.read().font_aa.read_only()
818 }
819}
820
821impl<'a> From<ttf_parser::Face<'a>> for FontFaceMetrics {
822 fn from(f: ttf_parser::Face<'a>) -> Self {
823 let underline = f
824 .underline_metrics()
825 .unwrap_or(ttf_parser::LineMetrics { position: 0, thickness: 0 });
826 FontFaceMetrics {
827 units_per_em: f.units_per_em() as _,
828 ascent: f.ascender() as f32,
829 descent: f.descender() as f32,
830 line_gap: f.line_gap() as f32,
831 underline_position: underline.position as f32,
832 underline_thickness: underline.thickness as f32,
833 cap_height: f.capital_height().unwrap_or(0) as f32,
834 x_height: f.x_height().unwrap_or(0) as f32,
835 bounds: euclid::rect(
836 f.global_bounding_box().x_min as f32,
837 f.global_bounding_box().x_max as f32,
838 f.global_bounding_box().width() as f32,
839 f.global_bounding_box().height() as f32,
840 ),
841 }
842 }
843}
844
845#[derive(PartialEq, Eq, Hash)]
846struct FontInstanceKey(Px, Box<[(ttf_parser::Tag, i32)]>);
847impl FontInstanceKey {
848 pub fn new(size: Px, variations: &[rustybuzz::Variation]) -> Self {
850 let variations_key: Vec<_> = variations.iter().map(|p| (p.tag, (p.value * 1000.0) as i32)).collect();
851 FontInstanceKey(size, variations_key.into_boxed_slice())
852 }
853}
854
855#[derive(Clone)]
862pub struct FontFace(Arc<LoadedFontFace>);
863struct LoadedFontFace {
864 data: FontBytes,
865 face_index: u32,
866 display_name: FontName,
867 family_name: FontName,
868 postscript_name: Option<Txt>,
869 style: FontStyle,
870 weight: FontWeight,
871 stretch: FontStretch,
872 metrics: FontFaceMetrics,
873 lig_carets: LigatureCaretList,
874 flags: FontFaceFlags,
875 m: Mutex<FontFaceMut>,
876}
877bitflags! {
878 #[derive(Debug, Clone, Copy)]
879 struct FontFaceFlags: u8 {
880 const IS_MONOSPACE = 0b0000_0001;
881 const HAS_LIGATURES = 0b0000_0010;
882 const HAS_RASTER_IMAGES = 0b0000_0100;
883 const HAS_SVG_IMAGES = 0b0000_1000;
884 }
885}
886struct FontFaceMut {
887 instances: HashMap<FontInstanceKey, Font>,
888 render_ids: Vec<RenderFontFace>,
889 unregistered: bool,
890}
891
892impl fmt::Debug for FontFace {
893 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
894 let m = self.0.m.lock();
895 f.debug_struct("FontFace")
896 .field("display_name", &self.0.display_name)
897 .field("family_name", &self.0.family_name)
898 .field("postscript_name", &self.0.postscript_name)
899 .field("flags", &self.0.flags)
900 .field("style", &self.0.style)
901 .field("weight", &self.0.weight)
902 .field("stretch", &self.0.stretch)
903 .field("metrics", &self.0.metrics)
904 .field("instances.len()", &m.instances.len())
905 .field("render_keys.len()", &m.render_ids.len())
906 .field("unregistered", &m.unregistered)
907 .finish_non_exhaustive()
908 }
909}
910impl PartialEq for FontFace {
911 fn eq(&self, other: &Self) -> bool {
912 Arc::ptr_eq(&self.0, &other.0)
913 }
914}
915impl Eq for FontFace {}
916impl FontFace {
917 pub fn empty() -> Self {
919 FontFace(Arc::new(LoadedFontFace {
920 data: FontBytes::from_static(&[]),
921 face_index: 0,
922 display_name: FontName::from("<empty>"),
923 family_name: FontName::from("<empty>"),
924 postscript_name: None,
925 flags: FontFaceFlags::IS_MONOSPACE,
926 style: FontStyle::Normal,
927 weight: FontWeight::NORMAL,
928 stretch: FontStretch::NORMAL,
929 metrics: FontFaceMetrics {
931 units_per_em: 2048,
932 ascent: 1616.0,
933 descent: -432.0,
934 line_gap: 0.0,
935 underline_position: -205.0,
936 underline_thickness: 102.0,
937 cap_height: 1616.0,
938 x_height: 1616.0,
939 bounds: euclid::Box2D::new(euclid::point2(0.0, -432.0), euclid::point2(1291.0, 1616.0)).to_rect(),
941 },
942 lig_carets: LigatureCaretList::empty(),
943 m: Mutex::new(FontFaceMut {
944 instances: HashMap::default(),
945 render_ids: vec![],
946 unregistered: false,
947 }),
948 }))
949 }
950
951 pub fn is_empty(&self) -> bool {
953 self.0.data.is_empty()
954 }
955
956 async fn load_custom(custom_font: CustomFont) -> Result<Self, FontLoadingError> {
957 let bytes;
958 let mut face_index;
959
960 match custom_font.source {
961 FontSource::File(path, index) => {
962 bytes = task::wait(|| FontBytes::from_file(path)).await?;
963 face_index = index;
964 }
965 FontSource::Memory(arc, index) => {
966 bytes = arc;
967 face_index = index;
968 }
969 FontSource::Alias(other_font) => {
970 let result = FONTS_SV
971 .write()
972 .loader
973 .load_resolved(&other_font, custom_font.style, custom_font.weight, custom_font.stretch);
974 return match result.wait_rsp().await {
975 Some(other_font) => Ok(FontFace(Arc::new(LoadedFontFace {
976 data: other_font.0.data.clone(),
977 face_index: other_font.0.face_index,
978 display_name: custom_font.name.clone(),
979 family_name: custom_font.name,
980 postscript_name: None,
981 style: other_font.0.style,
982 weight: other_font.0.weight,
983 stretch: other_font.0.stretch,
984 metrics: other_font.0.metrics.clone(),
985 m: Mutex::new(FontFaceMut {
986 instances: Default::default(),
987 render_ids: Default::default(),
988 unregistered: Default::default(),
989 }),
990 lig_carets: other_font.0.lig_carets.clone(),
991 flags: other_font.0.flags,
992 }))),
993 None => Err(FontLoadingError::NoSuchFontInCollection),
994 };
995 }
996 }
997
998 let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
999 Ok(f) => f,
1000 Err(e) => {
1001 match e {
1002 ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1004 e => return Err(FontLoadingError::Parse(e)),
1005 }
1006
1007 match ttf_parser::Face::parse(&bytes, face_index) {
1008 Ok(f) => f,
1009 Err(_) => return Err(FontLoadingError::Parse(e)),
1010 }
1011 }
1012 };
1013
1014 let has_ligatures = ttf_face.tables().gsub.is_some();
1015 let lig_carets = if has_ligatures {
1016 LigatureCaretList::empty()
1017 } else {
1018 LigatureCaretList::load(ttf_face.raw_face())?
1019 };
1020
1021 let has_raster_images = {
1023 let t = ttf_face.tables();
1024 t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1025 };
1026
1027 let mut flags = FontFaceFlags::empty();
1028 flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1029 flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1030 flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1031 flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1032
1033 Ok(FontFace(Arc::new(LoadedFontFace {
1034 face_index,
1035 display_name: custom_font.name.clone(),
1036 family_name: custom_font.name,
1037 postscript_name: None,
1038 style: custom_font.style,
1039 weight: custom_font.weight,
1040 stretch: custom_font.stretch,
1041 metrics: ttf_face.into(),
1042 lig_carets,
1043 m: Mutex::new(FontFaceMut {
1044 instances: Default::default(),
1045 render_ids: Default::default(),
1046 unregistered: Default::default(),
1047 }),
1048 data: bytes,
1049 flags,
1050 })))
1051 }
1052
1053 fn load(bytes: FontBytes, mut face_index: u32) -> Result<Self, FontLoadingError> {
1054 let _span = tracing::trace_span!("FontFace::load").entered();
1055
1056 let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1057 Ok(f) => f,
1058 Err(e) => {
1059 match e {
1060 ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1062 e => return Err(FontLoadingError::Parse(e)),
1063 }
1064
1065 match ttf_parser::Face::parse(&bytes, face_index) {
1066 Ok(f) => f,
1067 Err(_) => return Err(FontLoadingError::Parse(e)),
1068 }
1069 }
1070 };
1071
1072 let has_ligatures = ttf_face.tables().gsub.is_some();
1073 let lig_carets = if has_ligatures {
1074 LigatureCaretList::empty()
1075 } else {
1076 LigatureCaretList::load(ttf_face.raw_face())?
1077 };
1078
1079 let mut display_name = None;
1080 let mut family_name = None;
1081 let mut postscript_name = None;
1082 let mut any_name = None::<String>;
1083 for name in ttf_face.names() {
1084 if let Some(n) = name.to_string() {
1085 match name.name_id {
1086 ttf_parser::name_id::FULL_NAME => display_name = Some(n),
1087 ttf_parser::name_id::FAMILY => family_name = Some(n),
1088 ttf_parser::name_id::POST_SCRIPT_NAME => postscript_name = Some(n),
1089 _ => match &mut any_name {
1090 Some(s) => {
1091 if n.len() > s.len() {
1092 *s = n;
1093 }
1094 }
1095 None => any_name = Some(n),
1096 },
1097 }
1098 }
1099 }
1100 let display_name = FontName::new(Txt::from_str(
1101 display_name
1102 .as_ref()
1103 .or(family_name.as_ref())
1104 .or(postscript_name.as_ref())
1105 .or(any_name.as_ref())
1106 .unwrap(),
1107 ));
1108 let family_name = family_name.map(FontName::from).unwrap_or_else(|| display_name.clone());
1109 let postscript_name = postscript_name.map(Txt::from);
1110
1111 if ttf_face.units_per_em() == 0 {
1112 tracing::debug!("font {display_name:?} units_per_em 0");
1114 return Err(FontLoadingError::UnknownFormat);
1115 }
1116
1117 let has_raster_images = {
1119 let t = ttf_face.tables();
1120 t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1121 };
1122
1123 let mut flags = FontFaceFlags::empty();
1124 flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1125 flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1126 flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1127 flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1128
1129 Ok(FontFace(Arc::new(LoadedFontFace {
1130 face_index,
1131 family_name,
1132 display_name,
1133 postscript_name,
1134 style: ttf_face.style().into(),
1135 weight: ttf_face.weight().into(),
1136 stretch: ttf_face.width().into(),
1137 metrics: ttf_face.into(),
1138 lig_carets,
1139 m: Mutex::new(FontFaceMut {
1140 instances: Default::default(),
1141 render_ids: Default::default(),
1142 unregistered: Default::default(),
1143 }),
1144 data: bytes,
1145 flags,
1146 })))
1147 }
1148
1149 fn on_refresh(&self) {
1150 let mut m = self.0.m.lock();
1151 m.instances.clear();
1152 m.unregistered = true;
1153 }
1154
1155 fn render_face(&self, renderer: &ViewRenderer) -> zng_view_api::font::FontFaceId {
1156 let mut m = self.0.m.lock();
1157 for r in m.render_ids.iter() {
1158 if &r.renderer == renderer {
1159 return r.face_id;
1160 }
1161 }
1162
1163 let data = match self.0.data.to_ipc() {
1164 Ok(d) => d,
1165 Err(e) => {
1166 tracing::error!("cannot allocate ipc font data, {e}");
1167 return zng_view_api::font::FontFaceId::INVALID;
1168 }
1169 };
1170
1171 let key = match renderer.add_font_face(data, self.0.face_index) {
1172 Ok(k) => k,
1173 Err(_) => {
1174 tracing::debug!("respawned calling `add_font`, will return dummy font key");
1175 return zng_view_api::font::FontFaceId::INVALID;
1176 }
1177 };
1178
1179 m.render_ids.push(RenderFontFace::new(renderer, key));
1180
1181 key
1182 }
1183
1184 pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1193 if self.is_empty() {
1194 None
1195 } else {
1196 Some(rustybuzz::Face::from_slice(&self.0.data, self.0.face_index).unwrap())
1197 }
1198 }
1199
1200 pub fn ttf(&self) -> Option<ttf_parser::Face<'_>> {
1209 if self.is_empty() {
1210 None
1211 } else {
1212 Some(ttf_parser::Face::parse(&self.0.data, self.0.face_index).unwrap())
1213 }
1214 }
1215
1216 pub fn bytes(&self) -> &FontBytes {
1218 &self.0.data
1219 }
1220 pub fn index(&self) -> u32 {
1222 self.0.face_index
1223 }
1224
1225 pub fn display_name(&self) -> &FontName {
1227 &self.0.display_name
1228 }
1229
1230 pub fn family_name(&self) -> &FontName {
1232 &self.0.family_name
1233 }
1234
1235 pub fn postscript_name(&self) -> Option<&str> {
1237 self.0.postscript_name.as_deref()
1238 }
1239
1240 pub fn style(&self) -> FontStyle {
1242 self.0.style
1243 }
1244
1245 pub fn weight(&self) -> FontWeight {
1247 self.0.weight
1248 }
1249
1250 pub fn stretch(&self) -> FontStretch {
1252 self.0.stretch
1253 }
1254
1255 pub fn is_monospace(&self) -> bool {
1257 self.0.flags.contains(FontFaceFlags::IS_MONOSPACE)
1258 }
1259
1260 pub fn metrics(&self) -> &FontFaceMetrics {
1262 &self.0.metrics
1263 }
1264
1265 pub fn sized(&self, font_size: Px, variations: RFontVariations) -> Font {
1274 let key = FontInstanceKey::new(font_size, &variations);
1275 let mut m = self.0.m.lock();
1276 if !m.unregistered {
1277 m.instances
1278 .entry(key)
1279 .or_insert_with(|| Font::new(self.clone(), font_size, variations))
1280 .clone()
1281 } else {
1282 tracing::debug!(target: "font_loading", "creating font from unregistered `{}`, will not cache", self.0.display_name);
1283 Font::new(self.clone(), font_size, variations)
1284 }
1285 }
1286
1287 pub fn synthesis_for(&self, style: FontStyle, weight: FontWeight) -> FontSynthesis {
1289 let mut synth = FontSynthesis::DISABLED;
1290
1291 if style != FontStyle::Normal && self.style() == FontStyle::Normal {
1292 synth |= FontSynthesis::OBLIQUE;
1294 }
1295 if weight > self.weight() {
1296 synth |= FontSynthesis::BOLD;
1299 }
1300
1301 synth
1302 }
1303
1304 pub fn is_cached(&self) -> bool {
1308 !self.0.m.lock().unregistered
1309 }
1310
1311 pub fn color_palettes(&self) -> ColorPalettes<'_> {
1315 match self.ttf() {
1316 Some(ttf) => ColorPalettes::new(*ttf.raw_face()),
1317 None => ColorPalettes::empty(),
1318 }
1319 }
1320
1321 pub fn color_glyphs(&self) -> ColorGlyphs<'_> {
1325 match self.ttf() {
1326 Some(ttf) => ColorGlyphs::new(*ttf.raw_face()),
1327 None => ColorGlyphs::empty(),
1328 }
1329 }
1330
1331 pub fn has_ligatures(&self) -> bool {
1333 self.0.flags.contains(FontFaceFlags::HAS_LIGATURES)
1334 }
1335
1336 pub fn has_ligature_caret_offsets(&self) -> bool {
1341 !self.0.lig_carets.is_empty()
1342 }
1343
1344 pub fn has_raster_images(&self) -> bool {
1346 self.0.flags.contains(FontFaceFlags::HAS_RASTER_IMAGES)
1347 }
1348
1349 pub fn has_svg_images(&self) -> bool {
1351 self.0.flags.contains(FontFaceFlags::HAS_SVG_IMAGES)
1352 }
1353}
1354
1355#[derive(Clone)]
1361pub struct Font(Arc<LoadedFont>);
1362struct LoadedFont {
1363 face: FontFace,
1364 size: Px,
1365 variations: RFontVariations,
1366 metrics: FontMetrics,
1367 render_keys: Mutex<Vec<RenderFont>>,
1368 small_word_cache: RwLock<HashMap<WordCacheKey<[u8; Font::SMALL_WORD_LEN]>, ShapedSegmentData>>,
1369 word_cache: RwLock<HashMap<WordCacheKey<String>, ShapedSegmentData>>,
1370}
1371impl fmt::Debug for Font {
1372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1373 f.debug_struct("Font")
1374 .field("face", &self.0.face)
1375 .field("size", &self.0.size)
1376 .field("metrics", &self.0.metrics)
1377 .field("render_keys.len()", &self.0.render_keys.lock().len())
1378 .field("small_word_cache.len()", &self.0.small_word_cache.read().len())
1379 .field("word_cache.len()", &self.0.word_cache.read().len())
1380 .finish()
1381 }
1382}
1383impl PartialEq for Font {
1384 fn eq(&self, other: &Self) -> bool {
1385 Arc::ptr_eq(&self.0, &other.0)
1386 }
1387}
1388impl Eq for Font {}
1389impl Font {
1390 const SMALL_WORD_LEN: usize = 8;
1391
1392 fn to_small_word(s: &str) -> Option<[u8; Self::SMALL_WORD_LEN]> {
1393 if s.len() <= Self::SMALL_WORD_LEN {
1394 let mut a = [b'\0'; Self::SMALL_WORD_LEN];
1395 a[..s.len()].copy_from_slice(s.as_bytes());
1396 Some(a)
1397 } else {
1398 None
1399 }
1400 }
1401
1402 fn new(face: FontFace, size: Px, variations: RFontVariations) -> Self {
1403 Font(Arc::new(LoadedFont {
1404 metrics: face.metrics().sized(size),
1405 face,
1406 size,
1407 variations,
1408 render_keys: Mutex::new(vec![]),
1409 small_word_cache: RwLock::default(),
1410 word_cache: RwLock::default(),
1411 }))
1412 }
1413
1414 fn render_font(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1415 let _span = tracing::trace_span!("Font::render_font").entered();
1416
1417 let mut render_keys = self.0.render_keys.lock();
1418 for r in render_keys.iter() {
1419 if &r.renderer == renderer && r.synthesis == synthesis {
1420 return r.font_id;
1421 }
1422 }
1423
1424 let font_key = self.0.face.render_face(renderer);
1425
1426 let mut opt = zng_view_api::font::FontOptions::default();
1427 opt.synthetic_oblique = synthesis.contains(FontSynthesis::OBLIQUE);
1428 opt.synthetic_bold = synthesis.contains(FontSynthesis::BOLD);
1429 let variations = self.0.variations.iter().map(|v| (v.tag.to_bytes(), v.value)).collect();
1430
1431 let key = match renderer.add_font(font_key, self.0.size, opt, variations) {
1432 Ok(k) => k,
1433 Err(_) => {
1434 tracing::debug!("respawned calling `add_font_instance`, will return dummy font key");
1435 return zng_view_api::font::FontId::INVALID;
1436 }
1437 };
1438
1439 render_keys.push(RenderFont::new(renderer, synthesis, key));
1440
1441 key
1442 }
1443
1444 pub fn face(&self) -> &FontFace {
1446 &self.0.face
1447 }
1448
1449 pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1451 let ppem = self.0.size.0 as u16;
1452
1453 let mut font = self.0.face.harfbuzz()?;
1454
1455 font.set_pixels_per_em(Some((ppem, ppem)));
1456 font.set_variations(&self.0.variations);
1457
1458 Some(font)
1459 }
1460
1461 pub fn size(&self) -> Px {
1465 self.0.size
1466 }
1467
1468 pub fn variations(&self) -> &RFontVariations {
1470 &self.0.variations
1471 }
1472
1473 pub fn metrics(&self) -> &FontMetrics {
1475 &self.0.metrics
1476 }
1477
1478 pub fn ligature_caret_offsets(
1484 &self,
1485 lig: zng_view_api::font::GlyphIndex,
1486 ) -> impl ExactSizeIterator<Item = f32> + DoubleEndedIterator + '_ {
1487 let face = &self.0.face.0;
1488 face.lig_carets.carets(lig).iter().map(move |&o| match o {
1489 ligature_util::LigatureCaret::Coordinate(o) => {
1490 let size_scale = 1.0 / face.metrics.units_per_em as f32 * self.0.size.0 as f32;
1491 o as f32 * size_scale
1492 }
1493 ligature_util::LigatureCaret::GlyphContourPoint(i) => {
1494 if let Some(f) = self.harfbuzz() {
1495 struct Search {
1496 i: u16,
1497 s: u16,
1498 x: f32,
1499 }
1500 impl Search {
1501 fn check(&mut self, x: f32) {
1502 self.s = self.s.saturating_add(1);
1503 if self.s == self.i {
1504 self.x = x;
1505 }
1506 }
1507 }
1508 impl ttf_parser::OutlineBuilder for Search {
1509 fn move_to(&mut self, x: f32, _y: f32) {
1510 self.check(x);
1511 }
1512
1513 fn line_to(&mut self, x: f32, _y: f32) {
1514 self.check(x);
1515 }
1516
1517 fn quad_to(&mut self, _x1: f32, _y1: f32, x: f32, _y: f32) {
1518 self.check(x)
1519 }
1520
1521 fn curve_to(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, x: f32, _y: f32) {
1522 self.check(x);
1523 }
1524
1525 fn close(&mut self) {}
1526 }
1527 let mut search = Search { i, s: 0, x: 0.0 };
1528 if f.outline_glyph(ttf_parser::GlyphId(lig as _), &mut search).is_some() && search.s >= search.i {
1529 return search.x * self.0.metrics.size_scale;
1530 }
1531 }
1532 0.0
1533 }
1534 })
1535 }
1536}
1537impl zng_app::render::Font for Font {
1538 fn is_empty_fallback(&self) -> bool {
1539 self.face().is_empty()
1540 }
1541
1542 fn renderer_id(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1543 self.render_font(renderer, synthesis)
1544 }
1545}
1546
1547#[derive(Debug, Clone)]
1551pub struct FontFaceList {
1552 fonts: Box<[FontFace]>,
1553 requested_style: FontStyle,
1554 requested_weight: FontWeight,
1555 requested_stretch: FontStretch,
1556}
1557impl FontFaceList {
1558 pub fn empty() -> Self {
1560 Self {
1561 fonts: Box::new([FontFace::empty()]),
1562 requested_style: FontStyle::Normal,
1563 requested_weight: FontWeight::NORMAL,
1564 requested_stretch: FontStretch::NORMAL,
1565 }
1566 }
1567
1568 pub fn requested_style(&self) -> FontStyle {
1570 self.requested_style
1571 }
1572
1573 pub fn requested_weight(&self) -> FontWeight {
1575 self.requested_weight
1576 }
1577
1578 pub fn requested_stretch(&self) -> FontStretch {
1580 self.requested_stretch
1581 }
1582
1583 pub fn best(&self) -> &FontFace {
1585 &self.fonts[0]
1586 }
1587
1588 pub fn face_synthesis(&self, face_index: usize) -> FontSynthesis {
1590 if let Some(face) = self.fonts.get(face_index) {
1591 face.synthesis_for(self.requested_style, self.requested_weight)
1592 } else {
1593 FontSynthesis::DISABLED
1594 }
1595 }
1596
1597 pub fn iter(&self) -> std::slice::Iter<'_, FontFace> {
1599 self.fonts.iter()
1600 }
1601
1602 pub fn len(&self) -> usize {
1606 self.fonts.len()
1607 }
1608
1609 pub fn is_empty(&self) -> bool {
1611 self.fonts[0].is_empty() && self.fonts.len() == 1
1612 }
1613
1614 pub fn sized(&self, font_size: Px, variations: RFontVariations) -> FontList {
1618 FontList {
1619 fonts: self.fonts.iter().map(|f| f.sized(font_size, variations.clone())).collect(),
1620 requested_style: self.requested_style,
1621 requested_weight: self.requested_weight,
1622 requested_stretch: self.requested_stretch,
1623 }
1624 }
1625}
1626impl PartialEq for FontFaceList {
1627 fn eq(&self, other: &Self) -> bool {
1629 self.requested_style == other.requested_style
1630 && self.requested_weight == other.requested_weight
1631 && self.requested_stretch == other.requested_stretch
1632 && self.fonts.len() == other.fonts.len()
1633 && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1634 }
1635}
1636impl Eq for FontFaceList {}
1637impl std::ops::Deref for FontFaceList {
1638 type Target = [FontFace];
1639
1640 fn deref(&self) -> &Self::Target {
1641 &self.fonts
1642 }
1643}
1644impl<'a> std::iter::IntoIterator for &'a FontFaceList {
1645 type Item = &'a FontFace;
1646
1647 type IntoIter = std::slice::Iter<'a, FontFace>;
1648
1649 fn into_iter(self) -> Self::IntoIter {
1650 self.iter()
1651 }
1652}
1653impl std::ops::Index<usize> for FontFaceList {
1654 type Output = FontFace;
1655
1656 fn index(&self, index: usize) -> &Self::Output {
1657 &self.fonts[index]
1658 }
1659}
1660
1661#[derive(Debug, Clone)]
1663pub struct FontList {
1664 fonts: Box<[Font]>,
1665 requested_style: FontStyle,
1666 requested_weight: FontWeight,
1667 requested_stretch: FontStretch,
1668}
1669#[expect(clippy::len_without_is_empty)] impl FontList {
1671 pub fn best(&self) -> &Font {
1673 &self.fonts[0]
1674 }
1675
1676 pub fn requested_size(&self) -> Px {
1678 self.fonts[0].size()
1679 }
1680
1681 pub fn requested_style(&self) -> FontStyle {
1683 self.requested_style
1684 }
1685
1686 pub fn requested_weight(&self) -> FontWeight {
1688 self.requested_weight
1689 }
1690
1691 pub fn requested_stretch(&self) -> FontStretch {
1693 self.requested_stretch
1694 }
1695
1696 pub fn face_synthesis(&self, font_index: usize) -> FontSynthesis {
1698 if let Some(font) = self.fonts.get(font_index) {
1699 font.0.face.synthesis_for(self.requested_style, self.requested_weight)
1700 } else {
1701 FontSynthesis::DISABLED
1702 }
1703 }
1704
1705 pub fn iter(&self) -> std::slice::Iter<'_, Font> {
1707 self.fonts.iter()
1708 }
1709
1710 pub fn len(&self) -> usize {
1714 self.fonts.len()
1715 }
1716
1717 pub fn is_sized_from(&self, faces: &FontFaceList) -> bool {
1719 if self.len() != faces.len() {
1720 return false;
1721 }
1722
1723 for (font, face) in self.iter().zip(faces.iter()) {
1724 if font.face() != face {
1725 return false;
1726 }
1727 }
1728
1729 true
1730 }
1731}
1732impl PartialEq for FontList {
1733 fn eq(&self, other: &Self) -> bool {
1735 self.requested_style == other.requested_style
1736 && self.requested_weight == other.requested_weight
1737 && self.requested_stretch == other.requested_stretch
1738 && self.fonts.len() == other.fonts.len()
1739 && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1740 }
1741}
1742impl Eq for FontList {}
1743impl std::ops::Deref for FontList {
1744 type Target = [Font];
1745
1746 fn deref(&self) -> &Self::Target {
1747 &self.fonts
1748 }
1749}
1750impl<'a> std::iter::IntoIterator for &'a FontList {
1751 type Item = &'a Font;
1752
1753 type IntoIter = std::slice::Iter<'a, Font>;
1754
1755 fn into_iter(self) -> Self::IntoIter {
1756 self.iter()
1757 }
1758}
1759impl<I: SliceIndex<[Font]>> std::ops::Index<I> for FontList {
1760 type Output = I::Output;
1761
1762 fn index(&self, index: I) -> &I::Output {
1763 &self.fonts[index]
1764 }
1765}
1766
1767struct FontFaceLoader {
1768 custom_fonts: HashMap<FontName, Vec<FontFace>>,
1769 unregister_requests: Vec<(FontName, ResponderVar<bool>)>,
1770
1771 system_fonts_cache: HashMap<FontName, Vec<SystemFontFace>>,
1772 list_cache: HashMap<Box<[FontName]>, Vec<FontFaceListQuery>>,
1773}
1774struct SystemFontFace {
1775 properties: (FontStyle, FontWeight, FontStretch),
1776 result: ResponseVar<Option<FontFace>>,
1777}
1778struct FontFaceListQuery {
1779 properties: (FontStyle, FontWeight, FontStretch),
1780 lang: Lang,
1781 result: ResponseVar<FontFaceList>,
1782}
1783impl FontFaceLoader {
1784 fn new() -> Self {
1785 FontFaceLoader {
1786 custom_fonts: HashMap::new(),
1787 unregister_requests: vec![],
1788 system_fonts_cache: HashMap::new(),
1789 list_cache: HashMap::new(),
1790 }
1791 }
1792
1793 fn on_view_process_respawn(&mut self) {
1794 let sys_fonts = self.system_fonts_cache.values().flatten().filter_map(|f| f.result.rsp().flatten());
1795 for face in self.custom_fonts.values().flatten().cloned().chain(sys_fonts) {
1796 let mut m = face.0.m.lock();
1797 m.render_ids.clear();
1798 for inst in m.instances.values() {
1799 inst.0.render_keys.lock().clear();
1800 }
1801 }
1802 }
1803
1804 fn on_refresh(&mut self) {
1805 for (_, sys_family) in self.system_fonts_cache.drain() {
1806 for sys_font in sys_family {
1807 sys_font.result.with(|r| {
1808 if let Some(Some(face)) = r.done() {
1809 face.on_refresh();
1810 }
1811 });
1812 }
1813 }
1814 }
1815 fn on_prune(&mut self) {
1816 self.system_fonts_cache.retain(|_, v| {
1817 v.retain(|sff| {
1818 if sff.result.strong_count() == 1 {
1819 sff.result.with(|r| {
1820 match r.done() {
1821 Some(Some(face)) => Arc::strong_count(&face.0) > 1, Some(None) => false, None => true, }
1825 })
1826 } else {
1827 true
1829 }
1830 });
1831 !v.is_empty()
1832 });
1833
1834 self.list_cache.clear();
1835 }
1836
1837 fn register(custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
1838 let resp = task::respond(FontFace::load_custom(custom_font));
1840
1841 resp.hook(|args| {
1843 if let Some(done) = args.value().done() {
1844 if let Ok(face) = done {
1845 let mut fonts = FONTS_SV.write();
1846 let family = fonts.loader.custom_fonts.entry(face.0.family_name.clone()).or_default();
1847 let existing = family
1848 .iter()
1849 .position(|f| f.0.weight == face.0.weight && f.0.style == face.0.style && f.0.stretch == face.0.stretch);
1850
1851 if let Some(i) = existing {
1852 family[i] = face.clone();
1853 } else {
1854 family.push(face.clone());
1855 }
1856
1857 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
1858 }
1859 false
1860 } else {
1861 true
1862 }
1863 })
1864 .perm();
1865 resp
1866 }
1867
1868 fn unregister(&mut self, custom_family: FontName) -> ResponseVar<bool> {
1869 let (responder, response) = response_var();
1870
1871 if !self.unregister_requests.is_empty() {
1872 UPDATES.update(None);
1873 }
1874 self.unregister_requests.push((custom_family, responder));
1875
1876 response
1877 }
1878
1879 fn try_list(
1880 &self,
1881 families: &[FontName],
1882 style: FontStyle,
1883 weight: FontWeight,
1884 stretch: FontStretch,
1885 lang: &Lang,
1886 ) -> Option<ResponseVar<FontFaceList>> {
1887 if let Some(queries) = self.list_cache.get(families) {
1888 for q in queries {
1889 if q.properties == (style, weight, stretch) && &q.lang == lang {
1890 return Some(q.result.clone());
1891 }
1892 }
1893 }
1894 None
1895 }
1896
1897 fn load_list(
1898 &mut self,
1899 families: &[FontName],
1900 style: FontStyle,
1901 weight: FontWeight,
1902 stretch: FontStretch,
1903 lang: &Lang,
1904 ) -> ResponseVar<FontFaceList> {
1905 if let Some(r) = self.try_list(families, style, weight, stretch, lang) {
1906 return r;
1907 }
1908
1909 let mut list = Vec::with_capacity(families.len() + 1);
1910 let mut pending = vec![];
1911
1912 {
1913 let fallback = [GenericFonts {}.fallback(lang)];
1914 let mut used = HashSet::with_capacity(families.len());
1915 for name in families.iter().chain(&fallback) {
1916 if !used.insert(name) {
1917 continue;
1918 }
1919
1920 let face = self.load(name, style, weight, stretch, lang);
1921 if face.is_done() {
1922 if let Some(face) = face.rsp().unwrap() {
1923 list.push(face);
1924 }
1925 } else {
1926 pending.push((list.len(), face));
1927 }
1928 }
1929 }
1930
1931 let r = if pending.is_empty() {
1932 if list.is_empty() {
1933 tracing::error!(target: "font_loading", "failed to load fallback font");
1934 list.push(FontFace::empty());
1935 }
1936 response_done_var(FontFaceList {
1937 fonts: list.into_boxed_slice(),
1938 requested_style: style,
1939 requested_weight: weight,
1940 requested_stretch: stretch,
1941 })
1942 } else {
1943 task::respond(async move {
1944 for (i, pending) in pending.into_iter().rev() {
1945 if let Some(rsp) = pending.wait_rsp().await {
1946 list.insert(i, rsp);
1947 }
1948 }
1949
1950 if list.is_empty() {
1951 tracing::error!(target: "font_loading", "failed to load fallback font");
1952 list.push(FontFace::empty());
1953 }
1954
1955 FontFaceList {
1956 fonts: list.into_boxed_slice(),
1957 requested_style: style,
1958 requested_weight: weight,
1959 requested_stretch: stretch,
1960 }
1961 })
1962 };
1963
1964 self.list_cache
1965 .entry(families.iter().cloned().collect())
1966 .or_insert_with(|| Vec::with_capacity(1))
1967 .push(FontFaceListQuery {
1968 properties: (style, weight, stretch),
1969 lang: lang.clone(),
1970 result: r.clone(),
1971 });
1972
1973 r
1974 }
1975
1976 fn try_cached(
1977 &self,
1978 font_name: &FontName,
1979 style: FontStyle,
1980 weight: FontWeight,
1981 stretch: FontStretch,
1982 lang: &Lang,
1983 ) -> Option<ResponseVar<Option<FontFace>>> {
1984 let resolved = GenericFonts {}.resolve(font_name, lang);
1985 let font_name = resolved.as_ref().unwrap_or(font_name);
1986 self.try_resolved(font_name, style, weight, stretch)
1987 }
1988
1989 fn load(
1991 &mut self,
1992 font_name: &FontName,
1993 style: FontStyle,
1994 weight: FontWeight,
1995 stretch: FontStretch,
1996 lang: &Lang,
1997 ) -> ResponseVar<Option<FontFace>> {
1998 let resolved = GenericFonts {}.resolve(font_name, lang);
1999 let font_name = resolved.as_ref().unwrap_or(font_name);
2000 self.load_resolved(font_name, style, weight, stretch)
2001 }
2002
2003 fn try_resolved(
2005 &self,
2006 font_name: &FontName,
2007 style: FontStyle,
2008 weight: FontWeight,
2009 stretch: FontStretch,
2010 ) -> Option<ResponseVar<Option<FontFace>>> {
2011 if let Some(custom_family) = self.custom_fonts.get(font_name) {
2012 let custom = Self::match_custom(custom_family, style, weight, stretch);
2013 return Some(response_done_var(Some(custom)));
2014 }
2015
2016 if let Some(cached_sys_family) = self.system_fonts_cache.get(font_name) {
2017 for sys_face in cached_sys_family.iter() {
2018 if sys_face.properties == (style, weight, stretch) {
2019 return Some(sys_face.result.clone());
2020 }
2021 }
2022 }
2023
2024 None
2025 }
2026
2027 fn load_resolved(
2029 &mut self,
2030 font_name: &FontName,
2031 style: FontStyle,
2032 weight: FontWeight,
2033 stretch: FontStretch,
2034 ) -> ResponseVar<Option<FontFace>> {
2035 if let Some(cached) = self.try_resolved(font_name, style, weight, stretch) {
2036 return cached;
2037 }
2038
2039 let load = task::wait(clmv!(font_name, || {
2040 let (bytes, face_index) = match Self::get_system(&font_name, style, weight, stretch) {
2041 Some(h) => h,
2042 None => {
2043 #[cfg(debug_assertions)]
2044 static NOT_FOUND: Mutex<Option<HashSet<FontName>>> = Mutex::new(None);
2045
2046 #[cfg(debug_assertions)]
2047 if NOT_FOUND.lock().get_or_insert_with(HashSet::default).insert(font_name.clone()) {
2048 tracing::debug!(r#"font "{font_name}" not found"#);
2049 }
2050
2051 return None;
2052 }
2053 };
2054 match FontFace::load(bytes, face_index) {
2055 Ok(f) => Some(f),
2056 Err(FontLoadingError::UnknownFormat) => None,
2057 Err(e) => {
2058 tracing::error!(target: "font_loading", "failed to load system font, {e}\nquery: {:?}", (font_name, style, weight, stretch));
2059 None
2060 }
2061 }
2062 }));
2063 let result = task::respond(async_clmv!(font_name, {
2064 match task::with_deadline(load, 10.secs()).await {
2065 Ok(r) => r,
2066 Err(_) => {
2067 tracing::error!(target: "font_loading", "timeout loading {font_name:?}");
2068 None
2069 }
2070 }
2071 }));
2072
2073 self.system_fonts_cache
2074 .entry(font_name.clone())
2075 .or_insert_with(|| Vec::with_capacity(1))
2076 .push(SystemFontFace {
2077 properties: (style, weight, stretch),
2078 result: result.clone(),
2079 });
2080
2081 result
2082 }
2083
2084 fn get_system(font_name: &FontName, style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Option<(FontBytes, u32)> {
2085 let _span = tracing::trace_span!("FontFaceLoader::get_system").entered();
2086 match query_util::best(font_name, style, weight, stretch) {
2087 Ok(r) => r,
2088 Err(e) => {
2089 tracing::error!("cannot get `{font_name}` system font, {e}");
2090 None
2091 }
2092 }
2093 }
2094
2095 fn match_custom(faces: &[FontFace], style: FontStyle, weight: FontWeight, stretch: FontStretch) -> FontFace {
2096 if faces.len() == 1 {
2097 return faces[0].clone();
2099 }
2100
2101 let mut set = Vec::with_capacity(faces.len());
2102 let mut set_dist = 0.0f64; let wrong_side = if stretch <= FontStretch::NORMAL {
2109 |s| s > FontStretch::NORMAL
2110 } else {
2111 |s| s <= FontStretch::NORMAL
2112 };
2113 for face in faces {
2114 let mut dist = (face.stretch().0 - stretch.0).abs() as f64;
2115 if wrong_side(face.stretch()) {
2116 dist += f32::MAX as f64 + 1.0;
2117 }
2118
2119 if set.is_empty() {
2120 set.push(face);
2121 set_dist = dist;
2122 } else if dist < set_dist {
2123 set_dist = dist;
2125 set.clear();
2126 set.push(face);
2127 } else if (dist - set_dist).abs() < 0.0001 {
2128 set.push(face);
2130 }
2131 }
2132 if set.len() == 1 {
2133 return set[0].clone();
2134 }
2135
2136 let style_pref = match style {
2141 FontStyle::Normal => [FontStyle::Normal, FontStyle::Oblique, FontStyle::Italic],
2142 FontStyle::Italic => [FontStyle::Italic, FontStyle::Oblique, FontStyle::Normal],
2143 FontStyle::Oblique => [FontStyle::Oblique, FontStyle::Italic, FontStyle::Normal],
2144 };
2145 let mut best_style = style_pref.len();
2146 for face in &set {
2147 let i = style_pref.iter().position(|&s| s == face.style()).unwrap();
2148 if i < best_style {
2149 best_style = i;
2150 }
2151 }
2152 set.retain(|f| f.style() == style_pref[best_style]);
2153 if set.len() == 1 {
2154 return set[0].clone();
2155 }
2156
2157 let add_penalty = if weight.0 >= 400.0 && weight.0 <= 500.0 {
2165 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2167 if face.weight() < weight {
2169 *dist += 100.0;
2171 } else if face.weight().0 > 500.0 {
2172 *dist += 600.0;
2174 }
2175 }
2176 } else if weight.0 < 400.0 {
2177 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2179 if face.weight() > weight {
2180 *dist += weight.0 as f64;
2181 }
2182 }
2183 } else {
2184 debug_assert!(weight.0 > 500.0);
2185 |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2187 if face.weight() < weight {
2188 *dist += f32::MAX as f64;
2189 }
2190 }
2191 };
2192
2193 let mut best = set[0];
2194 let mut best_dist = f64::MAX;
2195
2196 for face in &set {
2197 let mut dist = (face.weight().0 - weight.0).abs() as f64;
2198
2199 add_penalty(face, weight, &mut dist);
2200
2201 if dist < best_dist {
2202 best_dist = dist;
2203 best = face;
2204 }
2205 }
2206
2207 best.clone()
2208 }
2209}
2210
2211struct RenderFontFace {
2212 renderer: ViewRenderer,
2213 face_id: zng_view_api::font::FontFaceId,
2214}
2215impl RenderFontFace {
2216 fn new(renderer: &ViewRenderer, face_id: zng_view_api::font::FontFaceId) -> Self {
2217 RenderFontFace {
2218 renderer: renderer.clone(),
2219 face_id,
2220 }
2221 }
2222}
2223impl Drop for RenderFontFace {
2224 fn drop(&mut self) {
2225 let _ = self.renderer.delete_font_face(self.face_id);
2227 }
2228}
2229
2230struct RenderFont {
2231 renderer: ViewRenderer,
2232 synthesis: FontSynthesis,
2233 font_id: zng_view_api::font::FontId,
2234}
2235impl RenderFont {
2236 fn new(renderer: &ViewRenderer, synthesis: FontSynthesis, font_id: zng_view_api::font::FontId) -> RenderFont {
2237 RenderFont {
2238 renderer: renderer.clone(),
2239 synthesis,
2240 font_id,
2241 }
2242 }
2243}
2244impl Drop for RenderFont {
2245 fn drop(&mut self) {
2246 let _ = self.renderer.delete_font(self.font_id);
2248 }
2249}
2250
2251app_local! {
2252 static GENERIC_FONTS_SV: GenericFontsService = GenericFontsService::new();
2253}
2254
2255struct GenericFontsService {
2256 serif: LangMap<FontName>,
2257 sans_serif: LangMap<FontName>,
2258 monospace: LangMap<FontName>,
2259 cursive: LangMap<FontName>,
2260 fantasy: LangMap<FontName>,
2261 fallback: LangMap<FontName>,
2262
2263 requests: Vec<Box<dyn FnOnce(&mut GenericFontsService) + Send + Sync>>,
2264}
2265impl GenericFontsService {
2266 fn new() -> Self {
2267 fn default(name: impl Into<FontName>) -> LangMap<FontName> {
2268 let mut f = LangMap::with_capacity(1);
2269 f.insert(lang!(und), name.into());
2270 f
2271 }
2272
2273 let serif = "serif";
2274 let sans_serif = "sans-serif";
2275 let monospace = "monospace";
2276 let cursive = "cursive";
2277 let fantasy = "fantasy";
2278 let fallback = if cfg!(windows) {
2279 "Segoe UI Symbol"
2280 } else if cfg!(target_os = "linux") {
2281 "Standard Symbols PS"
2282 } else {
2283 "sans-serif"
2284 };
2285
2286 GenericFontsService {
2287 serif: default(serif),
2288 sans_serif: default(sans_serif),
2289 monospace: default(monospace),
2290 cursive: default(cursive),
2291 fantasy: default(fantasy),
2292
2293 fallback: default(fallback),
2294
2295 requests: vec![],
2296 }
2297 }
2298}
2299
2300#[non_exhaustive]
2315pub struct GenericFonts {}
2316macro_rules! impl_fallback_accessors {
2317 ($($name:ident=$name_str:tt),+ $(,)?) => {$($crate::paste! {
2318 #[doc = "Gets the fallback *"$name_str "* font for the given language."]
2319 #[doc = "Note that the returned name can still be the generic `\""$name_str "\"`, this delegates the resolution to the operating system."]
2323
2324 pub fn $name(&self, lang: &Lang) -> FontName {
2325 GENERIC_FONTS_SV.read().$name.get(lang).unwrap().clone()
2326 }
2327
2328 #[doc = "Sets the fallback *"$name_str "* font for the given language."]
2329 pub fn [<set_ $name>]<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2334 let mut g = GENERIC_FONTS_SV.write();
2335 let font_name = font_name.into();
2336 if g.requests.is_empty() {
2337 UPDATES.update(None);
2338 }
2339 g.requests.push(Box::new(move |g| {
2340 g.$name.insert(lang.clone(), font_name);
2341 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::GenericFont(FontName::$name(), lang)));
2342 }));
2343 }
2344 })+};
2345}
2346impl GenericFonts {
2347 #[rustfmt::skip] impl_fallback_accessors! {
2349 serif="serif", sans_serif="sans-serif", monospace="monospace", cursive="cursive", fantasy="fantasy"
2350 }
2351
2352 pub fn fallback(&self, lang: &Lang) -> FontName {
2356 GENERIC_FONTS_SV.read().fallback.get(lang).unwrap().clone()
2357 }
2358
2359 pub fn set_fallback<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2365 let mut g = GENERIC_FONTS_SV.write();
2366 if g.requests.is_empty() {
2367 UPDATES.update(None);
2368 }
2369 let font_name = font_name.into();
2370 g.requests.push(Box::new(move |g| {
2371 FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Fallback(lang.clone())));
2372 g.fallback.insert(lang, font_name);
2373 }));
2374 }
2375
2376 pub fn resolve(&self, name: &FontName, lang: &Lang) -> Option<FontName> {
2380 if name == &FontName::serif() {
2381 Some(self.serif(lang))
2382 } else if name == &FontName::sans_serif() {
2383 Some(self.sans_serif(lang))
2384 } else if name == &FontName::monospace() {
2385 Some(self.monospace(lang))
2386 } else if name == &FontName::cursive() {
2387 Some(self.cursive(lang))
2388 } else if name == &FontName::fantasy() {
2389 Some(self.fantasy(lang))
2390 } else {
2391 None
2392 }
2393 }
2394}
2395
2396#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2397pub(crate) enum WeakFontBytes {
2398 Ipc(WeakIpcBytes),
2399 Arc(std::sync::Weak<Vec<u8>>),
2400 Static(&'static [u8]),
2401 Mmap(std::sync::Weak<SystemFontBytes>),
2402}
2403#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2404impl WeakFontBytes {
2405 pub(crate) fn upgrade(&self) -> Option<FontBytes> {
2406 match self {
2407 WeakFontBytes::Ipc(weak) => Some(FontBytes(FontBytesImpl::Ipc(weak.upgrade()?))),
2408 WeakFontBytes::Arc(weak) => Some(FontBytes(FontBytesImpl::Arc(weak.upgrade()?))),
2409 WeakFontBytes::Static(b) => Some(FontBytes(FontBytesImpl::Static(b))),
2410 WeakFontBytes::Mmap(weak) => Some(FontBytes(FontBytesImpl::System(weak.upgrade()?))),
2411 }
2412 }
2413
2414 pub(crate) fn strong_count(&self) -> usize {
2415 match self {
2416 WeakFontBytes::Ipc(weak) => weak.strong_count(),
2417 WeakFontBytes::Arc(weak) => weak.strong_count(),
2418 WeakFontBytes::Static(_) => 1,
2419 WeakFontBytes::Mmap(weak) => weak.strong_count(),
2420 }
2421 }
2422}
2423
2424struct SystemFontBytes {
2425 path: std::path::PathBuf,
2426 mmap: IpcBytes,
2427}
2428
2429#[derive(Clone)]
2430enum FontBytesImpl {
2431 Ipc(IpcBytes),
2433 Arc(Arc<Vec<u8>>),
2434 Static(&'static [u8]),
2435 System(Arc<SystemFontBytes>),
2436}
2437#[derive(Clone)]
2439pub struct FontBytes(FontBytesImpl);
2440impl FontBytes {
2441 pub fn from_ipc(bytes: IpcBytes) -> Self {
2443 Self(FontBytesImpl::Ipc(bytes))
2444 }
2445
2446 pub fn from_vec(bytes: Vec<u8>) -> io::Result<Self> {
2448 Ok(Self(FontBytesImpl::Ipc(IpcBytes::from_vec_blocking(bytes)?)))
2449 }
2450
2451 pub fn from_static(bytes: &'static [u8]) -> Self {
2453 Self(FontBytesImpl::Static(bytes))
2454 }
2455
2456 pub fn from_arc(bytes: Arc<Vec<u8>>) -> Self {
2460 Self(FontBytesImpl::Arc(bytes))
2461 }
2462
2463 pub fn from_file(path: PathBuf) -> io::Result<Self> {
2465 let path = dunce::canonicalize(path)?;
2466
2467 #[cfg(windows)]
2468 {
2469 use windows::Win32::{Foundation::MAX_PATH, System::SystemInformation::GetSystemWindowsDirectoryW};
2470 let mut buffer = [0u16; MAX_PATH as usize];
2471 let len = unsafe { GetSystemWindowsDirectoryW(Some(&mut buffer)) };
2473 let fonts_dir = String::from_utf16_lossy(&buffer[..len as usize]);
2474 if path.starts_with(fonts_dir) {
2476 return unsafe { load_from_system(path) };
2478 }
2479 }
2480 #[cfg(target_os = "macos")]
2481 if path.starts_with("/System/Library/Fonts/") || path.starts_with("/Library/Fonts/") {
2482 return unsafe { load_from_system(path) };
2484 }
2485 #[cfg(target_os = "android")]
2486 if path.starts_with("/system/fonts/") || path.starts_with("/system/font/") || path.starts_with("/system/product/fonts/") {
2487 return unsafe { load_from_system(path) };
2489 }
2490 #[cfg(unix)]
2491 if path.starts_with("/usr/share/fonts/") {
2492 return unsafe { load_from_system(path) };
2494 }
2495
2496 #[cfg(ipc)]
2497 unsafe fn load_from_system(path: PathBuf) -> io::Result<FontBytes> {
2498 let mmap = unsafe { IpcBytes::open_memmap_blocking(path.clone(), None) }?;
2500 Ok(FontBytes(FontBytesImpl::System(Arc::new(SystemFontBytes { path, mmap }))))
2501 }
2502
2503 #[cfg(all(not(ipc), not(target_arch = "wasm32")))]
2504 unsafe fn load_from_system(path: PathBuf) -> io::Result<FontBytes> {
2505 let mmap = IpcBytes::from_file_blocking(&path)?;
2506 Ok(FontBytes(FontBytesImpl::System(Arc::new(SystemFontBytes { path, mmap }))))
2507 }
2508
2509 Ok(Self(FontBytesImpl::Ipc(IpcBytes::from_file_blocking(&path)?)))
2510 }
2511
2512 #[cfg(ipc)]
2519 pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2520 let ipc = unsafe { IpcBytes::open_memmap_blocking(path, None) }?;
2522 Ok(Self(FontBytesImpl::Ipc(ipc)))
2523 }
2524
2525 #[cfg(ipc)]
2529 pub fn mmap_path(&self) -> Option<&std::path::Path> {
2530 if let FontBytesImpl::System(m) = &self.0 {
2531 Some(&m.path)
2532 } else {
2533 None
2534 }
2535 }
2536
2537 pub fn to_ipc(&self) -> io::Result<IpcFontBytes> {
2539 Ok(if let FontBytesImpl::System(m) = &self.0 {
2540 IpcFontBytes::System(m.path.clone())
2541 } else {
2542 IpcFontBytes::Bytes(self.to_ipc_bytes()?)
2543 })
2544 }
2545
2546 pub fn to_ipc_bytes(&self) -> io::Result<IpcBytes> {
2548 match &self.0 {
2549 FontBytesImpl::Ipc(b) => Ok(b.clone()),
2550 FontBytesImpl::Arc(b) => IpcBytes::from_slice_blocking(b),
2551 FontBytesImpl::Static(b) => IpcBytes::from_slice_blocking(b),
2552 FontBytesImpl::System(m) => IpcBytes::from_slice_blocking(&m.mmap[..]),
2553 }
2554 }
2555
2556 #[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2557 pub(crate) fn downgrade(&self) -> WeakFontBytes {
2558 match &self.0 {
2559 FontBytesImpl::Ipc(ipc) => WeakFontBytes::Ipc(ipc.downgrade()),
2560 FontBytesImpl::Arc(arc) => WeakFontBytes::Arc(Arc::downgrade(arc)),
2561 FontBytesImpl::Static(b) => WeakFontBytes::Static(b),
2562 FontBytesImpl::System(arc) => WeakFontBytes::Mmap(Arc::downgrade(arc)),
2563 }
2564 }
2565}
2566impl std::ops::Deref for FontBytes {
2567 type Target = [u8];
2568
2569 fn deref(&self) -> &Self::Target {
2570 match &self.0 {
2571 FontBytesImpl::Ipc(b) => &b[..],
2572 FontBytesImpl::Arc(b) => &b[..],
2573 FontBytesImpl::Static(b) => b,
2574 FontBytesImpl::System(m) => &m.mmap[..],
2575 }
2576 }
2577}
2578impl fmt::Debug for FontBytes {
2579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2580 let mut b = f.debug_struct("FontBytes");
2581 b.field(
2582 ".kind",
2583 &match &self.0 {
2584 FontBytesImpl::Ipc(_) => "IpcBytes",
2585 FontBytesImpl::Arc(_) => "Arc",
2586 FontBytesImpl::Static(_) => "Static",
2587 FontBytesImpl::System(_) => "Mmap",
2588 },
2589 );
2590 b.field(".len", &self.len().bytes());
2591 if let FontBytesImpl::System(m) = &self.0 {
2592 b.field(".path", &m.path);
2593 }
2594
2595 b.finish()
2596 }
2597}
2598
2599#[derive(Debug, Clone)]
2600enum FontSource {
2601 File(PathBuf, u32),
2602 Memory(FontBytes, u32),
2603 Alias(FontName),
2604}
2605
2606#[derive(Debug, Clone)]
2608pub struct CustomFont {
2609 name: FontName,
2610 source: FontSource,
2611 stretch: FontStretch,
2612 style: FontStyle,
2613 weight: FontWeight,
2614}
2615impl CustomFont {
2616 pub fn from_file<N: Into<FontName>, P: Into<PathBuf>>(name: N, path: P, font_index: u32) -> Self {
2624 CustomFont {
2625 name: name.into(),
2626 source: FontSource::File(path.into(), font_index),
2627 stretch: FontStretch::NORMAL,
2628 style: FontStyle::Normal,
2629 weight: FontWeight::NORMAL,
2630 }
2631 }
2632
2633 pub fn from_bytes<N: Into<FontName>>(name: N, data: FontBytes, font_index: u32) -> Self {
2641 CustomFont {
2642 name: name.into(),
2643 source: FontSource::Memory(data, font_index),
2644 stretch: FontStretch::NORMAL,
2645 style: FontStyle::Normal,
2646 weight: FontWeight::NORMAL,
2647 }
2648 }
2649
2650 pub fn from_other<N: Into<FontName>, O: Into<FontName>>(name: N, other_font: O) -> Self {
2656 CustomFont {
2657 name: name.into(),
2658 source: FontSource::Alias(other_font.into()),
2659 stretch: FontStretch::NORMAL,
2660 style: FontStyle::Normal,
2661 weight: FontWeight::NORMAL,
2662 }
2663 }
2664
2665 pub fn stretch(mut self, stretch: FontStretch) -> Self {
2669 self.stretch = stretch;
2670 self
2671 }
2672
2673 pub fn style(mut self, style: FontStyle) -> Self {
2677 self.style = style;
2678 self
2679 }
2680
2681 pub fn weight(mut self, weight: FontWeight) -> Self {
2685 self.weight = weight;
2686 self
2687 }
2688}
2689
2690#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Transitionable)]
2694#[serde(transparent)]
2695pub struct FontStretch(pub f32);
2696impl fmt::Debug for FontStretch {
2697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2698 let name = self.name();
2699 if name.is_empty() {
2700 f.debug_tuple("FontStretch").field(&self.0).finish()
2701 } else {
2702 if f.alternate() {
2703 write!(f, "FontStretch::")?;
2704 }
2705 write!(f, "{name}")
2706 }
2707 }
2708}
2709impl PartialOrd for FontStretch {
2710 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2711 Some(self.cmp(other))
2712 }
2713}
2714impl Ord for FontStretch {
2715 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2716 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
2717 }
2718}
2719impl PartialEq for FontStretch {
2720 fn eq(&self, other: &Self) -> bool {
2721 about_eq(self.0, other.0, EQ_GRANULARITY)
2722 }
2723}
2724impl Eq for FontStretch {}
2725impl std::hash::Hash for FontStretch {
2726 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2727 about_eq_hash(self.0, EQ_GRANULARITY, state)
2728 }
2729}
2730impl Default for FontStretch {
2731 fn default() -> FontStretch {
2732 FontStretch::NORMAL
2733 }
2734}
2735impl FontStretch {
2736 pub const ULTRA_CONDENSED: FontStretch = FontStretch(0.5);
2738 pub const EXTRA_CONDENSED: FontStretch = FontStretch(0.625);
2740 pub const CONDENSED: FontStretch = FontStretch(0.75);
2742 pub const SEMI_CONDENSED: FontStretch = FontStretch(0.875);
2744 pub const NORMAL: FontStretch = FontStretch(1.0);
2746 pub const SEMI_EXPANDED: FontStretch = FontStretch(1.125);
2748 pub const EXPANDED: FontStretch = FontStretch(1.25);
2750 pub const EXTRA_EXPANDED: FontStretch = FontStretch(1.5);
2752 pub const ULTRA_EXPANDED: FontStretch = FontStretch(2.0);
2754
2755 pub fn name(self) -> &'static str {
2757 macro_rules! name {
2758 ($($CONST:ident;)+) => {$(
2759 if self == Self::$CONST {
2760 return stringify!($CONST);
2761 }
2762 )+}
2763 }
2764 name! {
2765 ULTRA_CONDENSED;
2766 EXTRA_CONDENSED;
2767 CONDENSED;
2768 SEMI_CONDENSED;
2769 NORMAL;
2770 SEMI_EXPANDED;
2771 EXPANDED;
2772 EXTRA_EXPANDED;
2773 ULTRA_EXPANDED;
2774 }
2775 ""
2776 }
2777}
2778impl_from_and_into_var! {
2779 fn from(fct: Factor) -> FontStretch {
2780 FontStretch(fct.0)
2781 }
2782 fn from(pct: FactorPercent) -> FontStretch {
2783 FontStretch(pct.fct().0)
2784 }
2785 fn from(fct: f32) -> FontStretch {
2786 FontStretch(fct)
2787 }
2788}
2789impl From<ttf_parser::Width> for FontStretch {
2790 fn from(value: ttf_parser::Width) -> Self {
2791 use ttf_parser::Width::*;
2792 match value {
2793 UltraCondensed => FontStretch::ULTRA_CONDENSED,
2794 ExtraCondensed => FontStretch::EXTRA_CONDENSED,
2795 Condensed => FontStretch::CONDENSED,
2796 SemiCondensed => FontStretch::SEMI_CONDENSED,
2797 Normal => FontStretch::NORMAL,
2798 SemiExpanded => FontStretch::SEMI_EXPANDED,
2799 Expanded => FontStretch::EXPANDED,
2800 ExtraExpanded => FontStretch::EXTRA_EXPANDED,
2801 UltraExpanded => FontStretch::ULTRA_EXPANDED,
2802 }
2803 }
2804}
2805impl From<FontStretch> for ttf_parser::Width {
2806 fn from(value: FontStretch) -> Self {
2807 if value <= FontStretch::ULTRA_CONDENSED {
2808 ttf_parser::Width::UltraCondensed
2809 } else if value <= FontStretch::EXTRA_CONDENSED {
2810 ttf_parser::Width::ExtraCondensed
2811 } else if value <= FontStretch::CONDENSED {
2812 ttf_parser::Width::Condensed
2813 } else if value <= FontStretch::SEMI_CONDENSED {
2814 ttf_parser::Width::SemiCondensed
2815 } else if value <= FontStretch::NORMAL {
2816 ttf_parser::Width::Normal
2817 } else if value <= FontStretch::SEMI_EXPANDED {
2818 ttf_parser::Width::SemiExpanded
2819 } else if value <= FontStretch::EXPANDED {
2820 ttf_parser::Width::Expanded
2821 } else if value <= FontStretch::EXTRA_EXPANDED {
2822 ttf_parser::Width::ExtraExpanded
2823 } else {
2824 ttf_parser::Width::UltraExpanded
2825 }
2826 }
2827}
2828
2829#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
2831pub enum FontStyle {
2832 #[default]
2834 Normal,
2835 Italic,
2837 Oblique,
2839}
2840impl fmt::Debug for FontStyle {
2841 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2842 if f.alternate() {
2843 write!(f, "FontStyle::")?;
2844 }
2845 match self {
2846 Self::Normal => write!(f, "Normal"),
2847 Self::Italic => write!(f, "Italic"),
2848 Self::Oblique => write!(f, "Oblique"),
2849 }
2850 }
2851}
2852impl From<ttf_parser::Style> for FontStyle {
2853 fn from(value: ttf_parser::Style) -> Self {
2854 use ttf_parser::Style::*;
2855 match value {
2856 Normal => FontStyle::Normal,
2857 Italic => FontStyle::Italic,
2858 Oblique => FontStyle::Oblique,
2859 }
2860 }
2861}
2862
2863impl From<FontStyle> for ttf_parser::Style {
2864 fn from(value: FontStyle) -> Self {
2865 match value {
2866 FontStyle::Normal => Self::Normal,
2867 FontStyle::Italic => Self::Italic,
2868 FontStyle::Oblique => Self::Oblique,
2869 }
2870 }
2871}
2872
2873#[derive(Clone, Copy, Transitionable, serde::Serialize, serde::Deserialize)]
2876pub struct FontWeight(pub f32);
2877impl Default for FontWeight {
2878 fn default() -> FontWeight {
2879 FontWeight::NORMAL
2880 }
2881}
2882impl fmt::Debug for FontWeight {
2883 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2884 let name = self.name();
2885 if name.is_empty() {
2886 f.debug_tuple("FontWeight").field(&self.0).finish()
2887 } else {
2888 if f.alternate() {
2889 write!(f, "FontWeight::")?;
2890 }
2891 write!(f, "{name}")
2892 }
2893 }
2894}
2895impl PartialOrd for FontWeight {
2896 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2897 Some(self.cmp(other))
2898 }
2899}
2900impl Ord for FontWeight {
2901 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2902 about_eq_ord(self.0, other.0, EQ_GRANULARITY_100)
2903 }
2904}
2905impl PartialEq for FontWeight {
2906 fn eq(&self, other: &Self) -> bool {
2907 about_eq(self.0, other.0, EQ_GRANULARITY_100)
2908 }
2909}
2910impl Eq for FontWeight {}
2911impl std::hash::Hash for FontWeight {
2912 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2913 about_eq_hash(self.0, EQ_GRANULARITY_100, state)
2914 }
2915}
2916impl FontWeight {
2917 pub const THIN: FontWeight = FontWeight(100.0);
2919 pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
2921 pub const LIGHT: FontWeight = FontWeight(300.0);
2923 pub const NORMAL: FontWeight = FontWeight(400.0);
2925 pub const MEDIUM: FontWeight = FontWeight(500.0);
2927 pub const SEMIBOLD: FontWeight = FontWeight(600.0);
2929 pub const BOLD: FontWeight = FontWeight(700.0);
2931 pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
2933 pub const BLACK: FontWeight = FontWeight(900.0);
2935
2936 pub fn name(self) -> &'static str {
2938 macro_rules! name {
2939 ($($CONST:ident;)+) => {$(
2940 if self == Self::$CONST {
2941 return stringify!($CONST);
2942 }
2943 )+}
2944 }
2945 name! {
2946 THIN;
2947 EXTRA_LIGHT;
2948 LIGHT;
2949 NORMAL;
2950 MEDIUM;
2951 SEMIBOLD;
2952 BOLD;
2953 EXTRA_BOLD;
2954 BLACK;
2955 }
2956 ""
2957 }
2958}
2959impl_from_and_into_var! {
2960 fn from(weight: u32) -> FontWeight {
2961 FontWeight(weight as f32)
2962 }
2963 fn from(weight: f32) -> FontWeight {
2964 FontWeight(weight)
2965 }
2966}
2967impl From<ttf_parser::Weight> for FontWeight {
2968 fn from(value: ttf_parser::Weight) -> Self {
2969 use ttf_parser::Weight::*;
2970 match value {
2971 Thin => FontWeight::THIN,
2972 ExtraLight => FontWeight::EXTRA_LIGHT,
2973 Light => FontWeight::LIGHT,
2974 Normal => FontWeight::NORMAL,
2975 Medium => FontWeight::MEDIUM,
2976 SemiBold => FontWeight::SEMIBOLD,
2977 Bold => FontWeight::BOLD,
2978 ExtraBold => FontWeight::EXTRA_BOLD,
2979 Black => FontWeight::BLACK,
2980 Other(o) => FontWeight(o as f32),
2981 }
2982 }
2983}
2984impl From<FontWeight> for ttf_parser::Weight {
2985 fn from(value: FontWeight) -> Self {
2986 ttf_parser::Weight::from(value.0 as u16)
2987 }
2988}
2989
2990#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2992pub enum LineBreak {
2993 Auto,
2995 Loose,
2997 Normal,
2999 Strict,
3001 Anywhere,
3003}
3004impl Default for LineBreak {
3005 fn default() -> Self {
3007 LineBreak::Auto
3008 }
3009}
3010impl fmt::Debug for LineBreak {
3011 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3012 if f.alternate() {
3013 write!(f, "LineBreak::")?;
3014 }
3015 match self {
3016 LineBreak::Auto => write!(f, "Auto"),
3017 LineBreak::Loose => write!(f, "Loose"),
3018 LineBreak::Normal => write!(f, "Normal"),
3019 LineBreak::Strict => write!(f, "Strict"),
3020 LineBreak::Anywhere => write!(f, "Anywhere"),
3021 }
3022 }
3023}
3024
3025#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3030#[non_exhaustive]
3031pub enum ParagraphBreak {
3032 #[default]
3034 None,
3035 Line,
3037}
3038impl fmt::Debug for ParagraphBreak {
3039 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3040 if f.alternate() {
3041 write!(f, "ParagraphBreak::")?;
3042 }
3043 match self {
3044 ParagraphBreak::None => write!(f, "None"),
3045 ParagraphBreak::Line => write!(f, "Line"),
3046 }
3047 }
3048}
3049
3050#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3052pub enum Hyphens {
3053 None,
3055 Manual,
3060 Auto,
3062}
3063impl Default for Hyphens {
3064 fn default() -> Self {
3066 Hyphens::Auto
3067 }
3068}
3069impl fmt::Debug for Hyphens {
3070 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3071 if f.alternate() {
3072 write!(f, "Hyphens::")?;
3073 }
3074 match self {
3075 Hyphens::None => write!(f, "None"),
3076 Hyphens::Manual => write!(f, "Manual"),
3077 Hyphens::Auto => write!(f, "Auto"),
3078 }
3079 }
3080}
3081
3082#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3088pub enum WordBreak {
3089 Normal,
3091 BreakAll,
3093 KeepAll,
3095}
3096impl Default for WordBreak {
3097 fn default() -> Self {
3099 WordBreak::Normal
3100 }
3101}
3102impl fmt::Debug for WordBreak {
3103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3104 if f.alternate() {
3105 write!(f, "WordBreak::")?;
3106 }
3107 match self {
3108 WordBreak::Normal => write!(f, "Normal"),
3109 WordBreak::BreakAll => write!(f, "BreakAll"),
3110 WordBreak::KeepAll => write!(f, "KeepAll"),
3111 }
3112 }
3113}
3114
3115#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3117pub enum Justify {
3118 Auto,
3122 InterWord,
3124 InterLetter,
3126}
3127impl Default for Justify {
3128 fn default() -> Self {
3130 Justify::Auto
3131 }
3132}
3133impl Justify {
3134 pub fn resolve(self, lang: &Lang) -> Self {
3136 match self {
3137 Self::Auto => match lang.language.as_str() {
3138 "zh" | "ja" | "ko" => Self::InterLetter,
3139 _ => Self::InterWord,
3140 },
3141 m => m,
3142 }
3143 }
3144}
3145impl fmt::Debug for Justify {
3146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3147 if f.alternate() {
3148 write!(f, "Justify::")?;
3149 }
3150 match self {
3151 Justify::Auto => write!(f, "Auto"),
3152 Justify::InterWord => write!(f, "InterWord"),
3153 Justify::InterLetter => write!(f, "InterLetter"),
3154 }
3155 }
3156}
3157
3158#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3166#[non_exhaustive]
3167pub struct FontFaceMetrics {
3168 pub units_per_em: u32,
3172
3173 pub ascent: f32,
3175
3176 pub descent: f32,
3182
3183 pub line_gap: f32,
3185
3186 pub underline_position: f32,
3189
3190 pub underline_thickness: f32,
3192
3193 pub cap_height: f32,
3195
3196 pub x_height: f32,
3199
3200 pub bounds: euclid::Rect<f32, ()>,
3204}
3205impl FontFaceMetrics {
3206 pub fn sized(&self, font_size_px: Px) -> FontMetrics {
3208 let size_scale = 1.0 / self.units_per_em as f32 * font_size_px.0 as f32;
3209 let s = move |f: f32| Px((f * size_scale).round() as i32);
3210 FontMetrics {
3211 size_scale,
3212 ascent: s(self.ascent),
3213 descent: s(self.descent),
3214 line_gap: s(self.line_gap),
3215 underline_position: s(self.underline_position),
3216 underline_thickness: s(self.underline_thickness),
3217 cap_height: s(self.cap_height),
3218 x_height: (s(self.x_height)),
3219 bounds: {
3220 let b = self.bounds;
3221 PxRect::new(
3222 PxPoint::new(s(b.origin.x), s(b.origin.y)),
3223 PxSize::new(s(b.size.width), s(b.size.height)),
3224 )
3225 },
3226 }
3227 }
3228}
3229
3230#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3234#[non_exhaustive]
3235pub struct FontMetrics {
3236 pub size_scale: f32,
3238
3239 pub ascent: Px,
3241
3242 pub descent: Px,
3248
3249 pub line_gap: Px,
3251
3252 pub underline_position: Px,
3255
3256 pub underline_thickness: Px,
3258
3259 pub cap_height: Px,
3261
3262 pub x_height: Px,
3264
3265 pub bounds: PxRect,
3269}
3270impl FontMetrics {
3271 pub fn line_height(&self) -> Px {
3273 self.ascent - self.descent + self.line_gap
3274 }
3275}
3276
3277#[derive(Clone)]
3279pub enum TextTransformFn {
3280 None,
3282 Uppercase,
3284 Lowercase,
3286 Custom(Arc<dyn Fn(&Txt) -> Cow<Txt> + Send + Sync>),
3288}
3289impl TextTransformFn {
3290 pub fn transform<'t>(&self, text: &'t Txt) -> Cow<'t, Txt> {
3294 match self {
3295 TextTransformFn::None => Cow::Borrowed(text),
3296 TextTransformFn::Uppercase => {
3297 if text.chars().any(|c| !c.is_uppercase()) {
3298 Cow::Owned(text.to_uppercase().into())
3299 } else {
3300 Cow::Borrowed(text)
3301 }
3302 }
3303 TextTransformFn::Lowercase => {
3304 if text.chars().any(|c| !c.is_lowercase()) {
3305 Cow::Owned(text.to_lowercase().into())
3306 } else {
3307 Cow::Borrowed(text)
3308 }
3309 }
3310 TextTransformFn::Custom(fn_) => fn_(text),
3311 }
3312 }
3313
3314 pub fn custom(fn_: impl Fn(&Txt) -> Cow<Txt> + Send + Sync + 'static) -> Self {
3316 TextTransformFn::Custom(Arc::new(fn_))
3317 }
3318}
3319impl fmt::Debug for TextTransformFn {
3320 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3321 if f.alternate() {
3322 write!(f, "TextTransformFn::")?;
3323 }
3324 match self {
3325 TextTransformFn::None => write!(f, "None"),
3326 TextTransformFn::Uppercase => write!(f, "Uppercase"),
3327 TextTransformFn::Lowercase => write!(f, "Lowercase"),
3328 TextTransformFn::Custom(_) => write!(f, "Custom"),
3329 }
3330 }
3331}
3332impl PartialEq for TextTransformFn {
3333 fn eq(&self, other: &Self) -> bool {
3334 match (self, other) {
3335 (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
3336 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3337 }
3338 }
3339}
3340
3341#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3343pub enum WhiteSpace {
3344 #[default]
3346 Preserve,
3347 Merge,
3350 MergeParagraph,
3353 MergeAll,
3355}
3356impl WhiteSpace {
3357 pub fn transform(self, text: &Txt) -> Cow<'_, Txt> {
3361 match self {
3362 WhiteSpace::Preserve => Cow::Borrowed(text),
3363 WhiteSpace::Merge => {
3364 let mut prev_i = 0;
3366 for line in text.split_inclusive('\n') {
3367 let line_exclusive = line.trim_end_matches('\n').trim_end_matches('\r');
3369 let line_trim = line_exclusive.trim();
3370 let mut merge = line_trim.len() != line_exclusive.len() || line_trim.is_empty();
3371
3372 if !merge {
3374 let mut prev_is_space = true; for c in line.chars() {
3376 let is_space = c.is_whitespace();
3377 if prev_is_space && is_space {
3378 merge = true;
3379 break;
3380 }
3381 prev_is_space = is_space;
3382 }
3383 }
3384
3385 if !merge {
3386 prev_i += line.len();
3387 continue;
3388 }
3389
3390 let mut out = String::with_capacity(text.len() - 1);
3392 out.push_str(&text[..prev_i]);
3393
3394 let mut chars = text[prev_i..].chars();
3395 let mut prev_is_space = true;
3396 let mut prev_is_break = true;
3397 while let Some(c) = chars.next() {
3398 if c == '\r'
3399 && let Some(nc) = chars.next()
3400 {
3401 if nc == '\n' {
3402 if !prev_is_break && !out.is_empty() {
3403 out.push('\n');
3404 }
3405 prev_is_break = true;
3406 prev_is_space = true;
3407 } else {
3408 out.push(c);
3409 out.push(nc);
3410 prev_is_break = false;
3411 prev_is_space = nc.is_whitespace();
3412 }
3413 } else if c == '\n' {
3414 if !prev_is_break && !out.is_empty() {
3415 out.push('\n');
3416 }
3417 prev_is_break = true;
3418 prev_is_space = true;
3419 } else if c.is_whitespace() {
3420 if prev_is_space {
3421 continue;
3422 }
3423 out.push(' ');
3424 prev_is_space = true;
3425 } else {
3426 out.push(c);
3427 prev_is_space = false;
3428 prev_is_break = false;
3429 }
3430 }
3431
3432 if let Some((i, c)) = out.char_indices().rev().find(|(_, c)| !c.is_whitespace()) {
3434 out.truncate(i + c.len_utf8());
3435 }
3436
3437 return Cow::Owned(out.into());
3438 }
3439 Cow::Borrowed(text)
3440 }
3441 WhiteSpace::MergeParagraph => {
3442 let mut merge = text.contains('\n') || text.chars().last().unwrap_or('\0').is_whitespace();
3445 if !merge {
3446 let mut prev_is_space = true;
3447 for c in text.chars() {
3448 let is_space = c.is_whitespace();
3449 if prev_is_space && is_space {
3450 merge = true;
3451 break;
3452 }
3453 prev_is_space = is_space;
3454 }
3455 }
3456
3457 if merge {
3458 let mut out = String::with_capacity(text.len());
3459 let mut prev_is_break = false;
3460 for line in text.lines() {
3461 let line = line.trim();
3462 let is_break = line.is_empty();
3463 if !prev_is_break && is_break && !out.is_empty() {
3464 out.push('\n');
3465 }
3466 if !prev_is_break && !is_break && !out.is_empty() {
3467 out.push(' ');
3468 }
3469 prev_is_break = is_break;
3470
3471 let mut prev_is_space = false;
3472 for c in line.chars() {
3473 let is_space = c.is_whitespace();
3474 if is_space {
3475 if !prev_is_space {
3476 out.push(' ');
3477 }
3478 } else {
3479 out.push(c);
3480 }
3481 prev_is_space = is_space;
3482 }
3483 }
3484
3485 if let Some((i, c)) = out.char_indices().rev().find(|(_, c)| !c.is_whitespace()) {
3487 out.truncate(i + c.len_utf8());
3488 }
3489
3490 return Cow::Owned(out.into());
3491 }
3492 Cow::Borrowed(text)
3493 }
3494 WhiteSpace::MergeAll => {
3495 let mut prev_i = 0;
3497 let mut prev_is_space = true; for (i, c) in text.char_indices() {
3499 let is_space = c.is_whitespace();
3500 if prev_is_space && is_space || c == '\n' {
3501 if !prev_is_space {
3502 debug_assert_eq!(c, '\n');
3503 prev_i += c.len_utf8();
3504 prev_is_space = true;
3505 }
3506 let mut out = String::with_capacity(text.len() - 1);
3508 out.push_str(&text[..prev_i]);
3510 if !out.is_empty() {
3511 out.push(' ');
3512 }
3513 for c in text[(i + c.len_utf8())..].chars() {
3515 let is_space = c.is_whitespace();
3516 if prev_is_space && is_space {
3517 continue;
3518 }
3519 out.push(if is_space { ' ' } else { c });
3520 prev_is_space = is_space;
3521 }
3522
3523 if let Some((i, c)) = out.char_indices().rev().find(|(_, c)| !c.is_whitespace()) {
3525 out.truncate(i + c.len_utf8());
3526 }
3527
3528 return Cow::Owned(out.into());
3529 }
3530 prev_i = i;
3531 prev_is_space = is_space;
3532 }
3533
3534 let out = text.trim_end();
3538 if out.len() != text.len() {
3539 return Cow::Owned(Txt::from_str(out));
3540 }
3541
3542 Cow::Borrowed(text)
3543 }
3544 }
3545 }
3546}
3547impl fmt::Debug for WhiteSpace {
3548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3549 if f.alternate() {
3550 write!(f, "WhiteSpace::")?;
3551 }
3552 match self {
3553 WhiteSpace::Preserve => write!(f, "Preserve"),
3554 WhiteSpace::Merge => write!(f, "Merge"),
3555 WhiteSpace::MergeAll => write!(f, "MergeAll"),
3556 WhiteSpace::MergeParagraph => write!(f, "MergeParagraph"),
3557 }
3558 }
3559}
3560
3561#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
3563pub struct CaretIndex {
3564 pub index: usize,
3568 pub line: usize,
3578}
3579
3580impl PartialEq for CaretIndex {
3581 fn eq(&self, other: &Self) -> bool {
3582 self.index == other.index
3583 }
3584}
3585impl Eq for CaretIndex {}
3586impl CaretIndex {
3587 pub const ZERO: CaretIndex = CaretIndex { index: 0, line: 0 };
3589}
3590impl PartialOrd for CaretIndex {
3591 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3592 Some(self.cmp(other))
3593 }
3594}
3595impl Ord for CaretIndex {
3596 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3597 self.index.cmp(&other.index)
3598 }
3599}
3600
3601#[derive(Debug, Clone)]
3603#[non_exhaustive]
3604pub enum FontLoadingError {
3605 UnknownFormat,
3607 NoSuchFontInCollection,
3612 Parse(ttf_parser::FaceParsingError),
3614 NoFilesystem,
3617 Io(Arc<std::io::Error>),
3619}
3620impl PartialEq for FontLoadingError {
3621 fn eq(&self, other: &Self) -> bool {
3622 match (self, other) {
3623 (Self::Io(l0), Self::Io(r0)) => Arc::ptr_eq(l0, r0),
3624 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3625 }
3626 }
3627}
3628impl From<std::io::Error> for FontLoadingError {
3629 fn from(error: std::io::Error) -> FontLoadingError {
3630 Self::Io(Arc::new(error))
3631 }
3632}
3633impl fmt::Display for FontLoadingError {
3634 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3635 match self {
3636 Self::UnknownFormat => write!(f, "unknown format"),
3637 Self::NoSuchFontInCollection => write!(f, "no such font in the collection"),
3638 Self::NoFilesystem => write!(f, "no filesystem present"),
3639 Self::Parse(e) => fmt::Display::fmt(e, f),
3640 Self::Io(e) => fmt::Display::fmt(e, f),
3641 }
3642 }
3643}
3644impl std::error::Error for FontLoadingError {
3645 fn cause(&self) -> Option<&dyn std::error::Error> {
3646 match self {
3647 FontLoadingError::Parse(e) => Some(e),
3648 FontLoadingError::Io(e) => Some(e),
3649 _ => None,
3650 }
3651 }
3652}
3653
3654#[cfg(test)]
3655mod tests {
3656 use zng_app::APP;
3657
3658 use super::*;
3659
3660 #[test]
3661 fn generic_fonts_default() {
3662 let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
3663
3664 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(und)))
3665 }
3666
3667 #[test]
3668 fn generic_fonts_fallback() {
3669 let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
3670
3671 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(en_US)));
3672 assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(es)));
3673 }
3674
3675 #[test]
3676 fn generic_fonts_get1() {
3677 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3678 GenericFonts {}.set_sans_serif(lang!(en_US), "Test Value");
3679 app.update(false).assert_wait();
3680
3681 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3682 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3683 }
3684
3685 #[test]
3686 fn generic_fonts_get2() {
3687 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3688 GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3689 app.update(false).assert_wait();
3690
3691 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3692 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3693 }
3694
3695 #[test]
3696 fn generic_fonts_get_best() {
3697 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3698 GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3699 GenericFonts {}.set_sans_serif(lang!(en_US), "Best");
3700 app.update(false).assert_wait();
3701
3702 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Best");
3703 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3704 assert_eq!(&GenericFonts {}.sans_serif(&lang!("und")), "sans-serif");
3705 }
3706
3707 #[test]
3708 fn generic_fonts_get_no_lang_match() {
3709 let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3710 GenericFonts {}.set_sans_serif(lang!(es_US), "Test Value");
3711 app.update(false).assert_wait();
3712
3713 assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "sans-serif");
3714 assert_eq!(&GenericFonts {}.sans_serif(&lang!("es")), "Test Value");
3715 }
3716
3717 #[test]
3718 fn white_space_merge() {
3719 macro_rules! test {
3720 ($input:tt, $output:tt) => {
3721 let input = Txt::from($input);
3722 let output = WhiteSpace::Merge.transform(&input);
3723 assert_eq!($output, output.as_str());
3724
3725 let input = input.replace('\n', "\r\n");
3726 let output = WhiteSpace::Merge.transform(&Txt::from(input)).replace("\r\n", "\n");
3727 assert_eq!($output, output.as_str());
3728 };
3729 }
3730 test!("a b\n\nc", "a b\nc");
3731 test!("a b\nc", "a b\nc");
3732 test!(" a b\nc\n \n", "a b\nc");
3733 test!(" \n a b\nc", "a b\nc");
3734 test!("a\n \nb", "a\nb");
3735 }
3736
3737 #[test]
3738 fn white_space_merge_paragraph() {
3739 macro_rules! test {
3740 ($input:tt, $output:tt) => {
3741 let input = Txt::from($input);
3742 let output = WhiteSpace::MergeParagraph.transform(&input);
3743 assert_eq!($output, output.as_str());
3744
3745 let input = input.replace('\n', "\r\n");
3746 let output = WhiteSpace::MergeParagraph.transform(&Txt::from(input)).replace("\r\n", "\n");
3747 assert_eq!($output, output.as_str());
3748 };
3749 }
3750 test!("a b\n\nc", "a b\nc");
3751 test!("a b\nc", "a b c");
3752 test!(" a b\nc\n \n", "a b c");
3753 test!(" \n a b\nc", "a b c");
3754 test!("a\n \nb", "a\nb");
3755 }
3756
3757 #[test]
3758 fn white_space_merge_all() {
3759 macro_rules! test {
3760 ($input:tt, $output:tt) => {
3761 let input = Txt::from($input);
3762 let output = WhiteSpace::MergeAll.transform(&input);
3763 assert_eq!($output, output.as_str());
3764 };
3765 }
3766 test!("a b\n\nc", "a b c");
3767 test!("a b\nc", "a b c");
3768 test!(" a b\nc\n \n", "a b c");
3769 test!(" \n a b\nc", "a b c");
3770 test!("a\n \nb", "a b");
3771 }
3772}