zng_ext_font/
lib.rs

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//!
4//! Font loading, text segmenting and shaping.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9// suppress nag about very simple boxed closure signatures.
10#![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/// Font family name.
76///
77/// A possible value for the `font_family` property.
78///
79/// # Case Insensitive
80///
81/// Font family names are case-insensitive. `"Arial"` and `"ARIAL"` are equal and have the same hash.
82#[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            // case insensitive eq
119            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    /// New font name from `&'static str`.
139    pub const fn from_static(name: &'static str) -> Self {
140        FontName {
141            txt: Txt::from_static(name),
142            is_ascii: {
143                // str::is_ascii is not const
144                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    /// New font name.
160    ///
161    /// Note that the inner name value is a [`Txt`] so you can define a font name using `&'static str` or `String`.
162    ///
163    /// Font names are case insensitive but the input casing is preserved, this casing shows during display and in
164    /// the value of [`name`](Self::name).
165    ///
166    /// [`Txt`]: zng_txt::Txt
167    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    /// New "serif" font name.
176    ///
177    /// Serif fonts represent the formal text style for a script.
178    pub fn serif() -> Self {
179        Self::new("serif")
180    }
181
182    /// New "sans-serif" font name.
183    ///
184    /// Glyphs in sans-serif fonts, are generally low contrast (vertical and horizontal stems have close to the same thickness)
185    /// and have stroke endings that are plain — without any flaring, cross stroke, or other ornamentation.
186    pub fn sans_serif() -> Self {
187        Self::new("sans-serif")
188    }
189
190    /// New "monospace" font name.
191    ///
192    /// The sole criterion of a monospace font is that all glyphs have the same fixed width.
193    pub fn monospace() -> Self {
194        Self::new("monospace")
195    }
196
197    /// New "cursive" font name.
198    ///
199    /// Glyphs in cursive fonts generally use a more informal script style, and the result looks more
200    /// like handwritten pen or brush writing than printed letter-work.
201    pub fn cursive() -> Self {
202        Self::new("cursive")
203    }
204
205    /// New "fantasy" font name.
206    ///
207    /// Fantasy fonts are primarily decorative or expressive fonts that contain decorative or expressive representations of characters.
208    pub fn fantasy() -> Self {
209        Self::new("fantasy")
210    }
211
212    /// Reference the font name string.
213    pub fn name(&self) -> &str {
214        &self.txt
215    }
216
217    /// Unwraps into a [`Txt`].
218    ///
219    /// [`Txt`]: zng_txt::Txt
220    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/// A list of [font names](FontName) in priority order.
273///
274/// # Examples
275///
276/// This type is usually initialized using conversion:
277///
278/// ```
279/// # use zng_ext_font::*;
280/// fn foo(font_names: impl Into<FontNames>) {}
281///
282/// foo(["Arial", "sans-serif", "monospace"]);
283/// ```
284///
285/// You can also use the specialized [`push`](Self::push) that converts:
286///
287/// ```
288/// # use zng_ext_font::*;
289/// let user_preference = "Comic Sans".to_owned();
290///
291/// let mut names = FontNames::empty();
292/// names.push(user_preference);
293/// names.push("Arial");
294/// names.extend(FontNames::default());
295/// ```
296///
297/// # Default
298///
299/// The default value is the [`system_ui`](FontNames::system_ui) for the undefined language (`und`).
300#[derive(Eq, PartialEq, Hash, Clone, serde::Serialize, serde::Deserialize)]
301#[serde(transparent)]
302pub struct FontNames(pub Vec<FontName>);
303impl FontNames {
304    /// Empty list.
305    pub fn empty() -> Self {
306        FontNames(vec![])
307    }
308
309    /// Returns the default UI font names for Windows.
310    pub fn windows_ui(lang: &Lang) -> Self {
311        // source: VSCode
312        // https://github.com/microsoft/vscode/blob/6825c886700ac11d07f7646d8d8119c9cdd9d288/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css
313
314        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    /// Returns the default UI font names for MacOS/iOS.
328    pub fn mac_ui(lang: &Lang) -> Self {
329        // source: VSCode
330
331        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    /// Returns the default UI font names for Linux.
352    pub fn linux_ui(lang: &Lang) -> Self {
353        // source: VSCode
354
355        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    /// Returns the default UI font names for the current operating system.
409    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    /// Push a font name from any type that converts to [`FontName`].
422    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    /// Change in [`FONTS`] that may cause a font query to now give
560    /// a different result.
561    ///
562    /// # Cache
563    ///
564    /// Every time this event updates the font cache is cleared. Meaning that even
565    /// if the query returns the same font it will be a new reference.
566    ///
567    /// Fonts only unload when all references to then are dropped, so you can still continue using
568    /// old references if you don't want to monitor this event.
569    pub static FONT_CHANGED_EVENT: FontChangedArgs;
570}
571
572event_args! {
573    /// [`FONT_CHANGED_EVENT`] arguments.
574    pub struct FontChangedArgs {
575        /// The change that happened.
576        pub change: FontChange,
577
578        ..
579
580        /// Broadcast to all widgets.
581        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
582            list.search_all()
583        }
584    }
585}
586
587/// Possible changes in a [`FontChangedArgs`].
588#[derive(Clone, Debug)]
589pub enum FontChange {
590    /// OS fonts change.
591    ///
592    /// Currently this is only supported in Microsoft Windows.
593    SystemFonts,
594
595    /// Custom fonts change caused by call to [`FONTS.register`] or [`FONTS.unregister`].
596    ///
597    /// [`FONTS.register`]: FONTS::register
598    /// [`FONTS.unregister`]: FONTS::unregister
599    CustomFonts,
600
601    /// Custom request caused by call to [`FONTS.refresh`].
602    ///
603    /// [`FONTS.refresh`]: FONTS::refresh
604    Refresh,
605
606    /// One of the [`GenericFonts`] was set for the language.
607    ///
608    /// The font name is one of [`FontName`] generic names.
609    ///
610    /// [`GenericFonts`]: struct@GenericFonts
611    GenericFont(FontName, Lang),
612
613    /// A new [fallback](GenericFonts::fallback) font was set for the language.
614    Fallback(Lang),
615}
616
617/// Application extension that manages text fonts.
618///
619/// Services this extension provides:
620///
621/// * [`FONTS`] - Service that finds and loads fonts.
622/// * [`HYPHENATION`] - Service that loads and applies hyphenation dictionaries.
623///
624/// Events this extension provides:
625///
626/// * [`FONT_CHANGED_EVENT`] - Font config or system fonts changed.
627#[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                // cut circular reference so that when the last font ref gets dropped
660                // this font face also gets dropped. Also tag the font as unregistered
661                // so it does not create further circular references.
662                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
709/// Font loading, custom fonts and app font configuration.
710pub struct FONTS;
711impl FONTS {
712    /// Clear cache and notify `Refresh` in [`FONT_CHANGED_EVENT`].
713    ///
714    /// See the event documentation for more information.
715    pub fn refresh(&self) {
716        FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Refresh));
717    }
718
719    /// Remove all unused fonts from cache.
720    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    /// Actual name of generic fonts.
729    pub fn generics(&self) -> &'static GenericFonts {
730        &GenericFonts {}
731    }
732
733    /// Load and register a custom font.
734    ///
735    /// If the font loads correctly a [`FONT_CHANGED_EVENT`] notification is scheduled.
736    /// Fonts sourced from a file are not monitored for changes, you can *reload* the font
737    /// by calling `register` again with the same font name.
738    ///
739    /// The returned response will update once when the font finishes loading with the new font.
740    /// At minimum the new font will be available on the next update.
741    pub fn register(&self, custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
742        FontFaceLoader::register(custom_font)
743    }
744
745    /// Removes a custom font family. If the font faces are not in use it is also unloaded.
746    ///
747    /// Returns a response var that updates once with a value that indicates if any custom font was removed.
748    pub fn unregister(&self, custom_family: FontName) -> ResponseVar<bool> {
749        FONTS_SV.write().loader.unregister(custom_family)
750    }
751
752    /// Gets a font list that best matches the query.
753    pub fn list(
754        &self,
755        families: &[FontName],
756        style: FontStyle,
757        weight: FontWeight,
758        stretch: FontStretch,
759        lang: &Lang,
760    ) -> ResponseVar<FontFaceList> {
761        // try with shared lock
762        if let Some(cached) = FONTS_SV.read().loader.try_list(families, style, weight, stretch, lang) {
763            return cached;
764        }
765        // begin load with exclusive lock (cache is tried again in `load`)
766        FONTS_SV.write().loader.load_list(families, style, weight, stretch, lang)
767    }
768
769    /// Find a single font face that best matches the query.
770    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        // try with shared lock
779        if let Some(cached) = FONTS_SV.read().loader.try_cached(family, style, weight, stretch, lang) {
780            return cached;
781        }
782        // begin load with exclusive lock (cache is tried again in `load`)
783        FONTS_SV.write().loader.load(family, style, weight, stretch, lang)
784    }
785
786    /// Find a single font face with all normal properties.
787    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    /// Find a single font face with italic style, normal weight and stretch.
792    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    /// Find a single font face with bold weight, normal style and stretch.
797    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    /// Gets all [registered](Self::register) font families.
802    pub fn custom_fonts(&self) -> Vec<FontName> {
803        FONTS_SV.read().loader.custom_fonts.keys().cloned().collect()
804    }
805
806    /// Query all font families available in the system.
807    ///
808    /// Note that the variable will only update once with the query result, this is not a live view.
809    pub fn system_fonts(&self) -> ResponseVar<Vec<FontName>> {
810        query_util::system_all()
811    }
812
813    /// Gets the system font anti-aliasing config as a read-only var.
814    ///
815    /// The variable updates when the system config changes.
816    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    /// Returns the key.
849    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/// A font face selected from a font family.
856///
857/// Usually this is part of a [`FontList`] that can be requested from
858/// the [`FONTS`] service.
859///
860/// This type is a shared reference to the font data, cloning it is cheap.
861#[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    /// New empty font face.
918    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            // values copied from a monospace font
930            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                // `xMin`/`xMax`/`yMin`/`yMax`
940                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    /// Is empty font face.
952    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                    // try again with font 0 (font-kit selects a high index for Ubuntu Font)
1003                    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        // all tables used by `ttf_parser::Face::glyph_raster_image`
1022        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                    // try again with font 0 (font-kit selects a high index for Ubuntu Font)
1061                    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            // observed this in Noto Color Emoji (with font_kit)
1113            tracing::debug!("font {display_name:?} units_per_em 0");
1114            return Err(FontLoadingError::UnknownFormat);
1115        }
1116
1117        // all tables used by `ttf_parser::Face::glyph_raster_image`
1118        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    /// Loads the harfbuzz face.
1185    ///
1186    /// Loads from in memory [`bytes`].
1187    ///
1188    /// Returns `None` if [`is_empty`].
1189    ///
1190    /// [`is_empty`]: Self::is_empty
1191    /// [`bytes`]: Self::bytes
1192    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    /// Loads the full TTF face.
1201    ///
1202    /// Loads from in memory [`bytes`].
1203    ///
1204    /// Returns `None` if [`is_empty`].
1205    ///
1206    /// [`is_empty`]: Self::is_empty
1207    /// [`bytes`]: Self::bytes
1208    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    /// Reference the font file bytes.
1217    pub fn bytes(&self) -> &FontBytes {
1218        &self.0.data
1219    }
1220    /// Index of the font face in the [font file](Self::bytes).
1221    pub fn index(&self) -> u32 {
1222        self.0.face_index
1223    }
1224
1225    /// Font full name.
1226    pub fn display_name(&self) -> &FontName {
1227        &self.0.display_name
1228    }
1229
1230    /// Font family name.
1231    pub fn family_name(&self) -> &FontName {
1232        &self.0.family_name
1233    }
1234
1235    /// Font globally unique name.
1236    pub fn postscript_name(&self) -> Option<&str> {
1237        self.0.postscript_name.as_deref()
1238    }
1239
1240    /// Font style.
1241    pub fn style(&self) -> FontStyle {
1242        self.0.style
1243    }
1244
1245    /// Font weight.
1246    pub fn weight(&self) -> FontWeight {
1247        self.0.weight
1248    }
1249
1250    /// Font stretch.
1251    pub fn stretch(&self) -> FontStretch {
1252        self.0.stretch
1253    }
1254
1255    /// Font is monospace (fixed-width).
1256    pub fn is_monospace(&self) -> bool {
1257        self.0.flags.contains(FontFaceFlags::IS_MONOSPACE)
1258    }
1259
1260    /// Font metrics in font units.
1261    pub fn metrics(&self) -> &FontFaceMetrics {
1262        &self.0.metrics
1263    }
1264
1265    /// Gets a cached sized [`Font`].
1266    ///
1267    /// The `font_size` is the size of `1 font EM` in pixels.
1268    ///
1269    /// The `variations` are custom [font variations] that will be used
1270    /// during shaping and rendering.
1271    ///
1272    /// [font variations]: crate::font_features::FontVariations::finalize
1273    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    /// Gets what font synthesis to use to better render this font face given the style and weight.
1288    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            // if requested oblique or italic and the face is neither.
1293            synth |= FontSynthesis::OBLIQUE;
1294        }
1295        if weight > self.weight() {
1296            // if requested a weight larger then the face weight the renderer can
1297            // add extra stroke outlines to compensate.
1298            synth |= FontSynthesis::BOLD;
1299        }
1300
1301        synth
1302    }
1303
1304    /// If this font face is cached. All font faces are cached by default, a font face can be detached from
1305    /// cache when a [`FONT_CHANGED_EVENT`] event happens, in this case the font can still be used normally, but
1306    /// a request for the same font name will return a different reference.
1307    pub fn is_cached(&self) -> bool {
1308        !self.0.m.lock().unregistered
1309    }
1310
1311    /// CPAL table.
1312    ///
1313    /// Is empty if not provided by the font.
1314    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    /// COLR table.
1322    ///
1323    /// Is empty if not provided by the font.
1324    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    /// If the font provides glyph substitutions.
1332    pub fn has_ligatures(&self) -> bool {
1333        self.0.flags.contains(FontFaceFlags::HAS_LIGATURES)
1334    }
1335
1336    /// If this font provides custom positioned carets for some or all ligature glyphs.
1337    ///
1338    /// If `true` the [`Font::ligature_caret_offsets`] method can be used to get the caret offsets, otherwise
1339    /// it always returns empty.
1340    pub fn has_ligature_caret_offsets(&self) -> bool {
1341        !self.0.lig_carets.is_empty()
1342    }
1343
1344    /// If this font has bitmap images associated with some glyphs.
1345    pub fn has_raster_images(&self) -> bool {
1346        self.0.flags.contains(FontFaceFlags::HAS_RASTER_IMAGES)
1347    }
1348
1349    /// If this font has SVG images associated with some glyphs.
1350    pub fn has_svg_images(&self) -> bool {
1351        self.0.flags.contains(FontFaceFlags::HAS_SVG_IMAGES)
1352    }
1353}
1354
1355/// A sized font face.
1356///
1357/// A sized font can be requested from a [`FontFace`].
1358///
1359/// This type is a shared reference to the loaded font data, cloning it is cheap.
1360#[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    /// Reference the font face source of this font.
1445    pub fn face(&self) -> &FontFace {
1446        &self.0.face
1447    }
1448
1449    /// Gets the sized harfbuzz font.
1450    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    /// Font size.
1462    ///
1463    /// This is also the *pixels-per-em* value.
1464    pub fn size(&self) -> Px {
1465        self.0.size
1466    }
1467
1468    /// Custom font variations.
1469    pub fn variations(&self) -> &RFontVariations {
1470        &self.0.variations
1471    }
1472
1473    /// Sized font metrics.
1474    pub fn metrics(&self) -> &FontMetrics {
1475        &self.0.metrics
1476    }
1477
1478    /// Iterate over pixel offsets relative to `lig` glyph start that represents the
1479    /// caret offset for each cluster that is covered by the ligature, after the first.
1480    ///
1481    /// The caret offset for the first cluster is the glyph offset and is not yielded in the iterator. The
1482    /// yielded offsets are relative to the glyph position.
1483    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/// A list of [`FontFace`] resolved from a [`FontName`] list, plus the [fallback](GenericFonts::fallback) font.
1548///
1549/// Glyphs that are not resolved by the first font fallback to the second font and so on.
1550#[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    /// New list with only the [`FontFace::empty`].
1559    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    /// Style requested in the query that generated this font face list.
1569    pub fn requested_style(&self) -> FontStyle {
1570        self.requested_style
1571    }
1572
1573    /// Weight requested in the query that generated this font face list.
1574    pub fn requested_weight(&self) -> FontWeight {
1575        self.requested_weight
1576    }
1577
1578    /// Stretch requested in the query that generated this font face list.
1579    pub fn requested_stretch(&self) -> FontStretch {
1580        self.requested_stretch
1581    }
1582
1583    /// The font face that best matches the requested properties.
1584    pub fn best(&self) -> &FontFace {
1585        &self.fonts[0]
1586    }
1587
1588    /// Gets the font synthesis to use to better render the given font face on the list.
1589    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    /// Iterate over font faces, more specific first.
1598    pub fn iter(&self) -> std::slice::Iter<'_, FontFace> {
1599        self.fonts.iter()
1600    }
1601
1602    /// Number of font faces in the list.
1603    ///
1604    /// This is at least `1`, but can be the empty face.
1605    pub fn len(&self) -> usize {
1606        self.fonts.len()
1607    }
1608
1609    /// Is length `1` and only contains the empty face.
1610    pub fn is_empty(&self) -> bool {
1611        self.fonts[0].is_empty() && self.fonts.len() == 1
1612    }
1613
1614    /// Gets a sized font list.
1615    ///
1616    /// This calls [`FontFace::sized`] for each font in the list.
1617    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    /// Both are equal if each point to the same fonts in the same order and have the same requested properties.
1628    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/// A list of [`Font`] created from a [`FontFaceList`].
1662#[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)] // cannot be empty.
1670impl FontList {
1671    /// The font that best matches the requested properties.
1672    pub fn best(&self) -> &Font {
1673        &self.fonts[0]
1674    }
1675
1676    /// Font size requested in the query that generated this font list.
1677    pub fn requested_size(&self) -> Px {
1678        self.fonts[0].size()
1679    }
1680
1681    /// Style requested in the query that generated this font list.
1682    pub fn requested_style(&self) -> FontStyle {
1683        self.requested_style
1684    }
1685
1686    /// Weight requested in the query that generated this font list.
1687    pub fn requested_weight(&self) -> FontWeight {
1688        self.requested_weight
1689    }
1690
1691    /// Stretch requested in the query that generated this font list.
1692    pub fn requested_stretch(&self) -> FontStretch {
1693        self.requested_stretch
1694    }
1695
1696    /// Gets the font synthesis to use to better render the given font on the list.
1697    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    /// Iterate over font faces, more specific first.
1706    pub fn iter(&self) -> std::slice::Iter<'_, Font> {
1707        self.fonts.iter()
1708    }
1709
1710    /// Number of font faces in the list.
1711    ///
1712    /// This is at least `1`.
1713    pub fn len(&self) -> usize {
1714        self.fonts.len()
1715    }
1716
1717    /// Returns `true` is `self` is sized from the `faces` list.
1718    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    /// Both are equal if each point to the same fonts in the same order and have the same requested properties.
1734    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, // face shared
1822                            Some(None) => false,                                // loading for no one
1823                            None => true,                                       // retain not found
1824                        }
1825                    })
1826                } else {
1827                    // response var shared
1828                    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        // start loading
1839        let resp = task::respond(FontFace::load_custom(custom_font));
1840
1841        // modify loader.custom_fonts at the end of whatever update is happening when finishes loading.
1842        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    /// Try cached again, otherwise begins loading and inserts the response in the cache.
1990    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    /// Get a `font_name` that already resolved generic names if it is already in cache.
2004    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    /// Load a `font_name` that already resolved generic names.
2028    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            // it is common for custom font names to only have one face.
2098            return faces[0].clone();
2099        }
2100
2101        let mut set = Vec::with_capacity(faces.len());
2102        let mut set_dist = 0.0f64; // stretch distance of current set if it is not empty.
2103
2104        // # Filter Stretch
2105        //
2106        // Closest to query stretch, if the query is narrow, closest narrow then
2107        // closest wide, if the query is wide the reverse.
2108        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                // better candidate found, restart closest set.
2124                set_dist = dist;
2125                set.clear();
2126                set.push(face);
2127            } else if (dist - set_dist).abs() < 0.0001 {
2128                // another candidate, same distance.
2129                set.push(face);
2130            }
2131        }
2132        if set.len() == 1 {
2133            return set[0].clone();
2134        }
2135
2136        // # Filter Style
2137        //
2138        // Each query style has a fallback preference, we retain the faces that have the best
2139        // style given the query preference.
2140        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        // # Filter Weight
2158        //
2159        // a: under 400 query matches query then descending under query then ascending over query.
2160        // b: over 500 query matches query then ascending over query then descending under query.
2161        //
2162        // c: in 400..=500 query matches query then ascending to 500 then descending under query
2163        //     then ascending over 500.
2164        let add_penalty = if weight.0 >= 400.0 && weight.0 <= 500.0 {
2165            // c:
2166            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2167                // Add penalty for:
2168                if face.weight() < weight {
2169                    // Not being in search up to 500
2170                    *dist += 100.0;
2171                } else if face.weight().0 > 500.0 {
2172                    // Not being in search down to 0
2173                    *dist += 600.0;
2174                }
2175            }
2176        } else if weight.0 < 400.0 {
2177            // a:
2178            |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            // b:
2186            |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        // error here means the entire renderer was already dropped.
2226        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        // error here means the entire renderer was already dropped.
2247        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/// Generic fonts configuration for the app.
2301///
2302/// This type can be accessed from the [`FONTS`] service.
2303///
2304/// # Defaults
2305///
2306/// By default the `serif`, `sans_serif`, `monospace`, `cursive` and `fantasy` are set to their own generic name,
2307/// this delegates the resolution to the operating system.
2308///
2309/// The default `fallback` font is "Segoe UI Symbol" for Windows, "Standard Symbols PS" for Linux and "sans-serif" for others.
2310///
2311/// See also [`FontNames::system_ui`] for the default font selection for UIs.
2312///
2313/// [`FontNames::system_ui`]: crate::FontNames::system_ui
2314#[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    ///
2320    /// Returns a font name for the best `lang` match.
2321    ///
2322    #[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    ///
2330    /// The change applied for the next update.
2331    ///
2332    /// Use `lang!(und)` to set name used when no language matches.
2333    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] // for zng fmt
2348    impl_fallback_accessors! {
2349        serif="serif", sans_serif="sans-serif", monospace="monospace", cursive="cursive", fantasy="fantasy"
2350    }
2351
2352    /// Gets the ultimate fallback font used when none of the other fonts support a glyph.
2353    ///
2354    /// Returns a font name.
2355    pub fn fallback(&self, lang: &Lang) -> FontName {
2356        GENERIC_FONTS_SV.read().fallback.get(lang).unwrap().clone()
2357    }
2358
2359    /// Sets the ultimate fallback font used when none of other fonts support a glyph.
2360    ///
2361    /// The change applies for the next update.
2362    ///
2363    /// Use `lang!(und)` to set name used when no language matches.
2364    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    /// Returns the font name registered for the generic `name` and `lang`.
2377    ///
2378    /// Returns `None` if `name` if not one of the generic font names.
2379    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    /// IpcBytes already clones references, but we need the weak_count for caching
2432    Ipc(IpcBytes),
2433    Arc(Arc<Vec<u8>>),
2434    Static(&'static [u8]),
2435    System(Arc<SystemFontBytes>),
2436}
2437/// Reference to in memory font data.
2438#[derive(Clone)]
2439pub struct FontBytes(FontBytesImpl);
2440impl FontBytes {
2441    /// From shared memory that can be efficiently referenced in the view-process for rendering.
2442    pub fn from_ipc(bytes: IpcBytes) -> Self {
2443        Self(FontBytesImpl::Ipc(bytes))
2444    }
2445
2446    /// Moves data to an [`IpcBytes`] shared reference.
2447    pub fn from_vec(bytes: Vec<u8>) -> io::Result<Self> {
2448        Ok(Self(FontBytesImpl::Ipc(IpcBytes::from_vec_blocking(bytes)?)))
2449    }
2450
2451    /// Uses the reference in the app-process. In case the font needs to be send to view-process turns into [`IpcBytes`].
2452    pub fn from_static(bytes: &'static [u8]) -> Self {
2453        Self(FontBytesImpl::Static(bytes))
2454    }
2455
2456    /// Uses the reference in the app-process. In case the font needs to be send to view-process turns into [`IpcBytes`].
2457    ///
2458    /// Prefer `from_ipc` if you can control the data creation.
2459    pub fn from_arc(bytes: Arc<Vec<u8>>) -> Self {
2460        Self(FontBytesImpl::Arc(bytes))
2461    }
2462
2463    /// If the `path` is in the restricted system fonts directory memory maps it. Otherwise reads into [`IpcBytes`].
2464    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            // SAFETY: Buffer allocated to max possible
2472            let len = unsafe { GetSystemWindowsDirectoryW(Some(&mut buffer)) };
2473            let fonts_dir = String::from_utf16_lossy(&buffer[..len as usize]);
2474            // usually this is: r"C:\Windows\Fonts"
2475            if path.starts_with(fonts_dir) {
2476                // SAFETY: Windows restricts write access to files in this directory.
2477                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            // SAFETY: macOS restricts write access to files in this directory.
2483            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            // SAFETY: Android restricts write access to files in this directory.
2488            return unsafe { load_from_system(path) };
2489        }
2490        #[cfg(unix)]
2491        if path.starts_with("/usr/share/fonts/") {
2492            // SAFETY: OS restricts write access to files in this directory.
2493            return unsafe { load_from_system(path) };
2494        }
2495
2496        #[cfg(ipc)]
2497        unsafe fn load_from_system(path: PathBuf) -> io::Result<FontBytes> {
2498            // SAFETY: up to the caller
2499            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    /// Read lock the `path` and memory maps it.
2513    ///
2514    /// # Safety
2515    ///
2516    /// You must ensure the file content does not change. If the file has the same access restrictions as the
2517    /// current executable file you can say it is safe.
2518    #[cfg(ipc)]
2519    pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2520        // SAFETY: up to the caller
2521        let ipc = unsafe { IpcBytes::open_memmap_blocking(path, None) }?;
2522        Ok(Self(FontBytesImpl::Ipc(ipc)))
2523    }
2524
2525    /// File path, if the bytes are memory mapped.
2526    ///
2527    /// Note that the path is read-locked until all clones of `FontBytes` are dropped.
2528    #[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    /// Clone [`IpcBytes`] reference or clone data into a new one.
2538    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    /// Clone [`IpcBytes`] reference or clone data into a new one.
2547    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/// Custom font builder.
2607#[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    /// A custom font loaded from a file.
2617    ///
2618    /// If the file is a collection of fonts, `font_index` determines which, otherwise just pass `0`.
2619    ///
2620    /// The font is loaded in [`FONTS.register`].
2621    ///
2622    /// [`FONTS.register`]: FONTS::register
2623    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    /// A custom font loaded from a shared byte slice.
2634    ///
2635    /// If the font data is a collection of fonts, `font_index` determines which, otherwise just pass `0`.
2636    ///
2637    /// The font is loaded in [`FONTS.register`].
2638    ///
2639    /// [`FONTS.register`]: FONTS::register
2640    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    /// A custom font that maps to another font.
2651    ///
2652    /// The font is loaded in [`FONTS.register`].
2653    ///
2654    /// [`FONTS.register`]: FONTS::register
2655    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    /// Set the [`FontStretch`].
2666    ///
2667    /// Default is [`FontStretch::NORMAL`].
2668    pub fn stretch(mut self, stretch: FontStretch) -> Self {
2669        self.stretch = stretch;
2670        self
2671    }
2672
2673    /// Set the [`FontStyle`].
2674    ///
2675    /// Default is [`FontStyle::Normal`].
2676    pub fn style(mut self, style: FontStyle) -> Self {
2677        self.style = style;
2678        self
2679    }
2680
2681    /// Set the [`FontWeight`].
2682    ///
2683    /// Default is [`FontWeight::NORMAL`].
2684    pub fn weight(mut self, weight: FontWeight) -> Self {
2685        self.weight = weight;
2686        self
2687    }
2688}
2689
2690/// The width of a font as an approximate fraction of the normal width.
2691///
2692/// Widths range from 0.5 to 2.0 inclusive, with 1.0 as the normal width.
2693#[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    /// Ultra-condensed width (50%), the narrowest possible.
2737    pub const ULTRA_CONDENSED: FontStretch = FontStretch(0.5);
2738    /// Extra-condensed width (62.5%).
2739    pub const EXTRA_CONDENSED: FontStretch = FontStretch(0.625);
2740    /// Condensed width (75%).
2741    pub const CONDENSED: FontStretch = FontStretch(0.75);
2742    /// Semi-condensed width (87.5%).
2743    pub const SEMI_CONDENSED: FontStretch = FontStretch(0.875);
2744    /// Normal width (100%).
2745    pub const NORMAL: FontStretch = FontStretch(1.0);
2746    /// Semi-expanded width (112.5%).
2747    pub const SEMI_EXPANDED: FontStretch = FontStretch(1.125);
2748    /// Expanded width (125%).
2749    pub const EXPANDED: FontStretch = FontStretch(1.25);
2750    /// Extra-expanded width (150%).
2751    pub const EXTRA_EXPANDED: FontStretch = FontStretch(1.5);
2752    /// Ultra-expanded width (200%), the widest possible.
2753    pub const ULTRA_EXPANDED: FontStretch = FontStretch(2.0);
2754
2755    /// Gets the const name, if this value is one of the constants.
2756    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/// The italic or oblique form of a font.
2830#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
2831pub enum FontStyle {
2832    /// The regular form.
2833    #[default]
2834    Normal,
2835    /// A form that is generally cursive in nature.
2836    Italic,
2837    /// A skewed version of the regular form.
2838    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/// The degree of stroke thickness of a font. This value ranges from 100.0 to 900.0,
2874/// with 400.0 as normal.
2875#[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    /// Thin weight (100), the thinnest value.
2918    pub const THIN: FontWeight = FontWeight(100.0);
2919    /// Extra light weight (200).
2920    pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
2921    /// Light weight (300).
2922    pub const LIGHT: FontWeight = FontWeight(300.0);
2923    /// Normal (400).
2924    pub const NORMAL: FontWeight = FontWeight(400.0);
2925    /// Medium weight (500, higher than normal).
2926    pub const MEDIUM: FontWeight = FontWeight(500.0);
2927    /// Semi-bold weight (600).
2928    pub const SEMIBOLD: FontWeight = FontWeight(600.0);
2929    /// Bold weight (700).
2930    pub const BOLD: FontWeight = FontWeight(700.0);
2931    /// Extra-bold weight (800).
2932    pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
2933    /// Black weight (900), the thickest value.
2934    pub const BLACK: FontWeight = FontWeight(900.0);
2935
2936    /// Gets the const name, if this value is one of the constants.
2937    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/// Configuration of text wrapping for Chinese, Japanese, or Korean text.
2991#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2992pub enum LineBreak {
2993    /// The same rule used by other languages.
2994    Auto,
2995    /// The least restrictive rule, good for short lines.
2996    Loose,
2997    /// The most common rule.
2998    Normal,
2999    /// The most stringent rule.
3000    Strict,
3001    /// Allow line breaks in between any character including punctuation.
3002    Anywhere,
3003}
3004impl Default for LineBreak {
3005    /// [`LineBreak::Auto`]
3006    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/// Definition of how text is split into paragraphs.
3026///
3027/// In the core text shaping this affects paragraph spacing and indent. Rich text widgets
3028/// may also use this when defining their own paragraph segmentation.
3029#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3030#[non_exhaustive]
3031pub enum ParagraphBreak {
3032    /// The entire text is a single paragraph.
3033    #[default]
3034    None,
3035    /// Each actual line is a paragraph. That is `\n` is the paragraph break.
3036    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/// Hyphenation mode.
3051#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3052pub enum Hyphens {
3053    /// Hyphens are never inserted in word breaks.
3054    None,
3055    /// Word breaks only happen in specially marked break characters: `-` and `\u{00AD} SHY`.
3056    ///
3057    /// * `U+2010` - The visible hyphen character.
3058    /// * `U+00AD` - The invisible hyphen character, is made visible in a word break.
3059    Manual,
3060    /// Hyphens are inserted like `Manual` and also using language specific hyphenation rules.
3061    Auto,
3062}
3063impl Default for Hyphens {
3064    /// [`Hyphens::Auto`]
3065    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/// Configure line breaks inside words during text wrap.
3083///
3084/// This value is only considered if it is impossible to fit a full word to a line.
3085///
3086/// Hyphens can be inserted in word breaks using the [`Hyphens`] configuration.
3087#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3088pub enum WordBreak {
3089    /// Line breaks can be inserted in between letters of Chinese/Japanese/Korean text only.
3090    Normal,
3091    /// Line breaks can be inserted between any letter.
3092    BreakAll,
3093    /// Line breaks are not inserted between any letter.
3094    KeepAll,
3095}
3096impl Default for WordBreak {
3097    /// [`WordBreak::Normal`]
3098    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/// Text alignment justification mode.
3116#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3117pub enum Justify {
3118    /// Selects the justification mode based on the language.
3119    ///
3120    /// For Chinese/Japanese/Korean uses `InterLetter` for the others uses `InterWord`.
3121    Auto,
3122    /// The text is justified by adding space between words.
3123    InterWord,
3124    /// The text is justified by adding space between letters.
3125    InterLetter,
3126}
3127impl Default for Justify {
3128    /// [`Justify::Auto`]
3129    fn default() -> Self {
3130        Justify::Auto
3131    }
3132}
3133impl Justify {
3134    /// Resolve `Auto` for the given language.
3135    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/// Various metrics that apply to the entire [`FontFace`].
3159///
3160/// For OpenType fonts, these mostly come from the `OS/2` table.
3161///
3162/// See the [`FreeType Glyph Metrics`] documentation for an explanation of the various metrics.
3163///
3164/// [`FreeType Glyph Metrics`]: https://freetype.org/freetype2/docs/glyphs/glyphs-3.html
3165#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3166#[non_exhaustive]
3167pub struct FontFaceMetrics {
3168    /// The number of font units per em.
3169    ///
3170    /// Font sizes are usually expressed in pixels per em; e.g. `12px` means 12 pixels per em.
3171    pub units_per_em: u32,
3172
3173    /// The maximum amount the font rises above the baseline, in font units.
3174    pub ascent: f32,
3175
3176    /// The maximum amount the font descends below the baseline, in font units.
3177    ///
3178    /// This is typically a negative value to match the definition of `sTypoDescender` in the
3179    /// `OS/2` table in the OpenType specification. If you are used to using Windows or Mac APIs,
3180    /// beware, as the sign is reversed from what those APIs return.
3181    pub descent: f32,
3182
3183    /// Distance between baselines, in font units.
3184    pub line_gap: f32,
3185
3186    /// The suggested distance of the top of the underline from the baseline (negative values
3187    /// indicate below baseline), in font units.
3188    pub underline_position: f32,
3189
3190    /// A suggested value for the underline thickness, in font units.
3191    pub underline_thickness: f32,
3192
3193    /// The approximate amount that uppercase letters rise above the baseline, in font units.
3194    pub cap_height: f32,
3195
3196    /// The approximate amount that non-ascending lowercase letters rise above the baseline, in
3197    /// font units.
3198    pub x_height: f32,
3199
3200    /// A rectangle that surrounds all bounding boxes of all glyphs, in font units.
3201    ///
3202    /// This corresponds to the `xMin`/`xMax`/`yMin`/`yMax` values in the OpenType `head` table.
3203    pub bounds: euclid::Rect<f32, ()>,
3204}
3205impl FontFaceMetrics {
3206    /// Compute [`FontMetrics`] given a font size in pixels.
3207    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/// Various metrics about a [`Font`].
3231///
3232/// You can compute these metrics from a [`FontFaceMetrics`]
3233#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3234#[non_exhaustive]
3235pub struct FontMetrics {
3236    /// Multiply this to a font EM value to get the size in pixels.
3237    pub size_scale: f32,
3238
3239    /// The maximum amount the font rises above the baseline, in pixels.
3240    pub ascent: Px,
3241
3242    /// The maximum amount the font descends below the baseline, in pixels.
3243    ///
3244    /// This is typically a negative value to match the definition of `sTypoDescender` in the
3245    /// `OS/2` table in the OpenType specification. If you are used to using Windows or Mac APIs,
3246    /// beware, as the sign is reversed from what those APIs return.
3247    pub descent: Px,
3248
3249    /// Distance between baselines, in pixels.
3250    pub line_gap: Px,
3251
3252    /// The suggested distance of the top of the underline from the baseline (negative values
3253    /// indicate below baseline), in pixels.
3254    pub underline_position: Px,
3255
3256    /// A suggested value for the underline thickness, in pixels.
3257    pub underline_thickness: Px,
3258
3259    /// The approximate amount that uppercase letters rise above the baseline, in pixels.
3260    pub cap_height: Px,
3261
3262    /// The approximate amount that non-ascending lowercase letters rise above the baseline, in pixels.
3263    pub x_height: Px,
3264
3265    /// A rectangle that surrounds all bounding boxes of all glyphs, in pixels.
3266    ///
3267    /// This corresponds to the `xMin`/`xMax`/`yMin`/`yMax` values in the OpenType `head` table.
3268    pub bounds: PxRect,
3269}
3270impl FontMetrics {
3271    /// The font line height.
3272    pub fn line_height(&self) -> Px {
3273        self.ascent - self.descent + self.line_gap
3274    }
3275}
3276
3277/// Text transform function.
3278#[derive(Clone)]
3279pub enum TextTransformFn {
3280    /// No transform.
3281    None,
3282    /// To UPPERCASE.
3283    Uppercase,
3284    /// to lowercase.
3285    Lowercase,
3286    /// Custom transform function.
3287    Custom(Arc<dyn Fn(&Txt) -> Cow<Txt> + Send + Sync>),
3288}
3289impl TextTransformFn {
3290    /// Apply the text transform.
3291    ///
3292    /// Returns [`Cow::Owned`] if the text was changed.
3293    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    /// New [`Custom`](Self::Custom).
3315    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/// Text white space transform.
3342#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3343pub enum WhiteSpace {
3344    /// Text is not changed, all white spaces and line breaks are preserved.
3345    #[default]
3346    Preserve,
3347    /// Replace white space sequences with a single `' '` and trim lines.
3348    /// Replace line break sequences with a single `'\n'` and trim text.
3349    Merge,
3350    /// Replace white space sequences with a single `' '` and trim lines.
3351    /// Remove single line breaks. Replace line break sequences (>1) with a single `'\n'` and trim text.
3352    MergeParagraph,
3353    /// Replace white spaces and line breaks sequences with a single `' '` and trim the text.
3354    MergeAll,
3355}
3356impl WhiteSpace {
3357    /// Transform the white space of the text.
3358    ///
3359    /// Returns [`Cow::Owned`] if the text was changed.
3360    pub fn transform(self, text: &Txt) -> Cow<'_, Txt> {
3361        match self {
3362            WhiteSpace::Preserve => Cow::Borrowed(text),
3363            WhiteSpace::Merge => {
3364                // search first repeat
3365                let mut prev_i = 0;
3366                for line in text.split_inclusive('\n') {
3367                    // try trim
3368                    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                    // try sequence of spaces
3373                    if !merge {
3374                        let mut prev_is_space = true; // start true to trim
3375                        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                    // found repeat, enter merge mode
3391                    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                    // trim end
3433                    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                // needs to merge if contains '\n' because it is either removed or merged
3443                // also needs to merge it needs to trim.
3444                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                    // trim end
3486                    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                // search first repeat
3496                let mut prev_i = 0;
3497                let mut prev_is_space = true; // starts true to trim
3498                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                        // found repeat, enter merge mode
3507                        let mut out = String::with_capacity(text.len() - 1);
3508                        // push ok start or trim start
3509                        out.push_str(&text[..prev_i]);
3510                        if !out.is_empty() {
3511                            out.push(' ');
3512                        }
3513                        // collapse other whitespace sequences
3514                        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                        // trim end
3524                        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                // search did not trim start nor collapse whitespace sequences
3535
3536                // try trim end
3537                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/// Defines an insert offset in a shaped text.
3562#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
3563pub struct CaretIndex {
3564    /// Char byte offset in the full text.
3565    ///
3566    /// This index can be computed using the [`SegmentedText`].
3567    pub index: usize,
3568    /// Line index in the shaped text.
3569    ///
3570    /// This value is only used to disambiguate between the *end* of a wrap and
3571    /// the *start* of the next, the text itself does not have any line
3572    /// break but visually the user interacts with two lines. Note that this
3573    /// counts wrap lines, and that this value is not required to define a valid
3574    /// CaretIndex.
3575    ///
3576    /// This index can be computed using the [`ShapedText::snap_caret_line`].
3577    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    /// First position.
3588    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/// Reasons why a loader might fail to load a font.
3602#[derive(Debug, Clone)]
3603#[non_exhaustive]
3604pub enum FontLoadingError {
3605    /// The data was of a format the loader didn't recognize.
3606    UnknownFormat,
3607    /// Attempted to load an invalid index in a TrueType or OpenType font collection.
3608    ///
3609    /// For example, if a `.ttc` file has 2 fonts in it, and you ask for the 5th one, you'll get
3610    /// this error.
3611    NoSuchFontInCollection,
3612    /// Attempted to load a malformed or corrupted font.
3613    Parse(ttf_parser::FaceParsingError),
3614    /// Attempted to load a font from the filesystem, but there is no filesystem (e.g. in
3615    /// WebAssembly).
3616    NoFilesystem,
3617    /// A disk or similar I/O error occurred while attempting to load the font.
3618    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}