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
14use font_features::RFontVariations;
15use hashbrown::{HashMap, HashSet};
16use std::{
17    borrow::Cow,
18    fmt, ops,
19    path::{Path, PathBuf},
20    slice::SliceIndex,
21    sync::Arc,
22};
23
24#[macro_use]
25extern crate bitflags;
26
27pub mod font_features;
28
29mod query_util;
30
31mod emoji_util;
32pub use emoji_util::*;
33
34mod ligature_util;
35use ligature_util::*;
36
37mod unicode_bidi_util;
38
39mod segmenting;
40pub use segmenting::*;
41
42mod shaping;
43pub use shaping::*;
44use zng_clone_move::{async_clmv, clmv};
45
46mod hyphenation;
47pub use self::hyphenation::*;
48
49mod unit;
50pub use unit::*;
51
52use parking_lot::{Mutex, RwLock};
53use pastey::paste;
54use zng_app::{
55    AppExtension,
56    event::{event, event_args},
57    render::FontSynthesis,
58    update::{EventUpdate, UPDATES},
59    view_process::{
60        VIEW_PROCESS_INITED_EVENT, ViewRenderer,
61        raw_events::{RAW_FONT_AA_CHANGED_EVENT, RAW_FONT_CHANGED_EVENT},
62    },
63};
64use zng_app_context::app_local;
65use zng_ext_l10n::{Lang, LangMap, lang};
66use zng_layout::unit::{
67    ByteUnits as _, EQ_GRANULARITY, EQ_GRANULARITY_100, Factor, FactorPercent, Px, PxPoint, PxRect, PxSize, TimeUnits as _, about_eq,
68    about_eq_hash, about_eq_ord, euclid,
69};
70use zng_task as task;
71use zng_txt::Txt;
72use zng_var::{
73    IntoVar, ResponderVar, ResponseVar, Var, animation::Transitionable, const_var, impl_from_and_into_var, response_done_var, response_var,
74    var,
75};
76use zng_view_api::{config::FontAntiAliasing, font::IpcFontBytes, ipc::IpcBytes};
77
78/// Font family name.
79///
80/// A possible value for the `font_family` property.
81///
82/// # Case Insensitive
83///
84/// Font family names are case-insensitive. `"Arial"` and `"ARIAL"` are equal and have the same hash.
85#[derive(Clone)]
86pub struct FontName {
87    txt: Txt,
88    is_ascii: bool,
89}
90impl fmt::Debug for FontName {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        if f.alternate() {
93            f.debug_struct("FontName")
94                .field("txt", &self.txt)
95                .field("is_ascii", &self.is_ascii)
96                .finish()
97        } else {
98            write!(f, "{:?}", self.txt)
99        }
100    }
101}
102impl PartialEq for FontName {
103    fn eq(&self, other: &Self) -> bool {
104        self.unicase() == other.unicase()
105    }
106}
107impl Eq for FontName {}
108impl PartialEq<str> for FontName {
109    fn eq(&self, other: &str) -> bool {
110        self.unicase() == unicase::UniCase::<&str>::from(other)
111    }
112}
113impl std::hash::Hash for FontName {
114    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
115        std::hash::Hash::hash(&self.unicase(), state)
116    }
117}
118impl Ord for FontName {
119    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120        if self == other {
121            // case insensitive eq
122            return std::cmp::Ordering::Equal;
123        }
124        self.txt.cmp(&other.txt)
125    }
126}
127impl PartialOrd for FontName {
128    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
129        Some(self.cmp(other))
130    }
131}
132impl FontName {
133    fn unicase(&self) -> unicase::UniCase<&str> {
134        if self.is_ascii {
135            unicase::UniCase::ascii(self)
136        } else {
137            unicase::UniCase::unicode(self)
138        }
139    }
140
141    /// New font name from `&'static str`.
142    pub const fn from_static(name: &'static str) -> Self {
143        FontName {
144            txt: Txt::from_static(name),
145            is_ascii: {
146                // str::is_ascii is not const
147                let name_bytes = name.as_bytes();
148                let mut i = name_bytes.len();
149                let mut is_ascii = true;
150                while i > 0 {
151                    i -= 1;
152                    if !name_bytes[i].is_ascii() {
153                        is_ascii = false;
154                        break;
155                    }
156                }
157                is_ascii
158            },
159        }
160    }
161
162    /// New font name.
163    ///
164    /// Note that the inner name value is a [`Txt`] so you can define a font name using `&'static str` or `String`.
165    ///
166    /// Font names are case insensitive but the input casing is preserved, this casing shows during display and in
167    /// the value of [`name`](Self::name).
168    ///
169    /// [`Txt`]: zng_txt::Txt
170    pub fn new(name: impl Into<Txt>) -> Self {
171        let txt = name.into();
172        FontName {
173            is_ascii: txt.is_ascii(),
174            txt,
175        }
176    }
177
178    /// New "serif" font name.
179    ///
180    /// Serif fonts represent the formal text style for a script.
181    pub fn serif() -> Self {
182        Self::new("serif")
183    }
184
185    /// New "sans-serif" font name.
186    ///
187    /// Glyphs in sans-serif fonts, are generally low contrast (vertical and horizontal stems have close to the same thickness)
188    /// and have stroke endings that are plain — without any flaring, cross stroke, or other ornamentation.
189    pub fn sans_serif() -> Self {
190        Self::new("sans-serif")
191    }
192
193    /// New "monospace" font name.
194    ///
195    /// The sole criterion of a monospace font is that all glyphs have the same fixed width.
196    pub fn monospace() -> Self {
197        Self::new("monospace")
198    }
199
200    /// New "cursive" font name.
201    ///
202    /// Glyphs in cursive fonts generally use a more informal script style, and the result looks more
203    /// like handwritten pen or brush writing than printed letter-work.
204    pub fn cursive() -> Self {
205        Self::new("cursive")
206    }
207
208    /// New "fantasy" font name.
209    ///
210    /// Fantasy fonts are primarily decorative or expressive fonts that contain decorative or expressive representations of characters.
211    pub fn fantasy() -> Self {
212        Self::new("fantasy")
213    }
214
215    /// Reference the font name string.
216    pub fn name(&self) -> &str {
217        &self.txt
218    }
219
220    /// Unwraps into a [`Txt`].
221    ///
222    /// [`Txt`]: zng_txt::Txt
223    pub fn into_text(self) -> Txt {
224        self.txt
225    }
226}
227impl_from_and_into_var! {
228    fn from(s: &'static str) -> FontName {
229        FontName::new(s)
230    }
231    fn from(s: String) -> FontName {
232        FontName::new(s)
233    }
234    fn from(s: Cow<'static, str>) -> FontName {
235        FontName::new(s)
236    }
237    fn from(f: FontName) -> Txt {
238        f.into_text()
239    }
240}
241impl fmt::Display for FontName {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        f.write_str(self.name())
244    }
245}
246impl std::ops::Deref for FontName {
247    type Target = str;
248
249    fn deref(&self) -> &Self::Target {
250        self.txt.deref()
251    }
252}
253impl AsRef<str> for FontName {
254    fn as_ref(&self) -> &str {
255        self.txt.as_ref()
256    }
257}
258impl serde::Serialize for FontName {
259    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
260    where
261        S: serde::Serializer,
262    {
263        self.txt.serialize(serializer)
264    }
265}
266impl<'de> serde::Deserialize<'de> for FontName {
267    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
268    where
269        D: serde::Deserializer<'de>,
270    {
271        Txt::deserialize(deserializer).map(FontName::new)
272    }
273}
274
275/// A list of [font names](FontName) in priority order.
276///
277/// # Examples
278///
279/// This type is usually initialized using conversion:
280///
281/// ```
282/// # use zng_ext_font::*;
283/// fn foo(font_names: impl Into<FontNames>) {}
284///
285/// foo(["Arial", "sans-serif", "monospace"]);
286/// ```
287///
288/// You can also use the specialized [`push`](Self::push) that converts:
289///
290/// ```
291/// # use zng_ext_font::*;
292/// let user_preference = "Comic Sans".to_owned();
293///
294/// let mut names = FontNames::empty();
295/// names.push(user_preference);
296/// names.push("Arial");
297/// names.extend(FontNames::default());
298/// ```
299///
300/// # Default
301///
302/// The default value is the [`system_ui`](FontNames::system_ui) for the undefined language (`und`).
303#[derive(Eq, PartialEq, Hash, Clone, serde::Serialize, serde::Deserialize)]
304#[serde(transparent)]
305pub struct FontNames(pub Vec<FontName>);
306impl FontNames {
307    /// Empty list.
308    pub fn empty() -> Self {
309        FontNames(vec![])
310    }
311
312    /// Returns the default UI font names for Windows.
313    pub fn windows_ui(lang: &Lang) -> Self {
314        // source: VSCode
315        // https://github.com/microsoft/vscode/blob/6825c886700ac11d07f7646d8d8119c9cdd9d288/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css
316
317        if lang!("zh-Hans").matches(lang, true, false) {
318            ["Segoe UI", "Microsoft YaHei", "Segoe Ui Emoji", "sans-serif"].into()
319        } else if lang!("zh-Hant").matches(lang, true, false) {
320            ["Segoe UI", "Microsoft Jhenghei", "Segoe Ui Emoji", "sans-serif"].into()
321        } else if lang!(ja).matches(lang, true, false) {
322            ["Segoe UI", "Yu Gothic UI", "Meiryo UI", "Segoe Ui Emoji", "sans-serif"].into()
323        } else if lang!(ko).matches(lang, true, false) {
324            ["Segoe UI", "Malgun Gothic", "Dotom", "Segoe Ui Emoji", "sans-serif"].into()
325        } else {
326            ["Segoe UI", "Segoe Ui Emoji", "sans-serif"].into()
327        }
328    }
329
330    /// Returns the default UI font names for MacOS/iOS.
331    pub fn mac_ui(lang: &Lang) -> Self {
332        // source: VSCode
333
334        if lang!("zh-Hans").matches(lang, true, false) {
335            ["PingFang SC", "Hiragino Sans GB", "Apple Color Emoji", "sans-serif"].into()
336        } else if lang!("zh-Hant").matches(lang, true, false) {
337            ["PingFang TC", "Apple Color Emoji", "sans-serif"].into()
338        } else if lang!(ja).matches(lang, true, false) {
339            ["Hiragino Kaku Gothic Pro", "Apple Color Emoji", "sans-serif"].into()
340        } else if lang!(ko).matches(lang, true, false) {
341            [
342                "Nanum Gothic",
343                "Apple SD Gothic Neo",
344                "AppleGothic",
345                "Apple Color Emoji",
346                "sans-serif",
347            ]
348            .into()
349        } else {
350            ["Neue Helvetica", "Lucida Grande", "Apple Color Emoji", "sans-serif"].into()
351        }
352    }
353
354    /// Returns the default UI font names for Linux.
355    pub fn linux_ui(lang: &Lang) -> Self {
356        // source: VSCode
357
358        if lang!("zh-Hans").matches(lang, true, false) {
359            [
360                "Ubuntu",
361                "Droid Sans",
362                "Source Han Sans SC",
363                "Source Han Sans CN",
364                "Source Han Sans",
365                "Noto Color Emoji",
366                "sans-serif",
367            ]
368            .into()
369        } else if lang!("zh-Hant").matches(lang, true, false) {
370            [
371                "Ubuntu",
372                "Droid Sans",
373                "Source Han Sans TC",
374                "Source Han Sans TW",
375                "Source Han Sans",
376                "Noto Color Emoji",
377                "sans-serif",
378            ]
379            .into()
380        } else if lang!(ja).matches(lang, true, false) {
381            [
382                "system-ui",
383                "Ubuntu",
384                "Droid Sans",
385                "Source Han Sans J",
386                "Source Han Sans JP",
387                "Source Han Sans",
388                "Noto Color Emoji",
389                "sans-serif",
390            ]
391            .into()
392        } else if lang!(ko).matches(lang, true, false) {
393            [
394                "system-ui",
395                "Ubuntu",
396                "Droid Sans",
397                "Source Han Sans K",
398                "Source Han Sans JR",
399                "Source Han Sans",
400                "UnDotum",
401                "FBaekmuk Gulim",
402                "Noto Color Emoji",
403                "sans-serif",
404            ]
405            .into()
406        } else {
407            ["system-ui", "Ubuntu", "Droid Sans", "Noto Color Emoji", "sans-serif"].into()
408        }
409    }
410
411    /// Returns the default UI font names for the current operating system.
412    pub fn system_ui(lang: &Lang) -> Self {
413        if cfg!(windows) {
414            Self::windows_ui(lang)
415        } else if cfg!(target_os = "linux") {
416            Self::linux_ui(lang)
417        } else if cfg!(target_os = "macos") {
418            Self::mac_ui(lang)
419        } else {
420            [FontName::sans_serif()].into()
421        }
422    }
423
424    /// Push a font name from any type that converts to [`FontName`].
425    pub fn push(&mut self, font_name: impl Into<FontName>) {
426        self.0.push(font_name.into())
427    }
428}
429impl Default for FontNames {
430    fn default() -> Self {
431        Self::system_ui(&Lang::default())
432    }
433}
434impl fmt::Debug for FontNames {
435    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436        if f.alternate() {
437            f.debug_tuple("FontNames").field(&self.0).finish()
438        } else if self.0.is_empty() {
439            write!(f, "[]")
440        } else if self.0.len() == 1 {
441            write!(f, "{:?}", self.0[0])
442        } else {
443            write!(f, "[{:?}, ", self.0[0])?;
444            for name in &self.0[1..] {
445                write!(f, "{name:?}, ")?;
446            }
447            write!(f, "]")
448        }
449    }
450}
451impl fmt::Display for FontNames {
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        let mut iter = self.0.iter();
454
455        if let Some(name) = iter.next() {
456            write!(f, "{name}")?;
457            for name in iter {
458                write!(f, ", {name}")?;
459            }
460        }
461
462        Ok(())
463    }
464}
465impl_from_and_into_var! {
466    fn from(font_name: &'static str) -> FontNames {
467        FontNames(vec![FontName::new(font_name)])
468    }
469
470    fn from(font_name: String) -> FontNames {
471        FontNames(vec![FontName::new(font_name)])
472    }
473
474    fn from(font_name: Txt) -> FontNames {
475        FontNames(vec![FontName::new(font_name)])
476    }
477
478    fn from(font_names: Vec<FontName>) -> FontNames {
479        FontNames(font_names)
480    }
481
482    fn from(font_names: Vec<&'static str>) -> FontNames {
483        FontNames(font_names.into_iter().map(FontName::new).collect())
484    }
485
486    fn from(font_names: Vec<String>) -> FontNames {
487        FontNames(font_names.into_iter().map(FontName::new).collect())
488    }
489
490    fn from(font_name: FontName) -> FontNames {
491        FontNames(vec![font_name])
492    }
493}
494impl ops::Deref for FontNames {
495    type Target = Vec<FontName>;
496
497    fn deref(&self) -> &Self::Target {
498        &self.0
499    }
500}
501impl ops::DerefMut for FontNames {
502    fn deref_mut(&mut self) -> &mut Self::Target {
503        &mut self.0
504    }
505}
506impl std::iter::Extend<FontName> for FontNames {
507    fn extend<T: IntoIterator<Item = FontName>>(&mut self, iter: T) {
508        self.0.extend(iter)
509    }
510}
511impl IntoIterator for FontNames {
512    type Item = FontName;
513
514    type IntoIter = std::vec::IntoIter<FontName>;
515
516    fn into_iter(self) -> Self::IntoIter {
517        self.0.into_iter()
518    }
519}
520impl<const N: usize> From<[FontName; N]> for FontNames {
521    fn from(font_names: [FontName; N]) -> Self {
522        FontNames(font_names.into())
523    }
524}
525impl<const N: usize> IntoVar<FontNames> for [FontName; N] {
526    fn into_var(self) -> Var<FontNames> {
527        const_var(self.into())
528    }
529}
530impl<const N: usize> From<[&'static str; N]> for FontNames {
531    fn from(font_names: [&'static str; N]) -> Self {
532        FontNames(font_names.into_iter().map(FontName::new).collect())
533    }
534}
535impl<const N: usize> IntoVar<FontNames> for [&'static str; N] {
536    fn into_var(self) -> Var<FontNames> {
537        const_var(self.into())
538    }
539}
540impl<const N: usize> From<[String; N]> for FontNames {
541    fn from(font_names: [String; N]) -> Self {
542        FontNames(font_names.into_iter().map(FontName::new).collect())
543    }
544}
545impl<const N: usize> IntoVar<FontNames> for [String; N] {
546    fn into_var(self) -> Var<FontNames> {
547        const_var(self.into())
548    }
549}
550impl<const N: usize> From<[Txt; N]> for FontNames {
551    fn from(font_names: [Txt; N]) -> Self {
552        FontNames(font_names.into_iter().map(FontName::new).collect())
553    }
554}
555impl<const N: usize> IntoVar<FontNames> for [Txt; N] {
556    fn into_var(self) -> Var<FontNames> {
557        const_var(self.into())
558    }
559}
560
561event! {
562    /// Change in [`FONTS`] that may cause a font query to now give
563    /// a different result.
564    ///
565    /// # Cache
566    ///
567    /// Every time this event updates the font cache is cleared. Meaning that even
568    /// if the query returns the same font it will be a new reference.
569    ///
570    /// Fonts only unload when all references to then are dropped, so you can still continue using
571    /// old references if you don't want to monitor this event.
572    pub static FONT_CHANGED_EVENT: FontChangedArgs;
573}
574
575event_args! {
576    /// [`FONT_CHANGED_EVENT`] arguments.
577    pub struct FontChangedArgs {
578        /// The change that happened.
579        pub change: FontChange,
580
581        ..
582
583        /// Broadcast to all widgets.
584        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
585            list.search_all()
586        }
587    }
588}
589
590/// Possible changes in a [`FontChangedArgs`].
591#[derive(Clone, Debug)]
592pub enum FontChange {
593    /// OS fonts change.
594    ///
595    /// Currently this is only supported in Microsoft Windows.
596    SystemFonts,
597
598    /// Custom fonts change caused by call to [`FONTS.register`] or [`FONTS.unregister`].
599    ///
600    /// [`FONTS.register`]: FONTS::register
601    /// [`FONTS.unregister`]: FONTS::unregister
602    CustomFonts,
603
604    /// Custom request caused by call to [`FONTS.refresh`].
605    ///
606    /// [`FONTS.refresh`]: FONTS::refresh
607    Refresh,
608
609    /// One of the [`GenericFonts`] was set for the language.
610    ///
611    /// The font name is one of [`FontName`] generic names.
612    ///
613    /// [`GenericFonts`]: struct@GenericFonts
614    GenericFont(FontName, Lang),
615
616    /// A new [fallback](GenericFonts::fallback) font was set for the language.
617    Fallback(Lang),
618}
619
620/// Application extension that manages text fonts.
621///
622/// Services this extension provides:
623///
624/// * [`FONTS`] - Service that finds and loads fonts.
625/// * [`HYPHENATION`] - Service that loads and applies hyphenation dictionaries.
626///
627/// Events this extension provides:
628///
629/// * [`FONT_CHANGED_EVENT`] - Font config or system fonts changed.
630#[derive(Default)]
631#[non_exhaustive]
632pub struct FontManager {}
633impl AppExtension for FontManager {
634    fn event_preview(&mut self, update: &mut EventUpdate) {
635        if RAW_FONT_CHANGED_EVENT.has(update) {
636            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::SystemFonts));
637        } else if let Some(args) = RAW_FONT_AA_CHANGED_EVENT.on(update) {
638            FONTS_SV.read().font_aa.set(args.aa);
639        } else if FONT_CHANGED_EVENT.has(update) {
640            FONTS_SV.write().on_fonts_changed();
641        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
642            && args.is_respawn
643        {
644            let mut fonts = FONTS_SV.write();
645            fonts.loader.on_view_process_respawn();
646        }
647    }
648
649    fn update(&mut self) {
650        let mut fonts = FONTS_SV.write();
651
652        {
653            let mut f = GENERIC_FONTS_SV.write();
654            for request in std::mem::take(&mut f.requests) {
655                request(&mut f);
656            }
657        }
658
659        let mut changed = false;
660        for (request, responder) in std::mem::take(&mut fonts.loader.unregister_requests) {
661            let r = if let Some(removed) = fonts.loader.custom_fonts.remove(&request) {
662                // cut circular reference so that when the last font ref gets dropped
663                // this font face also gets dropped. Also tag the font as unregistered
664                // so it does not create further circular references.
665                for removed in removed {
666                    removed.on_refresh();
667                }
668
669                changed = true;
670
671                true
672            } else {
673                false
674            };
675            responder.respond(r);
676        }
677        if changed {
678            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
679        }
680
681        if fonts.prune_requested {
682            fonts.on_prune();
683        }
684    }
685}
686
687app_local! {
688    static FONTS_SV: FontsService = FontsService {
689        loader: FontFaceLoader::new(),
690        prune_requested: false,
691        font_aa: var(FontAntiAliasing::Default),
692    };
693}
694
695struct FontsService {
696    loader: FontFaceLoader,
697    prune_requested: bool,
698    font_aa: Var<FontAntiAliasing>,
699}
700impl FontsService {
701    fn on_fonts_changed(&mut self) {
702        self.loader.on_refresh();
703        self.prune_requested = false;
704    }
705
706    fn on_prune(&mut self) {
707        self.loader.on_prune();
708        self.prune_requested = false;
709    }
710}
711
712/// Font loading, custom fonts and app font configuration.
713pub struct FONTS;
714impl FONTS {
715    /// Clear cache and notify `Refresh` in [`FONT_CHANGED_EVENT`].
716    ///
717    /// See the event documentation for more information.
718    pub fn refresh(&self) {
719        FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Refresh));
720    }
721
722    /// Remove all unused fonts from cache.
723    pub fn prune(&self) {
724        let mut ft = FONTS_SV.write();
725        if !ft.prune_requested {
726            ft.prune_requested = true;
727            UPDATES.update(None);
728        }
729    }
730
731    /// Actual name of generic fonts.
732    pub fn generics(&self) -> &'static GenericFonts {
733        &GenericFonts {}
734    }
735
736    /// Load and register a custom font.
737    ///
738    /// If the font loads correctly a [`FONT_CHANGED_EVENT`] notification is scheduled.
739    /// Fonts sourced from a file are not monitored for changes, you can *reload* the font
740    /// by calling `register` again with the same font name.
741    ///
742    /// The returned response will update once when the font finishes loading with the new font.
743    /// At minimum the new font will be available on the next update.
744    pub fn register(&self, custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
745        FontFaceLoader::register(custom_font)
746    }
747
748    /// Removes a custom font family. If the font faces are not in use it is also unloaded.
749    ///
750    /// Returns a response var that updates once with a value that indicates if any custom font was removed.
751    pub fn unregister(&self, custom_family: FontName) -> ResponseVar<bool> {
752        FONTS_SV.write().loader.unregister(custom_family)
753    }
754
755    /// Gets a font list that best matches the query.
756    pub fn list(
757        &self,
758        families: &[FontName],
759        style: FontStyle,
760        weight: FontWeight,
761        stretch: FontStretch,
762        lang: &Lang,
763    ) -> ResponseVar<FontFaceList> {
764        // try with shared lock
765        if let Some(cached) = FONTS_SV.read().loader.try_list(families, style, weight, stretch, lang) {
766            return cached;
767        }
768        // begin load with exclusive lock (cache is tried again in `load`)
769        FONTS_SV.write().loader.load_list(families, style, weight, stretch, lang)
770    }
771
772    /// Find a single font face that best matches the query.
773    pub fn find(
774        &self,
775        family: &FontName,
776        style: FontStyle,
777        weight: FontWeight,
778        stretch: FontStretch,
779        lang: &Lang,
780    ) -> ResponseVar<Option<FontFace>> {
781        // try with shared lock
782        if let Some(cached) = FONTS_SV.read().loader.try_cached(family, style, weight, stretch, lang) {
783            return cached;
784        }
785        // begin load with exclusive lock (cache is tried again in `load`)
786        FONTS_SV.write().loader.load(family, style, weight, stretch, lang)
787    }
788
789    /// Find a single font face with all normal properties.
790    pub fn normal(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
791        self.find(family, FontStyle::Normal, FontWeight::NORMAL, FontStretch::NORMAL, lang)
792    }
793
794    /// Find a single font face with italic style, normal weight and stretch.
795    pub fn italic(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
796        self.find(family, FontStyle::Italic, FontWeight::NORMAL, FontStretch::NORMAL, lang)
797    }
798
799    /// Find a single font face with bold weight, normal style and stretch.
800    pub fn bold(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
801        self.find(family, FontStyle::Normal, FontWeight::BOLD, FontStretch::NORMAL, lang)
802    }
803
804    /// Gets all [registered](Self::register) font families.
805    pub fn custom_fonts(&self) -> Vec<FontName> {
806        FONTS_SV.read().loader.custom_fonts.keys().cloned().collect()
807    }
808
809    /// Query all font families available in the system.
810    ///
811    /// Note that the variable will only update once with the query result, this is not a live view.
812    pub fn system_fonts(&self) -> ResponseVar<Vec<FontName>> {
813        query_util::system_all()
814    }
815
816    /// Gets the system font anti-aliasing config as a read-only var.
817    ///
818    /// The variable updates when the system config changes.
819    pub fn system_font_aa(&self) -> Var<FontAntiAliasing> {
820        FONTS_SV.read().font_aa.read_only()
821    }
822}
823
824impl<'a> From<ttf_parser::Face<'a>> for FontFaceMetrics {
825    fn from(f: ttf_parser::Face<'a>) -> Self {
826        let underline = f
827            .underline_metrics()
828            .unwrap_or(ttf_parser::LineMetrics { position: 0, thickness: 0 });
829        FontFaceMetrics {
830            units_per_em: f.units_per_em() as _,
831            ascent: f.ascender() as f32,
832            descent: f.descender() as f32,
833            line_gap: f.line_gap() as f32,
834            underline_position: underline.position as f32,
835            underline_thickness: underline.thickness as f32,
836            cap_height: f.capital_height().unwrap_or(0) as f32,
837            x_height: f.x_height().unwrap_or(0) as f32,
838            bounds: euclid::rect(
839                f.global_bounding_box().x_min as f32,
840                f.global_bounding_box().x_max as f32,
841                f.global_bounding_box().width() as f32,
842                f.global_bounding_box().height() as f32,
843            ),
844        }
845    }
846}
847
848#[derive(PartialEq, Eq, Hash)]
849struct FontInstanceKey(Px, Box<[(ttf_parser::Tag, i32)]>);
850impl FontInstanceKey {
851    /// Returns the key.
852    pub fn new(size: Px, variations: &[rustybuzz::Variation]) -> Self {
853        let variations_key: Vec<_> = variations.iter().map(|p| (p.tag, (p.value * 1000.0) as i32)).collect();
854        FontInstanceKey(size, variations_key.into_boxed_slice())
855    }
856}
857
858/// A font face selected from a font family.
859///
860/// Usually this is part of a [`FontList`] that can be requested from
861/// the [`FONTS`] service.
862///
863/// This type is a shared reference to the font data, cloning it is cheap.
864#[derive(Clone)]
865pub struct FontFace(Arc<LoadedFontFace>);
866struct LoadedFontFace {
867    data: FontBytes,
868    face_index: u32,
869    display_name: FontName,
870    family_name: FontName,
871    postscript_name: Option<Txt>,
872    style: FontStyle,
873    weight: FontWeight,
874    stretch: FontStretch,
875    metrics: FontFaceMetrics,
876    lig_carets: LigatureCaretList,
877    flags: FontFaceFlags,
878    m: Mutex<FontFaceMut>,
879}
880bitflags! {
881    #[derive(Debug, Clone, Copy)]
882    struct FontFaceFlags: u8 {
883        const IS_MONOSPACE = 0b0000_0001;
884        const HAS_LIGATURES = 0b0000_0010;
885        const HAS_RASTER_IMAGES = 0b0000_0100;
886        const HAS_SVG_IMAGES = 0b0000_1000;
887    }
888}
889struct FontFaceMut {
890    instances: HashMap<FontInstanceKey, Font>,
891    render_ids: Vec<RenderFontFace>,
892    unregistered: bool,
893}
894
895impl fmt::Debug for FontFace {
896    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
897        let m = self.0.m.lock();
898        f.debug_struct("FontFace")
899            .field("display_name", &self.0.display_name)
900            .field("family_name", &self.0.family_name)
901            .field("postscript_name", &self.0.postscript_name)
902            .field("flags", &self.0.flags)
903            .field("style", &self.0.style)
904            .field("weight", &self.0.weight)
905            .field("stretch", &self.0.stretch)
906            .field("metrics", &self.0.metrics)
907            .field("instances.len()", &m.instances.len())
908            .field("render_keys.len()", &m.render_ids.len())
909            .field("unregistered", &m.unregistered)
910            .finish_non_exhaustive()
911    }
912}
913impl PartialEq for FontFace {
914    fn eq(&self, other: &Self) -> bool {
915        Arc::ptr_eq(&self.0, &other.0)
916    }
917}
918impl Eq for FontFace {}
919impl FontFace {
920    /// New empty font face.
921    pub fn empty() -> Self {
922        FontFace(Arc::new(LoadedFontFace {
923            data: FontBytes::from_static(&[]),
924            face_index: 0,
925            display_name: FontName::from("<empty>"),
926            family_name: FontName::from("<empty>"),
927            postscript_name: None,
928            flags: FontFaceFlags::IS_MONOSPACE,
929            style: FontStyle::Normal,
930            weight: FontWeight::NORMAL,
931            stretch: FontStretch::NORMAL,
932            // values copied from a monospace font
933            metrics: FontFaceMetrics {
934                units_per_em: 2048,
935                ascent: 1616.0,
936                descent: -432.0,
937                line_gap: 0.0,
938                underline_position: -205.0,
939                underline_thickness: 102.0,
940                cap_height: 1616.0,
941                x_height: 1616.0,
942                // `xMin`/`xMax`/`yMin`/`yMax`
943                bounds: euclid::Box2D::new(euclid::point2(0.0, -432.0), euclid::point2(1291.0, 1616.0)).to_rect(),
944            },
945            lig_carets: LigatureCaretList::empty(),
946            m: Mutex::new(FontFaceMut {
947                instances: HashMap::default(),
948                render_ids: vec![],
949                unregistered: false,
950            }),
951        }))
952    }
953
954    /// Is empty font face.
955    pub fn is_empty(&self) -> bool {
956        self.0.data.is_empty()
957    }
958
959    async fn load_custom(custom_font: CustomFont) -> Result<Self, FontLoadingError> {
960        let bytes;
961        let mut face_index;
962
963        match custom_font.source {
964            FontSource::File(path, index) => {
965                bytes = task::wait(|| FontBytes::from_file(path)).await?;
966                face_index = index;
967            }
968            FontSource::Memory(arc, index) => {
969                bytes = arc;
970                face_index = index;
971            }
972            FontSource::Alias(other_font) => {
973                let result = FONTS_SV
974                    .write()
975                    .loader
976                    .load_resolved(&other_font, custom_font.style, custom_font.weight, custom_font.stretch);
977                return match result.wait_rsp().await {
978                    Some(other_font) => Ok(FontFace(Arc::new(LoadedFontFace {
979                        data: other_font.0.data.clone(),
980                        face_index: other_font.0.face_index,
981                        display_name: custom_font.name.clone(),
982                        family_name: custom_font.name,
983                        postscript_name: None,
984                        style: other_font.0.style,
985                        weight: other_font.0.weight,
986                        stretch: other_font.0.stretch,
987                        metrics: other_font.0.metrics.clone(),
988                        m: Mutex::new(FontFaceMut {
989                            instances: Default::default(),
990                            render_ids: Default::default(),
991                            unregistered: Default::default(),
992                        }),
993                        lig_carets: other_font.0.lig_carets.clone(),
994                        flags: other_font.0.flags,
995                    }))),
996                    None => Err(FontLoadingError::NoSuchFontInCollection),
997                };
998            }
999        }
1000
1001        let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1002            Ok(f) => f,
1003            Err(e) => {
1004                match e {
1005                    // try again with font 0 (font-kit selects a high index for Ubuntu Font)
1006                    ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1007                    e => return Err(FontLoadingError::Parse(e)),
1008                }
1009
1010                match ttf_parser::Face::parse(&bytes, face_index) {
1011                    Ok(f) => f,
1012                    Err(_) => return Err(FontLoadingError::Parse(e)),
1013                }
1014            }
1015        };
1016
1017        let has_ligatures = ttf_face.tables().gsub.is_some();
1018        let lig_carets = if has_ligatures {
1019            LigatureCaretList::empty()
1020        } else {
1021            LigatureCaretList::load(ttf_face.raw_face())?
1022        };
1023
1024        // all tables used by `ttf_parser::Face::glyph_raster_image`
1025        let has_raster_images = {
1026            let t = ttf_face.tables();
1027            t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1028        };
1029
1030        let mut flags = FontFaceFlags::empty();
1031        flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1032        flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1033        flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1034        flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1035
1036        Ok(FontFace(Arc::new(LoadedFontFace {
1037            face_index,
1038            display_name: custom_font.name.clone(),
1039            family_name: custom_font.name,
1040            postscript_name: None,
1041            style: custom_font.style,
1042            weight: custom_font.weight,
1043            stretch: custom_font.stretch,
1044            metrics: ttf_face.into(),
1045            lig_carets,
1046            m: Mutex::new(FontFaceMut {
1047                instances: Default::default(),
1048                render_ids: Default::default(),
1049                unregistered: Default::default(),
1050            }),
1051            data: bytes,
1052            flags,
1053        })))
1054    }
1055
1056    fn load(bytes: FontBytes, mut face_index: u32) -> Result<Self, FontLoadingError> {
1057        let _span = tracing::trace_span!("FontFace::load").entered();
1058
1059        let ttf_face = match ttf_parser::Face::parse(&bytes, face_index) {
1060            Ok(f) => f,
1061            Err(e) => {
1062                match e {
1063                    // try again with font 0 (font-kit selects a high index for Ubuntu Font)
1064                    ttf_parser::FaceParsingError::FaceIndexOutOfBounds => face_index = 0,
1065                    e => return Err(FontLoadingError::Parse(e)),
1066                }
1067
1068                match ttf_parser::Face::parse(&bytes, face_index) {
1069                    Ok(f) => f,
1070                    Err(_) => return Err(FontLoadingError::Parse(e)),
1071                }
1072            }
1073        };
1074
1075        let has_ligatures = ttf_face.tables().gsub.is_some();
1076        let lig_carets = if has_ligatures {
1077            LigatureCaretList::empty()
1078        } else {
1079            LigatureCaretList::load(ttf_face.raw_face())?
1080        };
1081
1082        let mut display_name = None;
1083        let mut family_name = None;
1084        let mut postscript_name = None;
1085        let mut any_name = None::<String>;
1086        for name in ttf_face.names() {
1087            if let Some(n) = name.to_string() {
1088                match name.name_id {
1089                    ttf_parser::name_id::FULL_NAME => display_name = Some(n),
1090                    ttf_parser::name_id::FAMILY => family_name = Some(n),
1091                    ttf_parser::name_id::POST_SCRIPT_NAME => postscript_name = Some(n),
1092                    _ => match &mut any_name {
1093                        Some(s) => {
1094                            if n.len() > s.len() {
1095                                *s = n;
1096                            }
1097                        }
1098                        None => any_name = Some(n),
1099                    },
1100                }
1101            }
1102        }
1103        let display_name = FontName::new(Txt::from_str(
1104            display_name
1105                .as_ref()
1106                .or(family_name.as_ref())
1107                .or(postscript_name.as_ref())
1108                .or(any_name.as_ref())
1109                .unwrap(),
1110        ));
1111        let family_name = family_name.map(FontName::from).unwrap_or_else(|| display_name.clone());
1112        let postscript_name = postscript_name.map(Txt::from);
1113
1114        if ttf_face.units_per_em() == 0 {
1115            // observed this in Noto Color Emoji (with font_kit)
1116            tracing::debug!("font {display_name:?} units_per_em 0");
1117            return Err(FontLoadingError::UnknownFormat);
1118        }
1119
1120        // all tables used by `ttf_parser::Face::glyph_raster_image`
1121        let has_raster_images = {
1122            let t = ttf_face.tables();
1123            t.sbix.is_some() || t.bdat.is_some() || t.ebdt.is_some() || t.cbdt.is_some()
1124        };
1125
1126        let mut flags = FontFaceFlags::empty();
1127        flags.set(FontFaceFlags::IS_MONOSPACE, ttf_face.is_monospaced());
1128        flags.set(FontFaceFlags::HAS_LIGATURES, has_ligatures);
1129        flags.set(FontFaceFlags::HAS_RASTER_IMAGES, has_raster_images);
1130        flags.set(FontFaceFlags::HAS_SVG_IMAGES, ttf_face.tables().svg.is_some());
1131
1132        Ok(FontFace(Arc::new(LoadedFontFace {
1133            face_index,
1134            family_name,
1135            display_name,
1136            postscript_name,
1137            style: ttf_face.style().into(),
1138            weight: ttf_face.weight().into(),
1139            stretch: ttf_face.width().into(),
1140            metrics: ttf_face.into(),
1141            lig_carets,
1142            m: Mutex::new(FontFaceMut {
1143                instances: Default::default(),
1144                render_ids: Default::default(),
1145                unregistered: Default::default(),
1146            }),
1147            data: bytes,
1148            flags,
1149        })))
1150    }
1151
1152    fn on_refresh(&self) {
1153        let mut m = self.0.m.lock();
1154        m.instances.clear();
1155        m.unregistered = true;
1156    }
1157
1158    fn render_face(&self, renderer: &ViewRenderer) -> zng_view_api::font::FontFaceId {
1159        let mut m = self.0.m.lock();
1160        for r in m.render_ids.iter() {
1161            if &r.renderer == renderer {
1162                return r.face_id;
1163            }
1164        }
1165
1166        let key = match renderer.add_font_face(self.0.data.to_ipc(), self.0.face_index) {
1167            Ok(k) => k,
1168            Err(_) => {
1169                tracing::debug!("respawned calling `add_font`, will return dummy font key");
1170                return zng_view_api::font::FontFaceId::INVALID;
1171            }
1172        };
1173
1174        m.render_ids.push(RenderFontFace::new(renderer, key));
1175
1176        key
1177    }
1178
1179    /// Loads the harfbuzz face.
1180    ///
1181    /// Loads from in memory [`bytes`].
1182    ///
1183    /// Returns `None` if [`is_empty`].
1184    ///
1185    /// [`is_empty`]: Self::is_empty
1186    /// [`bytes`]: Self::bytes
1187    pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1188        if self.is_empty() {
1189            None
1190        } else {
1191            Some(rustybuzz::Face::from_slice(&self.0.data, self.0.face_index).unwrap())
1192        }
1193    }
1194
1195    /// Loads the full TTF face.
1196    ///
1197    /// Loads from in memory [`bytes`].
1198    ///
1199    /// Returns `None` if [`is_empty`].
1200    ///
1201    /// [`is_empty`]: Self::is_empty
1202    /// [`bytes`]: Self::bytes
1203    pub fn ttf(&self) -> Option<ttf_parser::Face<'_>> {
1204        if self.is_empty() {
1205            None
1206        } else {
1207            Some(ttf_parser::Face::parse(&self.0.data, self.0.face_index).unwrap())
1208        }
1209    }
1210
1211    /// Reference the font file bytes.
1212    pub fn bytes(&self) -> &FontBytes {
1213        &self.0.data
1214    }
1215    /// Index of the font face in the [font file](Self::bytes).
1216    pub fn index(&self) -> u32 {
1217        self.0.face_index
1218    }
1219
1220    /// Font full name.
1221    pub fn display_name(&self) -> &FontName {
1222        &self.0.display_name
1223    }
1224
1225    /// Font family name.
1226    pub fn family_name(&self) -> &FontName {
1227        &self.0.family_name
1228    }
1229
1230    /// Font globally unique name.
1231    pub fn postscript_name(&self) -> Option<&str> {
1232        self.0.postscript_name.as_deref()
1233    }
1234
1235    /// Font style.
1236    pub fn style(&self) -> FontStyle {
1237        self.0.style
1238    }
1239
1240    /// Font weight.
1241    pub fn weight(&self) -> FontWeight {
1242        self.0.weight
1243    }
1244
1245    /// Font stretch.
1246    pub fn stretch(&self) -> FontStretch {
1247        self.0.stretch
1248    }
1249
1250    /// Font is monospace (fixed-width).
1251    pub fn is_monospace(&self) -> bool {
1252        self.0.flags.contains(FontFaceFlags::IS_MONOSPACE)
1253    }
1254
1255    /// Font metrics in font units.
1256    pub fn metrics(&self) -> &FontFaceMetrics {
1257        &self.0.metrics
1258    }
1259
1260    /// Gets a cached sized [`Font`].
1261    ///
1262    /// The `font_size` is the size of `1 font EM` in pixels.
1263    ///
1264    /// The `variations` are custom [font variations] that will be used
1265    /// during shaping and rendering.
1266    ///
1267    /// [font variations]: crate::font_features::FontVariations::finalize
1268    pub fn sized(&self, font_size: Px, variations: RFontVariations) -> Font {
1269        let key = FontInstanceKey::new(font_size, &variations);
1270        let mut m = self.0.m.lock();
1271        if !m.unregistered {
1272            m.instances
1273                .entry(key)
1274                .or_insert_with(|| Font::new(self.clone(), font_size, variations))
1275                .clone()
1276        } else {
1277            tracing::debug!(target: "font_loading", "creating font from unregistered `{}`, will not cache", self.0.display_name);
1278            Font::new(self.clone(), font_size, variations)
1279        }
1280    }
1281
1282    /// Gets what font synthesis to use to better render this font face given the style and weight.
1283    pub fn synthesis_for(&self, style: FontStyle, weight: FontWeight) -> FontSynthesis {
1284        let mut synth = FontSynthesis::DISABLED;
1285
1286        if style != FontStyle::Normal && self.style() == FontStyle::Normal {
1287            // if requested oblique or italic and the face is neither.
1288            synth |= FontSynthesis::OBLIQUE;
1289        }
1290        if weight > self.weight() {
1291            // if requested a weight larger then the face weight the renderer can
1292            // add extra stroke outlines to compensate.
1293            synth |= FontSynthesis::BOLD;
1294        }
1295
1296        synth
1297    }
1298
1299    /// If this font face is cached. All font faces are cached by default, a font face can be detached from
1300    /// cache when a [`FONT_CHANGED_EVENT`] event happens, in this case the font can still be used normally, but
1301    /// a request for the same font name will return a different reference.
1302    pub fn is_cached(&self) -> bool {
1303        !self.0.m.lock().unregistered
1304    }
1305
1306    /// CPAL table.
1307    ///
1308    /// Is empty if not provided by the font.
1309    pub fn color_palettes(&self) -> ColorPalettes<'_> {
1310        match self.ttf() {
1311            Some(ttf) => ColorPalettes::new(*ttf.raw_face()),
1312            None => ColorPalettes::empty(),
1313        }
1314    }
1315
1316    /// COLR table.
1317    ///
1318    /// Is empty if not provided by the font.
1319    pub fn color_glyphs(&self) -> ColorGlyphs<'_> {
1320        match self.ttf() {
1321            Some(ttf) => ColorGlyphs::new(*ttf.raw_face()),
1322            None => ColorGlyphs::empty(),
1323        }
1324    }
1325
1326    /// If the font provides glyph substitutions.
1327    pub fn has_ligatures(&self) -> bool {
1328        self.0.flags.contains(FontFaceFlags::HAS_LIGATURES)
1329    }
1330
1331    /// If this font provides custom positioned carets for some or all ligature glyphs.
1332    ///
1333    /// If `true` the [`Font::ligature_caret_offsets`] method can be used to get the caret offsets, otherwise
1334    /// it always returns empty.
1335    pub fn has_ligature_caret_offsets(&self) -> bool {
1336        !self.0.lig_carets.is_empty()
1337    }
1338
1339    /// If this font has bitmap images associated with some glyphs.
1340    pub fn has_raster_images(&self) -> bool {
1341        self.0.flags.contains(FontFaceFlags::HAS_RASTER_IMAGES)
1342    }
1343
1344    /// If this font has SVG images associated with some glyphs.
1345    pub fn has_svg_images(&self) -> bool {
1346        self.0.flags.contains(FontFaceFlags::HAS_SVG_IMAGES)
1347    }
1348}
1349
1350/// A sized font face.
1351///
1352/// A sized font can be requested from a [`FontFace`].
1353///
1354/// This type is a shared reference to the loaded font data, cloning it is cheap.
1355#[derive(Clone)]
1356pub struct Font(Arc<LoadedFont>);
1357struct LoadedFont {
1358    face: FontFace,
1359    size: Px,
1360    variations: RFontVariations,
1361    metrics: FontMetrics,
1362    render_keys: Mutex<Vec<RenderFont>>,
1363    small_word_cache: RwLock<HashMap<WordCacheKey<[u8; Font::SMALL_WORD_LEN]>, ShapedSegmentData>>,
1364    word_cache: RwLock<HashMap<WordCacheKey<String>, ShapedSegmentData>>,
1365}
1366impl fmt::Debug for Font {
1367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1368        f.debug_struct("Font")
1369            .field("face", &self.0.face)
1370            .field("size", &self.0.size)
1371            .field("metrics", &self.0.metrics)
1372            .field("render_keys.len()", &self.0.render_keys.lock().len())
1373            .field("small_word_cache.len()", &self.0.small_word_cache.read().len())
1374            .field("word_cache.len()", &self.0.word_cache.read().len())
1375            .finish()
1376    }
1377}
1378impl PartialEq for Font {
1379    fn eq(&self, other: &Self) -> bool {
1380        Arc::ptr_eq(&self.0, &other.0)
1381    }
1382}
1383impl Eq for Font {}
1384impl Font {
1385    const SMALL_WORD_LEN: usize = 8;
1386
1387    fn to_small_word(s: &str) -> Option<[u8; Self::SMALL_WORD_LEN]> {
1388        if s.len() <= Self::SMALL_WORD_LEN {
1389            let mut a = [b'\0'; Self::SMALL_WORD_LEN];
1390            a[..s.len()].copy_from_slice(s.as_bytes());
1391            Some(a)
1392        } else {
1393            None
1394        }
1395    }
1396
1397    fn new(face: FontFace, size: Px, variations: RFontVariations) -> Self {
1398        Font(Arc::new(LoadedFont {
1399            metrics: face.metrics().sized(size),
1400            face,
1401            size,
1402            variations,
1403            render_keys: Mutex::new(vec![]),
1404            small_word_cache: RwLock::default(),
1405            word_cache: RwLock::default(),
1406        }))
1407    }
1408
1409    fn render_font(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1410        let _span = tracing::trace_span!("Font::render_font").entered();
1411
1412        let mut render_keys = self.0.render_keys.lock();
1413        for r in render_keys.iter() {
1414            if &r.renderer == renderer && r.synthesis == synthesis {
1415                return r.font_id;
1416            }
1417        }
1418
1419        let font_key = self.0.face.render_face(renderer);
1420
1421        let mut opt = zng_view_api::font::FontOptions::default();
1422        opt.synthetic_oblique = synthesis.contains(FontSynthesis::OBLIQUE);
1423        opt.synthetic_bold = synthesis.contains(FontSynthesis::BOLD);
1424        let variations = self.0.variations.iter().map(|v| (v.tag.to_bytes(), v.value)).collect();
1425
1426        let key = match renderer.add_font(font_key, self.0.size, opt, variations) {
1427            Ok(k) => k,
1428            Err(_) => {
1429                tracing::debug!("respawned calling `add_font_instance`, will return dummy font key");
1430                return zng_view_api::font::FontId::INVALID;
1431            }
1432        };
1433
1434        render_keys.push(RenderFont::new(renderer, synthesis, key));
1435
1436        key
1437    }
1438
1439    /// Reference the font face source of this font.
1440    pub fn face(&self) -> &FontFace {
1441        &self.0.face
1442    }
1443
1444    /// Gets the sized harfbuzz font.
1445    pub fn harfbuzz(&self) -> Option<rustybuzz::Face<'_>> {
1446        let ppem = self.0.size.0 as u16;
1447
1448        let mut font = self.0.face.harfbuzz()?;
1449
1450        font.set_pixels_per_em(Some((ppem, ppem)));
1451        font.set_variations(&self.0.variations);
1452
1453        Some(font)
1454    }
1455
1456    /// Font size.
1457    ///
1458    /// This is also the *pixels-per-em* value.
1459    pub fn size(&self) -> Px {
1460        self.0.size
1461    }
1462
1463    /// Custom font variations.
1464    pub fn variations(&self) -> &RFontVariations {
1465        &self.0.variations
1466    }
1467
1468    /// Sized font metrics.
1469    pub fn metrics(&self) -> &FontMetrics {
1470        &self.0.metrics
1471    }
1472
1473    /// Iterate over pixel offsets relative to `lig` glyph start that represents the
1474    /// caret offset for each cluster that is covered by the ligature, after the first.
1475    ///
1476    /// The caret offset for the first cluster is the glyph offset and is not yielded in the iterator. The
1477    /// yielded offsets are relative to the glyph position.
1478    pub fn ligature_caret_offsets(
1479        &self,
1480        lig: zng_view_api::font::GlyphIndex,
1481    ) -> impl ExactSizeIterator<Item = f32> + DoubleEndedIterator + '_ {
1482        let face = &self.0.face.0;
1483        face.lig_carets.carets(lig).iter().map(move |&o| match o {
1484            ligature_util::LigatureCaret::Coordinate(o) => {
1485                let size_scale = 1.0 / face.metrics.units_per_em as f32 * self.0.size.0 as f32;
1486                o as f32 * size_scale
1487            }
1488            ligature_util::LigatureCaret::GlyphContourPoint(i) => {
1489                if let Some(f) = self.harfbuzz() {
1490                    struct Search {
1491                        i: u16,
1492                        s: u16,
1493                        x: f32,
1494                    }
1495                    impl Search {
1496                        fn check(&mut self, x: f32) {
1497                            self.s = self.s.saturating_add(1);
1498                            if self.s == self.i {
1499                                self.x = x;
1500                            }
1501                        }
1502                    }
1503                    impl ttf_parser::OutlineBuilder for Search {
1504                        fn move_to(&mut self, x: f32, _y: f32) {
1505                            self.check(x);
1506                        }
1507
1508                        fn line_to(&mut self, x: f32, _y: f32) {
1509                            self.check(x);
1510                        }
1511
1512                        fn quad_to(&mut self, _x1: f32, _y1: f32, x: f32, _y: f32) {
1513                            self.check(x)
1514                        }
1515
1516                        fn curve_to(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32, x: f32, _y: f32) {
1517                            self.check(x);
1518                        }
1519
1520                        fn close(&mut self) {}
1521                    }
1522                    let mut search = Search { i, s: 0, x: 0.0 };
1523                    if f.outline_glyph(ttf_parser::GlyphId(lig as _), &mut search).is_some() && search.s >= search.i {
1524                        return search.x * self.0.metrics.size_scale;
1525                    }
1526                }
1527                0.0
1528            }
1529        })
1530    }
1531}
1532impl zng_app::render::Font for Font {
1533    fn is_empty_fallback(&self) -> bool {
1534        self.face().is_empty()
1535    }
1536
1537    fn renderer_id(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
1538        self.render_font(renderer, synthesis)
1539    }
1540}
1541
1542/// A list of [`FontFace`] resolved from a [`FontName`] list, plus the [fallback](GenericFonts::fallback) font.
1543///
1544/// Glyphs that are not resolved by the first font fallback to the second font and so on.
1545#[derive(Debug, Clone)]
1546pub struct FontFaceList {
1547    fonts: Box<[FontFace]>,
1548    requested_style: FontStyle,
1549    requested_weight: FontWeight,
1550    requested_stretch: FontStretch,
1551}
1552impl FontFaceList {
1553    /// New list with only the [`FontFace::empty`].
1554    pub fn empty() -> Self {
1555        Self {
1556            fonts: Box::new([FontFace::empty()]),
1557            requested_style: FontStyle::Normal,
1558            requested_weight: FontWeight::NORMAL,
1559            requested_stretch: FontStretch::NORMAL,
1560        }
1561    }
1562
1563    /// Style requested in the query that generated this font face list.
1564    pub fn requested_style(&self) -> FontStyle {
1565        self.requested_style
1566    }
1567
1568    /// Weight requested in the query that generated this font face list.
1569    pub fn requested_weight(&self) -> FontWeight {
1570        self.requested_weight
1571    }
1572
1573    /// Stretch requested in the query that generated this font face list.
1574    pub fn requested_stretch(&self) -> FontStretch {
1575        self.requested_stretch
1576    }
1577
1578    /// The font face that best matches the requested properties.
1579    pub fn best(&self) -> &FontFace {
1580        &self.fonts[0]
1581    }
1582
1583    /// Gets the font synthesis to use to better render the given font face on the list.
1584    pub fn face_synthesis(&self, face_index: usize) -> FontSynthesis {
1585        if let Some(face) = self.fonts.get(face_index) {
1586            face.synthesis_for(self.requested_style, self.requested_weight)
1587        } else {
1588            FontSynthesis::DISABLED
1589        }
1590    }
1591
1592    /// Iterate over font faces, more specific first.
1593    pub fn iter(&self) -> std::slice::Iter<'_, FontFace> {
1594        self.fonts.iter()
1595    }
1596
1597    /// Number of font faces in the list.
1598    ///
1599    /// This is at least `1`, but can be the empty face.
1600    pub fn len(&self) -> usize {
1601        self.fonts.len()
1602    }
1603
1604    /// Is length `1` and only contains the empty face.
1605    pub fn is_empty(&self) -> bool {
1606        self.fonts[0].is_empty() && self.fonts.len() == 1
1607    }
1608
1609    /// Gets a sized font list.
1610    ///
1611    /// This calls [`FontFace::sized`] for each font in the list.
1612    pub fn sized(&self, font_size: Px, variations: RFontVariations) -> FontList {
1613        FontList {
1614            fonts: self.fonts.iter().map(|f| f.sized(font_size, variations.clone())).collect(),
1615            requested_style: self.requested_style,
1616            requested_weight: self.requested_weight,
1617            requested_stretch: self.requested_stretch,
1618        }
1619    }
1620}
1621impl PartialEq for FontFaceList {
1622    /// Both are equal if each point to the same fonts in the same order and have the same requested properties.
1623    fn eq(&self, other: &Self) -> bool {
1624        self.requested_style == other.requested_style
1625            && self.requested_weight == other.requested_weight
1626            && self.requested_stretch == other.requested_stretch
1627            && self.fonts.len() == other.fonts.len()
1628            && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1629    }
1630}
1631impl Eq for FontFaceList {}
1632impl std::ops::Deref for FontFaceList {
1633    type Target = [FontFace];
1634
1635    fn deref(&self) -> &Self::Target {
1636        &self.fonts
1637    }
1638}
1639impl<'a> std::iter::IntoIterator for &'a FontFaceList {
1640    type Item = &'a FontFace;
1641
1642    type IntoIter = std::slice::Iter<'a, FontFace>;
1643
1644    fn into_iter(self) -> Self::IntoIter {
1645        self.iter()
1646    }
1647}
1648impl std::ops::Index<usize> for FontFaceList {
1649    type Output = FontFace;
1650
1651    fn index(&self, index: usize) -> &Self::Output {
1652        &self.fonts[index]
1653    }
1654}
1655
1656/// A list of [`Font`] created from a [`FontFaceList`].
1657#[derive(Debug, Clone)]
1658pub struct FontList {
1659    fonts: Box<[Font]>,
1660    requested_style: FontStyle,
1661    requested_weight: FontWeight,
1662    requested_stretch: FontStretch,
1663}
1664#[expect(clippy::len_without_is_empty)] // cannot be empty.
1665impl FontList {
1666    /// The font that best matches the requested properties.
1667    pub fn best(&self) -> &Font {
1668        &self.fonts[0]
1669    }
1670
1671    /// Font size requested in the query that generated this font list.
1672    pub fn requested_size(&self) -> Px {
1673        self.fonts[0].size()
1674    }
1675
1676    /// Style requested in the query that generated this font list.
1677    pub fn requested_style(&self) -> FontStyle {
1678        self.requested_style
1679    }
1680
1681    /// Weight requested in the query that generated this font list.
1682    pub fn requested_weight(&self) -> FontWeight {
1683        self.requested_weight
1684    }
1685
1686    /// Stretch requested in the query that generated this font list.
1687    pub fn requested_stretch(&self) -> FontStretch {
1688        self.requested_stretch
1689    }
1690
1691    /// Gets the font synthesis to use to better render the given font on the list.
1692    pub fn face_synthesis(&self, font_index: usize) -> FontSynthesis {
1693        if let Some(font) = self.fonts.get(font_index) {
1694            font.0.face.synthesis_for(self.requested_style, self.requested_weight)
1695        } else {
1696            FontSynthesis::DISABLED
1697        }
1698    }
1699
1700    /// Iterate over font faces, more specific first.
1701    pub fn iter(&self) -> std::slice::Iter<'_, Font> {
1702        self.fonts.iter()
1703    }
1704
1705    /// Number of font faces in the list.
1706    ///
1707    /// This is at least `1`.
1708    pub fn len(&self) -> usize {
1709        self.fonts.len()
1710    }
1711
1712    /// Returns `true` is `self` is sized from the `faces` list.
1713    pub fn is_sized_from(&self, faces: &FontFaceList) -> bool {
1714        if self.len() != faces.len() {
1715            return false;
1716        }
1717
1718        for (font, face) in self.iter().zip(faces.iter()) {
1719            if font.face() != face {
1720                return false;
1721            }
1722        }
1723
1724        true
1725    }
1726}
1727impl PartialEq for FontList {
1728    /// Both are equal if each point to the same fonts in the same order and have the same requested properties.
1729    fn eq(&self, other: &Self) -> bool {
1730        self.requested_style == other.requested_style
1731            && self.requested_weight == other.requested_weight
1732            && self.requested_stretch == other.requested_stretch
1733            && self.fonts.len() == other.fonts.len()
1734            && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
1735    }
1736}
1737impl Eq for FontList {}
1738impl std::ops::Deref for FontList {
1739    type Target = [Font];
1740
1741    fn deref(&self) -> &Self::Target {
1742        &self.fonts
1743    }
1744}
1745impl<'a> std::iter::IntoIterator for &'a FontList {
1746    type Item = &'a Font;
1747
1748    type IntoIter = std::slice::Iter<'a, Font>;
1749
1750    fn into_iter(self) -> Self::IntoIter {
1751        self.iter()
1752    }
1753}
1754impl<I: SliceIndex<[Font]>> std::ops::Index<I> for FontList {
1755    type Output = I::Output;
1756
1757    fn index(&self, index: I) -> &I::Output {
1758        &self.fonts[index]
1759    }
1760}
1761
1762struct FontFaceLoader {
1763    custom_fonts: HashMap<FontName, Vec<FontFace>>,
1764    unregister_requests: Vec<(FontName, ResponderVar<bool>)>,
1765
1766    system_fonts_cache: HashMap<FontName, Vec<SystemFontFace>>,
1767    list_cache: HashMap<Box<[FontName]>, Vec<FontFaceListQuery>>,
1768}
1769struct SystemFontFace {
1770    properties: (FontStyle, FontWeight, FontStretch),
1771    result: ResponseVar<Option<FontFace>>,
1772}
1773struct FontFaceListQuery {
1774    properties: (FontStyle, FontWeight, FontStretch),
1775    lang: Lang,
1776    result: ResponseVar<FontFaceList>,
1777}
1778impl FontFaceLoader {
1779    fn new() -> Self {
1780        FontFaceLoader {
1781            custom_fonts: HashMap::new(),
1782            unregister_requests: vec![],
1783            system_fonts_cache: HashMap::new(),
1784            list_cache: HashMap::new(),
1785        }
1786    }
1787
1788    fn on_view_process_respawn(&mut self) {
1789        let sys_fonts = self.system_fonts_cache.values().flatten().filter_map(|f| f.result.rsp().flatten());
1790        for face in self.custom_fonts.values().flatten().cloned().chain(sys_fonts) {
1791            let mut m = face.0.m.lock();
1792            m.render_ids.clear();
1793            for inst in m.instances.values() {
1794                inst.0.render_keys.lock().clear();
1795            }
1796        }
1797    }
1798
1799    fn on_refresh(&mut self) {
1800        for (_, sys_family) in self.system_fonts_cache.drain() {
1801            for sys_font in sys_family {
1802                sys_font.result.with(|r| {
1803                    if let Some(Some(face)) = r.done() {
1804                        face.on_refresh();
1805                    }
1806                });
1807            }
1808        }
1809    }
1810    fn on_prune(&mut self) {
1811        self.system_fonts_cache.retain(|_, v| {
1812            v.retain(|sff| {
1813                if sff.result.strong_count() == 1 {
1814                    sff.result.with(|r| {
1815                        match r.done() {
1816                            Some(Some(face)) => Arc::strong_count(&face.0) > 1, // face shared
1817                            Some(None) => false,                                // loading for no one
1818                            None => true,                                       // retain not found
1819                        }
1820                    })
1821                } else {
1822                    // response var shared
1823                    true
1824                }
1825            });
1826            !v.is_empty()
1827        });
1828
1829        self.list_cache.clear();
1830    }
1831
1832    fn register(custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
1833        // start loading
1834        let resp = task::respond(FontFace::load_custom(custom_font));
1835
1836        // modify loader.custom_fonts at the end of whatever update is happening when finishes loading.
1837        resp.hook(|args| {
1838            if let Some(done) = args.value().done() {
1839                if let Ok(face) = done {
1840                    let mut fonts = FONTS_SV.write();
1841                    let family = fonts.loader.custom_fonts.entry(face.0.family_name.clone()).or_default();
1842                    let existing = family
1843                        .iter()
1844                        .position(|f| f.0.weight == face.0.weight && f.0.style == face.0.style && f.0.stretch == face.0.stretch);
1845
1846                    if let Some(i) = existing {
1847                        family[i] = face.clone();
1848                    } else {
1849                        family.push(face.clone());
1850                    }
1851
1852                    FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
1853                }
1854                false
1855            } else {
1856                true
1857            }
1858        })
1859        .perm();
1860        resp
1861    }
1862
1863    fn unregister(&mut self, custom_family: FontName) -> ResponseVar<bool> {
1864        let (responder, response) = response_var();
1865
1866        if !self.unregister_requests.is_empty() {
1867            UPDATES.update(None);
1868        }
1869        self.unregister_requests.push((custom_family, responder));
1870
1871        response
1872    }
1873
1874    fn try_list(
1875        &self,
1876        families: &[FontName],
1877        style: FontStyle,
1878        weight: FontWeight,
1879        stretch: FontStretch,
1880        lang: &Lang,
1881    ) -> Option<ResponseVar<FontFaceList>> {
1882        if let Some(queries) = self.list_cache.get(families) {
1883            for q in queries {
1884                if q.properties == (style, weight, stretch) && &q.lang == lang {
1885                    return Some(q.result.clone());
1886                }
1887            }
1888        }
1889        None
1890    }
1891
1892    fn load_list(
1893        &mut self,
1894        families: &[FontName],
1895        style: FontStyle,
1896        weight: FontWeight,
1897        stretch: FontStretch,
1898        lang: &Lang,
1899    ) -> ResponseVar<FontFaceList> {
1900        if let Some(r) = self.try_list(families, style, weight, stretch, lang) {
1901            return r;
1902        }
1903
1904        let mut list = Vec::with_capacity(families.len() + 1);
1905        let mut pending = vec![];
1906
1907        {
1908            let fallback = [GenericFonts {}.fallback(lang)];
1909            let mut used = HashSet::with_capacity(families.len());
1910            for name in families.iter().chain(&fallback) {
1911                if !used.insert(name) {
1912                    continue;
1913                }
1914
1915                let face = self.load(name, style, weight, stretch, lang);
1916                if face.is_done() {
1917                    if let Some(face) = face.rsp().unwrap() {
1918                        list.push(face);
1919                    }
1920                } else {
1921                    pending.push((list.len(), face));
1922                }
1923            }
1924        }
1925
1926        let r = if pending.is_empty() {
1927            if list.is_empty() {
1928                tracing::error!(target: "font_loading", "failed to load fallback font");
1929                list.push(FontFace::empty());
1930            }
1931            response_done_var(FontFaceList {
1932                fonts: list.into_boxed_slice(),
1933                requested_style: style,
1934                requested_weight: weight,
1935                requested_stretch: stretch,
1936            })
1937        } else {
1938            task::respond(async move {
1939                for (i, pending) in pending.into_iter().rev() {
1940                    if let Some(rsp) = pending.wait_rsp().await {
1941                        list.insert(i, rsp);
1942                    }
1943                }
1944
1945                if list.is_empty() {
1946                    tracing::error!(target: "font_loading", "failed to load fallback font");
1947                    list.push(FontFace::empty());
1948                }
1949
1950                FontFaceList {
1951                    fonts: list.into_boxed_slice(),
1952                    requested_style: style,
1953                    requested_weight: weight,
1954                    requested_stretch: stretch,
1955                }
1956            })
1957        };
1958
1959        self.list_cache
1960            .entry(families.iter().cloned().collect())
1961            .or_insert_with(|| Vec::with_capacity(1))
1962            .push(FontFaceListQuery {
1963                properties: (style, weight, stretch),
1964                lang: lang.clone(),
1965                result: r.clone(),
1966            });
1967
1968        r
1969    }
1970
1971    fn try_cached(
1972        &self,
1973        font_name: &FontName,
1974        style: FontStyle,
1975        weight: FontWeight,
1976        stretch: FontStretch,
1977        lang: &Lang,
1978    ) -> Option<ResponseVar<Option<FontFace>>> {
1979        let resolved = GenericFonts {}.resolve(font_name, lang);
1980        let font_name = resolved.as_ref().unwrap_or(font_name);
1981        self.try_resolved(font_name, style, weight, stretch)
1982    }
1983
1984    /// Try cached again, otherwise begins loading and inserts the response in the cache.
1985    fn load(
1986        &mut self,
1987        font_name: &FontName,
1988        style: FontStyle,
1989        weight: FontWeight,
1990        stretch: FontStretch,
1991        lang: &Lang,
1992    ) -> ResponseVar<Option<FontFace>> {
1993        let resolved = GenericFonts {}.resolve(font_name, lang);
1994        let font_name = resolved.as_ref().unwrap_or(font_name);
1995        self.load_resolved(font_name, style, weight, stretch)
1996    }
1997
1998    /// Get a `font_name` that already resolved generic names if it is already in cache.
1999    fn try_resolved(
2000        &self,
2001        font_name: &FontName,
2002        style: FontStyle,
2003        weight: FontWeight,
2004        stretch: FontStretch,
2005    ) -> Option<ResponseVar<Option<FontFace>>> {
2006        if let Some(custom_family) = self.custom_fonts.get(font_name) {
2007            let custom = Self::match_custom(custom_family, style, weight, stretch);
2008            return Some(response_done_var(Some(custom)));
2009        }
2010
2011        if let Some(cached_sys_family) = self.system_fonts_cache.get(font_name) {
2012            for sys_face in cached_sys_family.iter() {
2013                if sys_face.properties == (style, weight, stretch) {
2014                    return Some(sys_face.result.clone());
2015                }
2016            }
2017        }
2018
2019        None
2020    }
2021
2022    /// Load a `font_name` that already resolved generic names.
2023    fn load_resolved(
2024        &mut self,
2025        font_name: &FontName,
2026        style: FontStyle,
2027        weight: FontWeight,
2028        stretch: FontStretch,
2029    ) -> ResponseVar<Option<FontFace>> {
2030        if let Some(cached) = self.try_resolved(font_name, style, weight, stretch) {
2031            return cached;
2032        }
2033
2034        let load = task::wait(clmv!(font_name, || {
2035            let (bytes, face_index) = match Self::get_system(&font_name, style, weight, stretch) {
2036                Some(h) => h,
2037                None => {
2038                    #[cfg(debug_assertions)]
2039                    static NOT_FOUND: Mutex<Option<HashSet<FontName>>> = Mutex::new(None);
2040
2041                    #[cfg(debug_assertions)]
2042                    if NOT_FOUND.lock().get_or_insert_with(HashSet::default).insert(font_name.clone()) {
2043                        tracing::debug!(r#"font "{font_name}" not found"#);
2044                    }
2045
2046                    return None;
2047                }
2048            };
2049            match FontFace::load(bytes, face_index) {
2050                Ok(f) => Some(f),
2051                Err(FontLoadingError::UnknownFormat) => None,
2052                Err(e) => {
2053                    tracing::error!(target: "font_loading", "failed to load system font, {e}\nquery: {:?}", (font_name, style, weight, stretch));
2054                    None
2055                }
2056            }
2057        }));
2058        let result = task::respond(async_clmv!(font_name, {
2059            match task::with_deadline(load, 10.secs()).await {
2060                Ok(r) => r,
2061                Err(_) => {
2062                    tracing::error!(target: "font_loading", "timeout loading {font_name:?}");
2063                    None
2064                }
2065            }
2066        }));
2067
2068        self.system_fonts_cache
2069            .entry(font_name.clone())
2070            .or_insert_with(|| Vec::with_capacity(1))
2071            .push(SystemFontFace {
2072                properties: (style, weight, stretch),
2073                result: result.clone(),
2074            });
2075
2076        result
2077    }
2078
2079    fn get_system(font_name: &FontName, style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Option<(FontBytes, u32)> {
2080        let _span = tracing::trace_span!("FontFaceLoader::get_system").entered();
2081        match query_util::best(font_name, style, weight, stretch) {
2082            Ok(r) => r,
2083            Err(e) => {
2084                tracing::error!("cannot get `{font_name}` system font, {e}");
2085                None
2086            }
2087        }
2088    }
2089
2090    fn match_custom(faces: &[FontFace], style: FontStyle, weight: FontWeight, stretch: FontStretch) -> FontFace {
2091        if faces.len() == 1 {
2092            // it is common for custom font names to only have one face.
2093            return faces[0].clone();
2094        }
2095
2096        let mut set = Vec::with_capacity(faces.len());
2097        let mut set_dist = 0.0f64; // stretch distance of current set if it is not empty.
2098
2099        // # Filter Stretch
2100        //
2101        // Closest to query stretch, if the query is narrow, closest narrow then
2102        // closest wide, if the query is wide the reverse.
2103        let wrong_side = if stretch <= FontStretch::NORMAL {
2104            |s| s > FontStretch::NORMAL
2105        } else {
2106            |s| s <= FontStretch::NORMAL
2107        };
2108        for face in faces {
2109            let mut dist = (face.stretch().0 - stretch.0).abs() as f64;
2110            if wrong_side(face.stretch()) {
2111                dist += f32::MAX as f64 + 1.0;
2112            }
2113
2114            if set.is_empty() {
2115                set.push(face);
2116                set_dist = dist;
2117            } else if dist < set_dist {
2118                // better candidate found, restart closest set.
2119                set_dist = dist;
2120                set.clear();
2121                set.push(face);
2122            } else if (dist - set_dist).abs() < 0.0001 {
2123                // another candidate, same distance.
2124                set.push(face);
2125            }
2126        }
2127        if set.len() == 1 {
2128            return set[0].clone();
2129        }
2130
2131        // # Filter Style
2132        //
2133        // Each query style has a fallback preference, we retain the faces that have the best
2134        // style given the query preference.
2135        let style_pref = match style {
2136            FontStyle::Normal => [FontStyle::Normal, FontStyle::Oblique, FontStyle::Italic],
2137            FontStyle::Italic => [FontStyle::Italic, FontStyle::Oblique, FontStyle::Normal],
2138            FontStyle::Oblique => [FontStyle::Oblique, FontStyle::Italic, FontStyle::Normal],
2139        };
2140        let mut best_style = style_pref.len();
2141        for face in &set {
2142            let i = style_pref.iter().position(|&s| s == face.style()).unwrap();
2143            if i < best_style {
2144                best_style = i;
2145            }
2146        }
2147        set.retain(|f| f.style() == style_pref[best_style]);
2148        if set.len() == 1 {
2149            return set[0].clone();
2150        }
2151
2152        // # Filter Weight
2153        //
2154        // a: under 400 query matches query then descending under query then ascending over query.
2155        // b: over 500 query matches query then ascending over query then descending under query.
2156        //
2157        // c: in 400..=500 query matches query then ascending to 500 then descending under query
2158        //     then ascending over 500.
2159        let add_penalty = if weight.0 >= 400.0 && weight.0 <= 500.0 {
2160            // c:
2161            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2162                // Add penalty for:
2163                if face.weight() < weight {
2164                    // Not being in search up to 500
2165                    *dist += 100.0;
2166                } else if face.weight().0 > 500.0 {
2167                    // Not being in search down to 0
2168                    *dist += 600.0;
2169                }
2170            }
2171        } else if weight.0 < 400.0 {
2172            // a:
2173            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2174                if face.weight() > weight {
2175                    *dist += weight.0 as f64;
2176                }
2177            }
2178        } else {
2179            debug_assert!(weight.0 > 500.0);
2180            // b:
2181            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
2182                if face.weight() < weight {
2183                    *dist += f32::MAX as f64;
2184                }
2185            }
2186        };
2187
2188        let mut best = set[0];
2189        let mut best_dist = f64::MAX;
2190
2191        for face in &set {
2192            let mut dist = (face.weight().0 - weight.0).abs() as f64;
2193
2194            add_penalty(face, weight, &mut dist);
2195
2196            if dist < best_dist {
2197                best_dist = dist;
2198                best = face;
2199            }
2200        }
2201
2202        best.clone()
2203    }
2204}
2205
2206struct RenderFontFace {
2207    renderer: ViewRenderer,
2208    face_id: zng_view_api::font::FontFaceId,
2209}
2210impl RenderFontFace {
2211    fn new(renderer: &ViewRenderer, face_id: zng_view_api::font::FontFaceId) -> Self {
2212        RenderFontFace {
2213            renderer: renderer.clone(),
2214            face_id,
2215        }
2216    }
2217}
2218impl Drop for RenderFontFace {
2219    fn drop(&mut self) {
2220        // error here means the entire renderer was already dropped.
2221        let _ = self.renderer.delete_font_face(self.face_id);
2222    }
2223}
2224
2225struct RenderFont {
2226    renderer: ViewRenderer,
2227    synthesis: FontSynthesis,
2228    font_id: zng_view_api::font::FontId,
2229}
2230impl RenderFont {
2231    fn new(renderer: &ViewRenderer, synthesis: FontSynthesis, font_id: zng_view_api::font::FontId) -> RenderFont {
2232        RenderFont {
2233            renderer: renderer.clone(),
2234            synthesis,
2235            font_id,
2236        }
2237    }
2238}
2239impl Drop for RenderFont {
2240    fn drop(&mut self) {
2241        // error here means the entire renderer was already dropped.
2242        let _ = self.renderer.delete_font(self.font_id);
2243    }
2244}
2245
2246app_local! {
2247    static GENERIC_FONTS_SV: GenericFontsService = GenericFontsService::new();
2248}
2249
2250struct GenericFontsService {
2251    serif: LangMap<FontName>,
2252    sans_serif: LangMap<FontName>,
2253    monospace: LangMap<FontName>,
2254    cursive: LangMap<FontName>,
2255    fantasy: LangMap<FontName>,
2256    fallback: LangMap<FontName>,
2257
2258    requests: Vec<Box<dyn FnOnce(&mut GenericFontsService) + Send + Sync>>,
2259}
2260impl GenericFontsService {
2261    fn new() -> Self {
2262        fn default(name: impl Into<FontName>) -> LangMap<FontName> {
2263            let mut f = LangMap::with_capacity(1);
2264            f.insert(lang!(und), name.into());
2265            f
2266        }
2267
2268        let serif = "serif";
2269        let sans_serif = "sans-serif";
2270        let monospace = "monospace";
2271        let cursive = "cursive";
2272        let fantasy = "fantasy";
2273        let fallback = if cfg!(windows) {
2274            "Segoe UI Symbol"
2275        } else if cfg!(target_os = "linux") {
2276            "Standard Symbols PS"
2277        } else {
2278            "sans-serif"
2279        };
2280
2281        GenericFontsService {
2282            serif: default(serif),
2283            sans_serif: default(sans_serif),
2284            monospace: default(monospace),
2285            cursive: default(cursive),
2286            fantasy: default(fantasy),
2287
2288            fallback: default(fallback),
2289
2290            requests: vec![],
2291        }
2292    }
2293}
2294
2295/// Generic fonts configuration for the app.
2296///
2297/// This type can be accessed from the [`FONTS`] service.
2298///
2299/// # Defaults
2300///
2301/// By default the `serif`, `sans_serif`, `monospace`, `cursive` and `fantasy` are set to their own generic name,
2302/// this delegates the resolution to the operating system.
2303///
2304/// The default `fallback` font is "Segoe UI Symbol" for Windows, "Standard Symbols PS" for Linux and "sans-serif" for others.
2305///
2306/// See also [`FontNames::system_ui`] for the default font selection for UIs.
2307///
2308/// [`FontNames::system_ui`]: crate::FontNames::system_ui
2309#[non_exhaustive]
2310pub struct GenericFonts {}
2311macro_rules! impl_fallback_accessors {
2312    ($($name:ident=$name_str:tt),+ $(,)?) => {$($crate::paste! {
2313    #[doc = "Gets the fallback *"$name_str "* font for the given language."]
2314    ///
2315    /// Returns a font name for the best `lang` match.
2316    ///
2317    #[doc = "Note that the returned name can still be the generic `\""$name_str "\"`, this delegates the resolution to the operating system."]
2318
2319    pub fn $name(&self, lang: &Lang) -> FontName {
2320        GENERIC_FONTS_SV.read().$name.get(lang).unwrap().clone()
2321    }
2322
2323    #[doc = "Sets the fallback *"$name_str "* font for the given language."]
2324    ///
2325    /// The change applied for the next update.
2326    ///
2327    /// Use `lang!(und)` to set name used when no language matches.
2328    pub fn [<set_ $name>]<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2329        let mut g = GENERIC_FONTS_SV.write();
2330        let font_name = font_name.into();
2331        if g.requests.is_empty() {
2332            UPDATES.update(None);
2333        }
2334        g.requests.push(Box::new(move |g| {
2335            g.$name.insert(lang.clone(), font_name);
2336            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::GenericFont(FontName::$name(), lang)));
2337        }));
2338    }
2339    })+};
2340}
2341impl GenericFonts {
2342    #[rustfmt::skip] // for zng fmt
2343    impl_fallback_accessors! {
2344        serif="serif", sans_serif="sans-serif", monospace="monospace", cursive="cursive", fantasy="fantasy"
2345    }
2346
2347    /// Gets the ultimate fallback font used when none of the other fonts support a glyph.
2348    ///
2349    /// Returns a font name.
2350    pub fn fallback(&self, lang: &Lang) -> FontName {
2351        GENERIC_FONTS_SV.read().fallback.get(lang).unwrap().clone()
2352    }
2353
2354    /// Sets the ultimate fallback font used when none of other fonts support a glyph.
2355    ///
2356    /// The change applies for the next update.
2357    ///
2358    /// Use `lang!(und)` to set name used when no language matches.
2359    pub fn set_fallback<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
2360        let mut g = GENERIC_FONTS_SV.write();
2361        if g.requests.is_empty() {
2362            UPDATES.update(None);
2363        }
2364        let font_name = font_name.into();
2365        g.requests.push(Box::new(move |g| {
2366            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Fallback(lang.clone())));
2367            g.fallback.insert(lang, font_name);
2368        }));
2369    }
2370
2371    /// Returns the font name registered for the generic `name` and `lang`.
2372    ///
2373    /// Returns `None` if `name` if not one of the generic font names.
2374    pub fn resolve(&self, name: &FontName, lang: &Lang) -> Option<FontName> {
2375        if name == &FontName::serif() {
2376            Some(self.serif(lang))
2377        } else if name == &FontName::sans_serif() {
2378            Some(self.sans_serif(lang))
2379        } else if name == &FontName::monospace() {
2380            Some(self.monospace(lang))
2381        } else if name == &FontName::cursive() {
2382            Some(self.cursive(lang))
2383        } else if name == &FontName::fantasy() {
2384            Some(self.fantasy(lang))
2385        } else {
2386            None
2387        }
2388    }
2389}
2390
2391#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2392pub(crate) enum WeakFontBytes {
2393    Ipc(std::sync::Weak<IpcBytes>),
2394    Arc(std::sync::Weak<Vec<u8>>),
2395    Static(&'static [u8]),
2396    Mmap(std::sync::Weak<FontBytesMmap>),
2397}
2398#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2399impl WeakFontBytes {
2400    pub(crate) fn upgrade(&self) -> Option<FontBytes> {
2401        match self {
2402            WeakFontBytes::Ipc(weak) => Some(FontBytes(FontBytesImpl::Ipc(weak.upgrade()?))),
2403            WeakFontBytes::Arc(weak) => Some(FontBytes(FontBytesImpl::Arc(weak.upgrade()?))),
2404            WeakFontBytes::Static(b) => Some(FontBytes(FontBytesImpl::Static(b))),
2405            WeakFontBytes::Mmap(weak) => Some(FontBytes(FontBytesImpl::Mmap(weak.upgrade()?))),
2406        }
2407    }
2408
2409    pub(crate) fn strong_count(&self) -> usize {
2410        match self {
2411            WeakFontBytes::Ipc(weak) => weak.strong_count(),
2412            WeakFontBytes::Arc(weak) => weak.strong_count(),
2413            WeakFontBytes::Static(_) => 1,
2414            WeakFontBytes::Mmap(weak) => weak.strong_count(),
2415        }
2416    }
2417}
2418
2419#[cfg(not(target_arch = "wasm32"))]
2420struct FontBytesMmap {
2421    path: std::path::PathBuf,
2422    _read_lock: std::fs::File,
2423    mmap: memmap2::Mmap,
2424}
2425
2426#[derive(Clone)]
2427enum FontBytesImpl {
2428    /// IpcBytes already clones references, but we need the weak_count for caching
2429    Ipc(Arc<IpcBytes>),
2430    Arc(Arc<Vec<u8>>),
2431    Static(&'static [u8]),
2432    #[cfg(not(target_arch = "wasm32"))]
2433    Mmap(Arc<FontBytesMmap>),
2434}
2435/// Reference to in memory font data.
2436#[derive(Clone)]
2437pub struct FontBytes(FontBytesImpl);
2438impl FontBytes {
2439    /// From shared memory that can be efficiently referenced in the view-process for rendering.
2440    pub fn from_ipc(bytes: IpcBytes) -> Self {
2441        Self(FontBytesImpl::Ipc(Arc::new(bytes)))
2442    }
2443
2444    /// Moves data to an [`IpcBytes`] shared reference.
2445    pub fn from_vec(bytes: Vec<u8>) -> Self {
2446        Self(FontBytesImpl::Ipc(Arc::new(IpcBytes::from_vec(bytes))))
2447    }
2448
2449    /// Uses the reference in the app-process. In case the font needs to be send to view-process turns into [`IpcBytes`].
2450    pub fn from_static(bytes: &'static [u8]) -> Self {
2451        Self(FontBytesImpl::Static(bytes))
2452    }
2453
2454    /// Uses the reference in the app-process. In case the font needs to be send to view-process turns into [`IpcBytes`].
2455    ///
2456    /// Prefer `from_ipc` if you can control the data creation.
2457    pub fn from_arc(bytes: Arc<Vec<u8>>) -> Self {
2458        Self(FontBytesImpl::Arc(bytes))
2459    }
2460
2461    /// If the `path` is in the restricted system fonts directory memory maps it. Otherwise reads into [`IpcBytes`].
2462    pub fn from_file(path: PathBuf) -> std::io::Result<Self> {
2463        let path = dunce::canonicalize(path)?;
2464
2465        #[cfg(windows)]
2466        {
2467            use windows::Win32::{Foundation::MAX_PATH, System::SystemInformation::GetSystemWindowsDirectoryW};
2468            let mut buffer = [0u16; MAX_PATH as usize];
2469            // SAFETY: Buffer allocated to max possible
2470            let len = unsafe { GetSystemWindowsDirectoryW(Some(&mut buffer)) };
2471            let fonts_dir = String::from_utf16_lossy(&buffer[..len as usize]);
2472            // usually this is: r"C:\Windows\Fonts"
2473            if path.starts_with(fonts_dir) {
2474                // SAFETY: Windows restricts write access to files in this directory.
2475                return unsafe { Self::from_file_mmap(path) };
2476            }
2477        }
2478        #[cfg(target_os = "macos")]
2479        if path.starts_with("/System/Library/Fonts/") || path.starts_with("/Library/Fonts/") {
2480            // SAFETY: macOS restricts write access to files in this directory.
2481            return unsafe { Self::from_file_mmap(path) };
2482        }
2483        #[cfg(target_os = "android")]
2484        if path.starts_with("/system/fonts/") || path.starts_with("/system/font/") || path.starts_with("/system/product/fonts/") {
2485            // SAFETY: Android restricts write access to files in this directory.
2486            return unsafe { Self::from_file_mmap(path) };
2487        }
2488        #[cfg(unix)]
2489        if path.starts_with("/usr/share/fonts/") {
2490            // SAFETY: OS restricts write access to files in this directory.
2491            return unsafe { Self::from_file_mmap(path) };
2492        }
2493
2494        std::fs::read(path).map(Self::from_vec)
2495    }
2496
2497    /// Read lock the `path` and memory maps it.
2498    ///
2499    /// # Safety
2500    ///
2501    /// You must ensure the file content does not change. If the file has the same access restrictions as the
2502    /// current executable file you can say it is safe.
2503    #[cfg(not(target_arch = "wasm32"))]
2504    pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2505        let file = std::fs::File::open(&path)?;
2506        file.try_lock_shared()?;
2507        let mmap = unsafe { memmap2::Mmap::map(&file) }?;
2508        Ok(Self(FontBytesImpl::Mmap(Arc::new(FontBytesMmap {
2509            path,
2510            _read_lock: file,
2511            mmap,
2512        }))))
2513    }
2514
2515    /// No memory map in wasm, just reads the file.
2516    #[cfg(target_arch = "wasm32")]
2517    pub unsafe fn from_file_mmap(path: PathBuf) -> std::io::Result<Self> {
2518        Self::from_file(path)
2519    }
2520
2521    /// File path, if the bytes are memory mapped.
2522    ///
2523    /// Note that the path is read-locked until all clones of `FontBytes` are dropped.
2524    #[cfg(not(target_arch = "wasm32"))]
2525    pub fn mmap_path(&self) -> Option<&Path> {
2526        if let FontBytesImpl::Mmap(m) = &self.0 {
2527            Some(&m.path)
2528        } else {
2529            None
2530        }
2531    }
2532    /// Always `None` in WASM
2533    #[cfg(target_arch = "wasm32")]
2534    pub fn mmap_path(&self) -> Option<&Path> {
2535        None
2536    }
2537
2538    /// Clone [`IpcBytes`] reference or clone data into a new one.
2539    #[cfg(not(target_arch = "wasm32"))]
2540    pub fn to_ipc(&self) -> IpcFontBytes {
2541        if let FontBytesImpl::Mmap(m) = &self.0 {
2542            IpcFontBytes::System(m.path.clone())
2543        } else {
2544            IpcFontBytes::Bytes(self.to_ipc_bytes())
2545        }
2546    }
2547    #[cfg(target_arch = "wasm32")]
2548    /// Clone [`IpcBytes`] reference or clone data into a new one.
2549    pub fn to_ipc(&self) -> IpcFontBytes {
2550        IpcFontBytes::Bytes(self.to_ipc_bytes())
2551    }
2552
2553    /// Clone [`IpcBytes`] reference or clone data into a new one.
2554    pub fn to_ipc_bytes(&self) -> IpcBytes {
2555        match &self.0 {
2556            FontBytesImpl::Ipc(b) => (**b).clone(),
2557            FontBytesImpl::Arc(b) => IpcBytes::from_vec((**b).clone()),
2558            FontBytesImpl::Static(b) => IpcBytes::from_slice(b),
2559            #[cfg(not(target_arch = "wasm32"))]
2560            FontBytesImpl::Mmap(m) => IpcBytes::from_slice(&m.mmap[..]),
2561        }
2562    }
2563
2564    #[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
2565    pub(crate) fn downgrade(&self) -> WeakFontBytes {
2566        match &self.0 {
2567            FontBytesImpl::Ipc(arc) => WeakFontBytes::Ipc(Arc::downgrade(arc)),
2568            FontBytesImpl::Arc(arc) => WeakFontBytes::Arc(Arc::downgrade(arc)),
2569            FontBytesImpl::Static(b) => WeakFontBytes::Static(b),
2570            #[cfg(not(target_arch = "wasm32"))]
2571            FontBytesImpl::Mmap(arc) => WeakFontBytes::Mmap(Arc::downgrade(arc)),
2572        }
2573    }
2574}
2575impl std::ops::Deref for FontBytes {
2576    type Target = [u8];
2577
2578    fn deref(&self) -> &Self::Target {
2579        match &self.0 {
2580            FontBytesImpl::Ipc(b) => &b[..],
2581            FontBytesImpl::Arc(b) => &b[..],
2582            FontBytesImpl::Static(b) => b,
2583            #[cfg(not(target_arch = "wasm32"))]
2584            FontBytesImpl::Mmap(m) => &m.mmap[..],
2585        }
2586    }
2587}
2588impl fmt::Debug for FontBytes {
2589    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2590        let mut b = f.debug_struct("FontBytes");
2591        b.field(
2592            ".kind",
2593            &match &self.0 {
2594                FontBytesImpl::Ipc(_) => "IpcBytes",
2595                FontBytesImpl::Arc(_) => "Arc",
2596                FontBytesImpl::Static(_) => "Static",
2597                #[cfg(not(target_arch = "wasm32"))]
2598                FontBytesImpl::Mmap { .. } => "Mmap",
2599            },
2600        );
2601        b.field(".len", &self.len().bytes());
2602        #[cfg(not(target_arch = "wasm32"))]
2603        if let FontBytesImpl::Mmap(m) = &self.0 {
2604            b.field(".path", &m.path);
2605        }
2606
2607        b.finish()
2608    }
2609}
2610
2611#[derive(Debug, Clone)]
2612enum FontSource {
2613    File(PathBuf, u32),
2614    Memory(FontBytes, u32),
2615    Alias(FontName),
2616}
2617
2618/// Custom font builder.
2619#[derive(Debug, Clone)]
2620pub struct CustomFont {
2621    name: FontName,
2622    source: FontSource,
2623    stretch: FontStretch,
2624    style: FontStyle,
2625    weight: FontWeight,
2626}
2627impl CustomFont {
2628    /// A custom font loaded from a file.
2629    ///
2630    /// If the file is a collection of fonts, `font_index` determines which, otherwise just pass `0`.
2631    ///
2632    /// The font is loaded in [`FONTS.register`].
2633    ///
2634    /// [`FONTS.register`]: FONTS::register
2635    pub fn from_file<N: Into<FontName>, P: Into<PathBuf>>(name: N, path: P, font_index: u32) -> Self {
2636        CustomFont {
2637            name: name.into(),
2638            source: FontSource::File(path.into(), font_index),
2639            stretch: FontStretch::NORMAL,
2640            style: FontStyle::Normal,
2641            weight: FontWeight::NORMAL,
2642        }
2643    }
2644
2645    /// A custom font loaded from a shared byte slice.
2646    ///
2647    /// If the font data is a collection of fonts, `font_index` determines which, otherwise just pass `0`.
2648    ///
2649    /// The font is loaded in [`FONTS.register`].
2650    ///
2651    /// [`FONTS.register`]: FONTS::register
2652    pub fn from_bytes<N: Into<FontName>>(name: N, data: FontBytes, font_index: u32) -> Self {
2653        CustomFont {
2654            name: name.into(),
2655            source: FontSource::Memory(data, font_index),
2656            stretch: FontStretch::NORMAL,
2657            style: FontStyle::Normal,
2658            weight: FontWeight::NORMAL,
2659        }
2660    }
2661
2662    /// A custom font that maps to another font.
2663    ///
2664    /// The font is loaded in [`FONTS.register`].
2665    ///
2666    /// [`FONTS.register`]: FONTS::register
2667    pub fn from_other<N: Into<FontName>, O: Into<FontName>>(name: N, other_font: O) -> Self {
2668        CustomFont {
2669            name: name.into(),
2670            source: FontSource::Alias(other_font.into()),
2671            stretch: FontStretch::NORMAL,
2672            style: FontStyle::Normal,
2673            weight: FontWeight::NORMAL,
2674        }
2675    }
2676
2677    /// Set the [`FontStretch`].
2678    ///
2679    /// Default is [`FontStretch::NORMAL`].
2680    pub fn stretch(mut self, stretch: FontStretch) -> Self {
2681        self.stretch = stretch;
2682        self
2683    }
2684
2685    /// Set the [`FontStyle`].
2686    ///
2687    /// Default is [`FontStyle::Normal`].
2688    pub fn style(mut self, style: FontStyle) -> Self {
2689        self.style = style;
2690        self
2691    }
2692
2693    /// Set the [`FontWeight`].
2694    ///
2695    /// Default is [`FontWeight::NORMAL`].
2696    pub fn weight(mut self, weight: FontWeight) -> Self {
2697        self.weight = weight;
2698        self
2699    }
2700}
2701
2702/// The width of a font as an approximate fraction of the normal width.
2703///
2704/// Widths range from 0.5 to 2.0 inclusive, with 1.0 as the normal width.
2705#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Transitionable)]
2706#[serde(transparent)]
2707pub struct FontStretch(pub f32);
2708impl fmt::Debug for FontStretch {
2709    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2710        let name = self.name();
2711        if name.is_empty() {
2712            f.debug_tuple("FontStretch").field(&self.0).finish()
2713        } else {
2714            if f.alternate() {
2715                write!(f, "FontStretch::")?;
2716            }
2717            write!(f, "{name}")
2718        }
2719    }
2720}
2721impl PartialOrd for FontStretch {
2722    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2723        Some(self.cmp(other))
2724    }
2725}
2726impl Ord for FontStretch {
2727    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2728        about_eq_ord(self.0, other.0, EQ_GRANULARITY)
2729    }
2730}
2731impl PartialEq for FontStretch {
2732    fn eq(&self, other: &Self) -> bool {
2733        about_eq(self.0, other.0, EQ_GRANULARITY)
2734    }
2735}
2736impl Eq for FontStretch {}
2737impl std::hash::Hash for FontStretch {
2738    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2739        about_eq_hash(self.0, EQ_GRANULARITY, state)
2740    }
2741}
2742impl Default for FontStretch {
2743    fn default() -> FontStretch {
2744        FontStretch::NORMAL
2745    }
2746}
2747impl FontStretch {
2748    /// Ultra-condensed width (50%), the narrowest possible.
2749    pub const ULTRA_CONDENSED: FontStretch = FontStretch(0.5);
2750    /// Extra-condensed width (62.5%).
2751    pub const EXTRA_CONDENSED: FontStretch = FontStretch(0.625);
2752    /// Condensed width (75%).
2753    pub const CONDENSED: FontStretch = FontStretch(0.75);
2754    /// Semi-condensed width (87.5%).
2755    pub const SEMI_CONDENSED: FontStretch = FontStretch(0.875);
2756    /// Normal width (100%).
2757    pub const NORMAL: FontStretch = FontStretch(1.0);
2758    /// Semi-expanded width (112.5%).
2759    pub const SEMI_EXPANDED: FontStretch = FontStretch(1.125);
2760    /// Expanded width (125%).
2761    pub const EXPANDED: FontStretch = FontStretch(1.25);
2762    /// Extra-expanded width (150%).
2763    pub const EXTRA_EXPANDED: FontStretch = FontStretch(1.5);
2764    /// Ultra-expanded width (200%), the widest possible.
2765    pub const ULTRA_EXPANDED: FontStretch = FontStretch(2.0);
2766
2767    /// Gets the const name, if this value is one of the constants.
2768    pub fn name(self) -> &'static str {
2769        macro_rules! name {
2770            ($($CONST:ident;)+) => {$(
2771                if self == Self::$CONST {
2772                    return stringify!($CONST);
2773                }
2774            )+}
2775        }
2776        name! {
2777            ULTRA_CONDENSED;
2778            EXTRA_CONDENSED;
2779            CONDENSED;
2780            SEMI_CONDENSED;
2781            NORMAL;
2782            SEMI_EXPANDED;
2783            EXPANDED;
2784            EXTRA_EXPANDED;
2785            ULTRA_EXPANDED;
2786        }
2787        ""
2788    }
2789}
2790impl_from_and_into_var! {
2791    fn from(fct: Factor) -> FontStretch {
2792        FontStretch(fct.0)
2793    }
2794    fn from(pct: FactorPercent) -> FontStretch {
2795        FontStretch(pct.fct().0)
2796    }
2797    fn from(fct: f32) -> FontStretch {
2798        FontStretch(fct)
2799    }
2800}
2801impl From<ttf_parser::Width> for FontStretch {
2802    fn from(value: ttf_parser::Width) -> Self {
2803        use ttf_parser::Width::*;
2804        match value {
2805            UltraCondensed => FontStretch::ULTRA_CONDENSED,
2806            ExtraCondensed => FontStretch::EXTRA_CONDENSED,
2807            Condensed => FontStretch::CONDENSED,
2808            SemiCondensed => FontStretch::SEMI_CONDENSED,
2809            Normal => FontStretch::NORMAL,
2810            SemiExpanded => FontStretch::SEMI_EXPANDED,
2811            Expanded => FontStretch::EXPANDED,
2812            ExtraExpanded => FontStretch::EXTRA_EXPANDED,
2813            UltraExpanded => FontStretch::ULTRA_EXPANDED,
2814        }
2815    }
2816}
2817impl From<FontStretch> for ttf_parser::Width {
2818    fn from(value: FontStretch) -> Self {
2819        if value <= FontStretch::ULTRA_CONDENSED {
2820            ttf_parser::Width::UltraCondensed
2821        } else if value <= FontStretch::EXTRA_CONDENSED {
2822            ttf_parser::Width::ExtraCondensed
2823        } else if value <= FontStretch::CONDENSED {
2824            ttf_parser::Width::Condensed
2825        } else if value <= FontStretch::SEMI_CONDENSED {
2826            ttf_parser::Width::SemiCondensed
2827        } else if value <= FontStretch::NORMAL {
2828            ttf_parser::Width::Normal
2829        } else if value <= FontStretch::SEMI_EXPANDED {
2830            ttf_parser::Width::SemiExpanded
2831        } else if value <= FontStretch::EXPANDED {
2832            ttf_parser::Width::Expanded
2833        } else if value <= FontStretch::EXTRA_EXPANDED {
2834            ttf_parser::Width::ExtraExpanded
2835        } else {
2836            ttf_parser::Width::UltraExpanded
2837        }
2838    }
2839}
2840
2841/// The italic or oblique form of a font.
2842#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
2843pub enum FontStyle {
2844    /// The regular form.
2845    #[default]
2846    Normal,
2847    /// A form that is generally cursive in nature.
2848    Italic,
2849    /// A skewed version of the regular form.
2850    Oblique,
2851}
2852impl fmt::Debug for FontStyle {
2853    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2854        if f.alternate() {
2855            write!(f, "FontStyle::")?;
2856        }
2857        match self {
2858            Self::Normal => write!(f, "Normal"),
2859            Self::Italic => write!(f, "Italic"),
2860            Self::Oblique => write!(f, "Oblique"),
2861        }
2862    }
2863}
2864impl From<ttf_parser::Style> for FontStyle {
2865    fn from(value: ttf_parser::Style) -> Self {
2866        use ttf_parser::Style::*;
2867        match value {
2868            Normal => FontStyle::Normal,
2869            Italic => FontStyle::Italic,
2870            Oblique => FontStyle::Oblique,
2871        }
2872    }
2873}
2874
2875impl From<FontStyle> for ttf_parser::Style {
2876    fn from(value: FontStyle) -> Self {
2877        match value {
2878            FontStyle::Normal => Self::Normal,
2879            FontStyle::Italic => Self::Italic,
2880            FontStyle::Oblique => Self::Oblique,
2881        }
2882    }
2883}
2884
2885/// The degree of stroke thickness of a font. This value ranges from 100.0 to 900.0,
2886/// with 400.0 as normal.
2887#[derive(Clone, Copy, Transitionable, serde::Serialize, serde::Deserialize)]
2888pub struct FontWeight(pub f32);
2889impl Default for FontWeight {
2890    fn default() -> FontWeight {
2891        FontWeight::NORMAL
2892    }
2893}
2894impl fmt::Debug for FontWeight {
2895    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2896        let name = self.name();
2897        if name.is_empty() {
2898            f.debug_tuple("FontWeight").field(&self.0).finish()
2899        } else {
2900            if f.alternate() {
2901                write!(f, "FontWeight::")?;
2902            }
2903            write!(f, "{name}")
2904        }
2905    }
2906}
2907impl PartialOrd for FontWeight {
2908    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
2909        Some(self.cmp(other))
2910    }
2911}
2912impl Ord for FontWeight {
2913    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
2914        about_eq_ord(self.0, other.0, EQ_GRANULARITY_100)
2915    }
2916}
2917impl PartialEq for FontWeight {
2918    fn eq(&self, other: &Self) -> bool {
2919        about_eq(self.0, other.0, EQ_GRANULARITY_100)
2920    }
2921}
2922impl Eq for FontWeight {}
2923impl std::hash::Hash for FontWeight {
2924    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2925        about_eq_hash(self.0, EQ_GRANULARITY_100, state)
2926    }
2927}
2928impl FontWeight {
2929    /// Thin weight (100), the thinnest value.
2930    pub const THIN: FontWeight = FontWeight(100.0);
2931    /// Extra light weight (200).
2932    pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
2933    /// Light weight (300).
2934    pub const LIGHT: FontWeight = FontWeight(300.0);
2935    /// Normal (400).
2936    pub const NORMAL: FontWeight = FontWeight(400.0);
2937    /// Medium weight (500, higher than normal).
2938    pub const MEDIUM: FontWeight = FontWeight(500.0);
2939    /// Semi-bold weight (600).
2940    pub const SEMIBOLD: FontWeight = FontWeight(600.0);
2941    /// Bold weight (700).
2942    pub const BOLD: FontWeight = FontWeight(700.0);
2943    /// Extra-bold weight (800).
2944    pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
2945    /// Black weight (900), the thickest value.
2946    pub const BLACK: FontWeight = FontWeight(900.0);
2947
2948    /// Gets the const name, if this value is one of the constants.
2949    pub fn name(self) -> &'static str {
2950        macro_rules! name {
2951                ($($CONST:ident;)+) => {$(
2952                    if self == Self::$CONST {
2953                        return stringify!($CONST);
2954                    }
2955                )+}
2956            }
2957        name! {
2958            THIN;
2959            EXTRA_LIGHT;
2960            LIGHT;
2961            NORMAL;
2962            MEDIUM;
2963            SEMIBOLD;
2964            BOLD;
2965            EXTRA_BOLD;
2966            BLACK;
2967        }
2968        ""
2969    }
2970}
2971impl_from_and_into_var! {
2972    fn from(weight: u32) -> FontWeight {
2973        FontWeight(weight as f32)
2974    }
2975    fn from(weight: f32) -> FontWeight {
2976        FontWeight(weight)
2977    }
2978}
2979impl From<ttf_parser::Weight> for FontWeight {
2980    fn from(value: ttf_parser::Weight) -> Self {
2981        use ttf_parser::Weight::*;
2982        match value {
2983            Thin => FontWeight::THIN,
2984            ExtraLight => FontWeight::EXTRA_LIGHT,
2985            Light => FontWeight::LIGHT,
2986            Normal => FontWeight::NORMAL,
2987            Medium => FontWeight::MEDIUM,
2988            SemiBold => FontWeight::SEMIBOLD,
2989            Bold => FontWeight::BOLD,
2990            ExtraBold => FontWeight::EXTRA_BOLD,
2991            Black => FontWeight::BLACK,
2992            Other(o) => FontWeight(o as f32),
2993        }
2994    }
2995}
2996impl From<FontWeight> for ttf_parser::Weight {
2997    fn from(value: FontWeight) -> Self {
2998        ttf_parser::Weight::from(value.0 as u16)
2999    }
3000}
3001
3002/// Configuration of text wrapping for Chinese, Japanese, or Korean text.
3003#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3004pub enum LineBreak {
3005    /// The same rule used by other languages.
3006    Auto,
3007    /// The least restrictive rule, good for short lines.
3008    Loose,
3009    /// The most common rule.
3010    Normal,
3011    /// The most stringent rule.
3012    Strict,
3013    /// Allow line breaks in between any character including punctuation.
3014    Anywhere,
3015}
3016impl Default for LineBreak {
3017    /// [`LineBreak::Auto`]
3018    fn default() -> Self {
3019        LineBreak::Auto
3020    }
3021}
3022impl fmt::Debug for LineBreak {
3023    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3024        if f.alternate() {
3025            write!(f, "LineBreak::")?;
3026        }
3027        match self {
3028            LineBreak::Auto => write!(f, "Auto"),
3029            LineBreak::Loose => write!(f, "Loose"),
3030            LineBreak::Normal => write!(f, "Normal"),
3031            LineBreak::Strict => write!(f, "Strict"),
3032            LineBreak::Anywhere => write!(f, "Anywhere"),
3033        }
3034    }
3035}
3036
3037/// Definition of how text is split into paragraphs.
3038///
3039/// In the core text shaping this affects paragraph spacing and indent. Rich text widgets
3040/// may also use this when defining their own paragraph segmentation.
3041#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3042#[non_exhaustive]
3043pub enum ParagraphBreak {
3044    /// The entire text is a single paragraph.
3045    #[default]
3046    None,
3047    /// Each actual line is a paragraph. That is `\n` is the paragraph break.
3048    Line,
3049}
3050impl fmt::Debug for ParagraphBreak {
3051    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3052        if f.alternate() {
3053            write!(f, "ParagraphBreak::")?;
3054        }
3055        match self {
3056            ParagraphBreak::None => write!(f, "None"),
3057            ParagraphBreak::Line => write!(f, "Line"),
3058        }
3059    }
3060}
3061
3062/// Hyphenation mode.
3063#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3064pub enum Hyphens {
3065    /// Hyphens are never inserted in word breaks.
3066    None,
3067    /// Word breaks only happen in specially marked break characters: `-` and `\u{00AD} SHY`.
3068    ///
3069    /// * `U+2010` - The visible hyphen character.
3070    /// * `U+00AD` - The invisible hyphen character, is made visible in a word break.
3071    Manual,
3072    /// Hyphens are inserted like `Manual` and also using language specific hyphenation rules.
3073    Auto,
3074}
3075impl Default for Hyphens {
3076    /// [`Hyphens::Auto`]
3077    fn default() -> Self {
3078        Hyphens::Auto
3079    }
3080}
3081impl fmt::Debug for Hyphens {
3082    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3083        if f.alternate() {
3084            write!(f, "Hyphens::")?;
3085        }
3086        match self {
3087            Hyphens::None => write!(f, "None"),
3088            Hyphens::Manual => write!(f, "Manual"),
3089            Hyphens::Auto => write!(f, "Auto"),
3090        }
3091    }
3092}
3093
3094/// Configure line breaks inside words during text wrap.
3095///
3096/// This value is only considered if it is impossible to fit a full word to a line.
3097///
3098/// Hyphens can be inserted in word breaks using the [`Hyphens`] configuration.
3099#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3100pub enum WordBreak {
3101    /// Line breaks can be inserted in between letters of Chinese/Japanese/Korean text only.
3102    Normal,
3103    /// Line breaks can be inserted between any letter.
3104    BreakAll,
3105    /// Line breaks are not inserted between any letter.
3106    KeepAll,
3107}
3108impl Default for WordBreak {
3109    /// [`WordBreak::Normal`]
3110    fn default() -> Self {
3111        WordBreak::Normal
3112    }
3113}
3114impl fmt::Debug for WordBreak {
3115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3116        if f.alternate() {
3117            write!(f, "WordBreak::")?;
3118        }
3119        match self {
3120            WordBreak::Normal => write!(f, "Normal"),
3121            WordBreak::BreakAll => write!(f, "BreakAll"),
3122            WordBreak::KeepAll => write!(f, "KeepAll"),
3123        }
3124    }
3125}
3126
3127/// Text alignment justification mode.
3128#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
3129pub enum Justify {
3130    /// Selects the justification mode based on the language.
3131    ///
3132    /// For Chinese/Japanese/Korean uses `InterLetter` for the others uses `InterWord`.
3133    Auto,
3134    /// The text is justified by adding space between words.
3135    InterWord,
3136    /// The text is justified by adding space between letters.
3137    InterLetter,
3138}
3139impl Default for Justify {
3140    /// [`Justify::Auto`]
3141    fn default() -> Self {
3142        Justify::Auto
3143    }
3144}
3145impl Justify {
3146    /// Resolve `Auto` for the given language.
3147    pub fn resolve(self, lang: &Lang) -> Self {
3148        match self {
3149            Self::Auto => match lang.language.as_str() {
3150                "zh" | "ja" | "ko" => Self::InterLetter,
3151                _ => Self::InterWord,
3152            },
3153            m => m,
3154        }
3155    }
3156}
3157impl fmt::Debug for Justify {
3158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3159        if f.alternate() {
3160            write!(f, "Justify::")?;
3161        }
3162        match self {
3163            Justify::Auto => write!(f, "Auto"),
3164            Justify::InterWord => write!(f, "InterWord"),
3165            Justify::InterLetter => write!(f, "InterLetter"),
3166        }
3167    }
3168}
3169
3170/// Various metrics that apply to the entire [`FontFace`].
3171///
3172/// For OpenType fonts, these mostly come from the `OS/2` table.
3173///
3174/// See the [`FreeType Glyph Metrics`] documentation for an explanation of the various metrics.
3175///
3176/// [`FreeType Glyph Metrics`]: https://freetype.org/freetype2/docs/glyphs/glyphs-3.html
3177#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3178#[non_exhaustive]
3179pub struct FontFaceMetrics {
3180    /// The number of font units per em.
3181    ///
3182    /// Font sizes are usually expressed in pixels per em; e.g. `12px` means 12 pixels per em.
3183    pub units_per_em: u32,
3184
3185    /// The maximum amount the font rises above the baseline, in font units.
3186    pub ascent: f32,
3187
3188    /// The maximum amount the font descends below the baseline, in font units.
3189    ///
3190    /// This is typically a negative value to match the definition of `sTypoDescender` in the
3191    /// `OS/2` table in the OpenType specification. If you are used to using Windows or Mac APIs,
3192    /// beware, as the sign is reversed from what those APIs return.
3193    pub descent: f32,
3194
3195    /// Distance between baselines, in font units.
3196    pub line_gap: f32,
3197
3198    /// The suggested distance of the top of the underline from the baseline (negative values
3199    /// indicate below baseline), in font units.
3200    pub underline_position: f32,
3201
3202    /// A suggested value for the underline thickness, in font units.
3203    pub underline_thickness: f32,
3204
3205    /// The approximate amount that uppercase letters rise above the baseline, in font units.
3206    pub cap_height: f32,
3207
3208    /// The approximate amount that non-ascending lowercase letters rise above the baseline, in
3209    /// font units.
3210    pub x_height: f32,
3211
3212    /// A rectangle that surrounds all bounding boxes of all glyphs, in font units.
3213    ///
3214    /// This corresponds to the `xMin`/`xMax`/`yMin`/`yMax` values in the OpenType `head` table.
3215    pub bounds: euclid::Rect<f32, ()>,
3216}
3217impl FontFaceMetrics {
3218    /// Compute [`FontMetrics`] given a font size in pixels.
3219    pub fn sized(&self, font_size_px: Px) -> FontMetrics {
3220        let size_scale = 1.0 / self.units_per_em as f32 * font_size_px.0 as f32;
3221        let s = move |f: f32| Px((f * size_scale).round() as i32);
3222        FontMetrics {
3223            size_scale,
3224            ascent: s(self.ascent),
3225            descent: s(self.descent),
3226            line_gap: s(self.line_gap),
3227            underline_position: s(self.underline_position),
3228            underline_thickness: s(self.underline_thickness),
3229            cap_height: s(self.cap_height),
3230            x_height: (s(self.x_height)),
3231            bounds: {
3232                let b = self.bounds;
3233                PxRect::new(
3234                    PxPoint::new(s(b.origin.x), s(b.origin.y)),
3235                    PxSize::new(s(b.size.width), s(b.size.height)),
3236                )
3237            },
3238        }
3239    }
3240}
3241
3242/// Various metrics about a [`Font`].
3243///
3244/// You can compute these metrics from a [`FontFaceMetrics`]
3245#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
3246#[non_exhaustive]
3247pub struct FontMetrics {
3248    /// Multiply this to a font EM value to get the size in pixels.
3249    pub size_scale: f32,
3250
3251    /// The maximum amount the font rises above the baseline, in pixels.
3252    pub ascent: Px,
3253
3254    /// The maximum amount the font descends below the baseline, in pixels.
3255    ///
3256    /// This is typically a negative value to match the definition of `sTypoDescender` in the
3257    /// `OS/2` table in the OpenType specification. If you are used to using Windows or Mac APIs,
3258    /// beware, as the sign is reversed from what those APIs return.
3259    pub descent: Px,
3260
3261    /// Distance between baselines, in pixels.
3262    pub line_gap: Px,
3263
3264    /// The suggested distance of the top of the underline from the baseline (negative values
3265    /// indicate below baseline), in pixels.
3266    pub underline_position: Px,
3267
3268    /// A suggested value for the underline thickness, in pixels.
3269    pub underline_thickness: Px,
3270
3271    /// The approximate amount that uppercase letters rise above the baseline, in pixels.
3272    pub cap_height: Px,
3273
3274    /// The approximate amount that non-ascending lowercase letters rise above the baseline, in pixels.
3275    pub x_height: Px,
3276
3277    /// A rectangle that surrounds all bounding boxes of all glyphs, in pixels.
3278    ///
3279    /// This corresponds to the `xMin`/`xMax`/`yMin`/`yMax` values in the OpenType `head` table.
3280    pub bounds: PxRect,
3281}
3282impl FontMetrics {
3283    /// The font line height.
3284    pub fn line_height(&self) -> Px {
3285        self.ascent - self.descent + self.line_gap
3286    }
3287}
3288
3289/// Text transform function.
3290#[derive(Clone)]
3291pub enum TextTransformFn {
3292    /// No transform.
3293    None,
3294    /// To UPPERCASE.
3295    Uppercase,
3296    /// to lowercase.
3297    Lowercase,
3298    /// Custom transform function.
3299    Custom(Arc<dyn Fn(&Txt) -> Cow<Txt> + Send + Sync>),
3300}
3301impl TextTransformFn {
3302    /// Apply the text transform.
3303    ///
3304    /// Returns [`Cow::Owned`] if the text was changed.
3305    pub fn transform<'t>(&self, text: &'t Txt) -> Cow<'t, Txt> {
3306        match self {
3307            TextTransformFn::None => Cow::Borrowed(text),
3308            TextTransformFn::Uppercase => {
3309                if text.chars().any(|c| !c.is_uppercase()) {
3310                    Cow::Owned(text.to_uppercase().into())
3311                } else {
3312                    Cow::Borrowed(text)
3313                }
3314            }
3315            TextTransformFn::Lowercase => {
3316                if text.chars().any(|c| !c.is_lowercase()) {
3317                    Cow::Owned(text.to_lowercase().into())
3318                } else {
3319                    Cow::Borrowed(text)
3320                }
3321            }
3322            TextTransformFn::Custom(fn_) => fn_(text),
3323        }
3324    }
3325
3326    /// New [`Custom`](Self::Custom).
3327    pub fn custom(fn_: impl Fn(&Txt) -> Cow<Txt> + Send + Sync + 'static) -> Self {
3328        TextTransformFn::Custom(Arc::new(fn_))
3329    }
3330}
3331impl fmt::Debug for TextTransformFn {
3332    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3333        if f.alternate() {
3334            write!(f, "TextTransformFn::")?;
3335        }
3336        match self {
3337            TextTransformFn::None => write!(f, "None"),
3338            TextTransformFn::Uppercase => write!(f, "Uppercase"),
3339            TextTransformFn::Lowercase => write!(f, "Lowercase"),
3340            TextTransformFn::Custom(_) => write!(f, "Custom"),
3341        }
3342    }
3343}
3344impl PartialEq for TextTransformFn {
3345    fn eq(&self, other: &Self) -> bool {
3346        match (self, other) {
3347            (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
3348            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3349        }
3350    }
3351}
3352
3353/// Text white space transform.
3354#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
3355pub enum WhiteSpace {
3356    // TODO(breaking) non_exhaustive and merge line breaks (unless empty line)
3357    /// Text is not changed, all white spaces and line breaks are preserved.
3358    Preserve,
3359    /// Replace white spaces with a single `U+0020 SPACE` and trim lines. Line breaks are preserved.
3360    Merge,
3361    /// Replace white spaces and line breaks with `U+0020 SPACE` and trim the text.
3362    MergeAll,
3363}
3364impl Default for WhiteSpace {
3365    /// [`WhiteSpace::Preserve`].
3366    fn default() -> Self {
3367        WhiteSpace::Preserve
3368    }
3369}
3370impl WhiteSpace {
3371    /// Transform the white space of the text.
3372    ///
3373    /// Returns [`Cow::Owned`] if the text was changed.
3374    pub fn transform(self, text: &Txt) -> Cow<'_, Txt> {
3375        match self {
3376            WhiteSpace::Preserve => Cow::Borrowed(text),
3377            WhiteSpace::Merge => {
3378                let is_white_space = |c: char| c.is_whitespace() && !"\n\r\u{85}".contains(c);
3379                let t = text.trim_matches(is_white_space);
3380
3381                let mut prev_space = false;
3382                for c in t.chars() {
3383                    if is_white_space(c) {
3384                        if prev_space || c != '\u{20}' {
3385                            // collapse spaces or replace non ' ' white space with ' '.
3386
3387                            let mut r = String::new();
3388                            let mut sep = "";
3389                            for part in t.split(is_white_space).filter(|s| !s.is_empty()) {
3390                                r.push_str(sep);
3391                                r.push_str(part);
3392                                sep = "\u{20}";
3393                            }
3394                            return Cow::Owned(Txt::from_str(&r));
3395                        } else {
3396                            prev_space = true;
3397                        }
3398                    } else {
3399                        prev_space = false;
3400                    }
3401                }
3402
3403                if t.len() != text.len() {
3404                    Cow::Owned(Txt::from_str(t))
3405                } else {
3406                    Cow::Borrowed(text)
3407                }
3408            }
3409            WhiteSpace::MergeAll => {
3410                let t = text.trim();
3411
3412                let mut prev_space = false;
3413                for c in t.chars() {
3414                    if c.is_whitespace() {
3415                        if prev_space || c != '\u{20}' {
3416                            // collapse spaces or replace non ' ' white space with ' '.
3417
3418                            let mut r = String::new();
3419                            let mut sep = "";
3420                            for part in t.split_whitespace() {
3421                                r.push_str(sep);
3422                                r.push_str(part);
3423                                sep = "\u{20}";
3424                            }
3425                            return Cow::Owned(Txt::from_str(&r));
3426                        } else {
3427                            prev_space = true;
3428                        }
3429                    } else {
3430                        prev_space = false;
3431                    }
3432                }
3433
3434                if t.len() != text.len() {
3435                    Cow::Owned(Txt::from_str(t))
3436                } else {
3437                    Cow::Borrowed(text)
3438                }
3439            }
3440        }
3441    }
3442}
3443impl fmt::Debug for WhiteSpace {
3444    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3445        if f.alternate() {
3446            write!(f, "WhiteSpace::")?;
3447        }
3448        match self {
3449            WhiteSpace::Preserve => write!(f, "Preserve"),
3450            WhiteSpace::Merge => write!(f, "Merge"),
3451            WhiteSpace::MergeAll => write!(f, "MergeAll"),
3452        }
3453    }
3454}
3455
3456/// Defines an insert offset in a shaped text.
3457#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
3458pub struct CaretIndex {
3459    /// Char byte offset in the full text.
3460    ///
3461    /// This index can be computed using the [`SegmentedText`].
3462    pub index: usize,
3463    /// Line index in the shaped text.
3464    ///
3465    /// This value is only used to disambiguate between the *end* of a wrap and
3466    /// the *start* of the next, the text itself does not have any line
3467    /// break but visually the user interacts with two lines. Note that this
3468    /// counts wrap lines, and that this value is not required to define a valid
3469    /// CaretIndex.
3470    ///
3471    /// This index can be computed using the [`ShapedText::snap_caret_line`].
3472    pub line: usize,
3473}
3474
3475impl PartialEq for CaretIndex {
3476    fn eq(&self, other: &Self) -> bool {
3477        self.index == other.index
3478    }
3479}
3480impl Eq for CaretIndex {}
3481impl CaretIndex {
3482    /// First position.
3483    pub const ZERO: CaretIndex = CaretIndex { index: 0, line: 0 };
3484}
3485impl PartialOrd for CaretIndex {
3486    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3487        Some(self.cmp(other))
3488    }
3489}
3490impl Ord for CaretIndex {
3491    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3492        self.index.cmp(&other.index)
3493    }
3494}
3495
3496/// Reasons why a loader might fail to load a font.
3497#[derive(Debug, Clone)]
3498#[non_exhaustive]
3499pub enum FontLoadingError {
3500    /// The data was of a format the loader didn't recognize.
3501    UnknownFormat,
3502    /// Attempted to load an invalid index in a TrueType or OpenType font collection.
3503    ///
3504    /// For example, if a `.ttc` file has 2 fonts in it, and you ask for the 5th one, you'll get
3505    /// this error.
3506    NoSuchFontInCollection,
3507    /// Attempted to load a malformed or corrupted font.
3508    Parse(ttf_parser::FaceParsingError),
3509    /// Attempted to load a font from the filesystem, but there is no filesystem (e.g. in
3510    /// WebAssembly).
3511    NoFilesystem,
3512    /// A disk or similar I/O error occurred while attempting to load the font.
3513    Io(Arc<std::io::Error>),
3514}
3515impl PartialEq for FontLoadingError {
3516    fn eq(&self, other: &Self) -> bool {
3517        match (self, other) {
3518            (Self::Io(l0), Self::Io(r0)) => Arc::ptr_eq(l0, r0),
3519            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
3520        }
3521    }
3522}
3523impl From<std::io::Error> for FontLoadingError {
3524    fn from(error: std::io::Error) -> FontLoadingError {
3525        Self::Io(Arc::new(error))
3526    }
3527}
3528impl fmt::Display for FontLoadingError {
3529    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3530        match self {
3531            Self::UnknownFormat => write!(f, "unknown format"),
3532            Self::NoSuchFontInCollection => write!(f, "no such font in the collection"),
3533            Self::NoFilesystem => write!(f, "no filesystem present"),
3534            Self::Parse(e) => fmt::Display::fmt(e, f),
3535            Self::Io(e) => fmt::Display::fmt(e, f),
3536        }
3537    }
3538}
3539impl std::error::Error for FontLoadingError {
3540    fn cause(&self) -> Option<&dyn std::error::Error> {
3541        match self {
3542            FontLoadingError::Parse(e) => Some(e),
3543            FontLoadingError::Io(e) => Some(e),
3544            _ => None,
3545        }
3546    }
3547}
3548
3549#[cfg(test)]
3550mod tests {
3551    use zng_app::APP;
3552
3553    use super::*;
3554
3555    #[test]
3556    fn generic_fonts_default() {
3557        let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
3558
3559        assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(und)))
3560    }
3561
3562    #[test]
3563    fn generic_fonts_fallback() {
3564        let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
3565
3566        assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(en_US)));
3567        assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(es)));
3568    }
3569
3570    #[test]
3571    fn generic_fonts_get1() {
3572        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3573        GenericFonts {}.set_sans_serif(lang!(en_US), "Test Value");
3574        app.update(false).assert_wait();
3575
3576        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3577        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3578    }
3579
3580    #[test]
3581    fn generic_fonts_get2() {
3582        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3583        GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3584        app.update(false).assert_wait();
3585
3586        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
3587        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3588    }
3589
3590    #[test]
3591    fn generic_fonts_get_best() {
3592        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3593        GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
3594        GenericFonts {}.set_sans_serif(lang!(en_US), "Best");
3595        app.update(false).assert_wait();
3596
3597        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Best");
3598        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
3599        assert_eq!(&GenericFonts {}.sans_serif(&lang!("und")), "sans-serif");
3600    }
3601
3602    #[test]
3603    fn generic_fonts_get_no_lang_match() {
3604        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
3605        GenericFonts {}.set_sans_serif(lang!(es_US), "Test Value");
3606        app.update(false).assert_wait();
3607
3608        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "sans-serif");
3609        assert_eq!(&GenericFonts {}.sans_serif(&lang!("es")), "Test Value");
3610    }
3611}