#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
#![allow(clippy::type_complexity)]
#![warn(unused_extern_crates)]
#![warn(missing_docs)]
use font_features::RFontVariations;
use hashbrown::{HashMap, HashSet};
use std::{borrow::Cow, fmt, ops, path::PathBuf, rc::Rc, slice::SliceIndex, sync::Arc};
#[macro_use]
extern crate bitflags;
pub mod font_features;
mod match_util;
mod emoji_util;
pub use emoji_util::*;
mod ligature_util;
use ligature_util::*;
mod unicode_bidi_util;
mod segmenting;
pub use segmenting::*;
mod shaping;
pub use shaping::*;
use zng_clone_move::{async_clmv, clmv};
mod hyphenation;
pub use self::hyphenation::*;
mod font_kit_cache;
use font_kit_cache::*;
mod unit;
pub use unit::*;
use parking_lot::{Mutex, RwLock};
use paste::paste;
use zng_app::{
    event::{event, event_args},
    render::FontSynthesis,
    update::{EventUpdate, UPDATES},
    view_process::{
        raw_events::{RAW_FONT_AA_CHANGED_EVENT, RAW_FONT_CHANGED_EVENT},
        ViewRenderer, VIEW_PROCESS_INITED_EVENT,
    },
    AppExtension,
};
use zng_app_context::app_local;
use zng_ext_l10n::{lang, Lang, LangMap};
use zng_layout::unit::{
    about_eq, about_eq_hash, about_eq_ord, euclid, Factor, FactorPercent, Px, PxPoint, PxRect, PxSize, TimeUnits as _, EQ_EPSILON,
    EQ_EPSILON_100,
};
use zng_task as task;
use zng_txt::Txt;
use zng_var::{
    animation::Transitionable, impl_from_and_into_var, response_done_var, response_var, var, AnyVar, ArcVar, IntoVar, LocalVar,
    ResponderVar, ResponseVar, Var,
};
use zng_view_api::{config::FontAntiAliasing, ViewProcessOffline};
#[derive(Clone)]
pub struct FontName {
    text: Txt,
    is_ascii: bool,
}
impl fmt::Debug for FontName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_struct("FontName")
                .field("text", &self.text)
                .field("is_ascii", &self.is_ascii)
                .finish()
        } else {
            write!(f, "{:?}", self.text)
        }
    }
}
impl PartialEq for FontName {
    fn eq(&self, other: &Self) -> bool {
        self.unicase() == other.unicase()
    }
}
impl Eq for FontName {}
impl PartialEq<str> for FontName {
    fn eq(&self, other: &str) -> bool {
        self.unicase() == unicase::UniCase::<&str>::from(other)
    }
}
impl std::hash::Hash for FontName {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::hash::Hash::hash(&self.unicase(), state)
    }
}
impl FontName {
    fn unicase(&self) -> unicase::UniCase<&str> {
        if self.is_ascii {
            unicase::UniCase::ascii(self)
        } else {
            unicase::UniCase::unicode(self)
        }
    }
    pub const fn from_static(name: &'static str) -> Self {
        FontName {
            text: Txt::from_static(name),
            is_ascii: {
                let name_bytes = name.as_bytes();
                let mut i = name_bytes.len();
                let mut is_ascii = true;
                while i > 0 {
                    i -= 1;
                    if !name_bytes[i].is_ascii() {
                        is_ascii = false;
                        break;
                    }
                }
                is_ascii
            },
        }
    }
    pub fn new(name: impl Into<Txt>) -> Self {
        let text = name.into();
        FontName {
            is_ascii: text.is_ascii(),
            text,
        }
    }
    pub fn serif() -> Self {
        Self::new("serif")
    }
    pub fn sans_serif() -> Self {
        Self::new("sans-serif")
    }
    pub fn monospace() -> Self {
        Self::new("monospace")
    }
    pub fn cursive() -> Self {
        Self::new("cursive")
    }
    pub fn fantasy() -> Self {
        Self::new("fantasy")
    }
    pub fn name(&self) -> &str {
        &self.text
    }
    pub fn into_text(self) -> Txt {
        self.text
    }
}
impl_from_and_into_var! {
    fn from(s: &'static str) -> FontName {
        FontName::new(s)
    }
    fn from(s: String) -> FontName {
        FontName::new(s)
    }
    fn from(s: Cow<'static, str>) -> FontName {
        FontName::new(s)
    }
    fn from(f: FontName) -> Txt {
        f.into_text()
    }
}
impl fmt::Display for FontName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.name())
    }
}
impl std::ops::Deref for FontName {
    type Target = str;
    fn deref(&self) -> &Self::Target {
        self.text.deref()
    }
}
impl AsRef<str> for FontName {
    fn as_ref(&self) -> &str {
        self.text.as_ref()
    }
}
impl serde::Serialize for FontName {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.text.serialize(serializer)
    }
}
impl<'de> serde::Deserialize<'de> for FontName {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Txt::deserialize(deserializer).map(FontName::new)
    }
}
#[derive(Eq, PartialEq, Hash, Clone, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct FontNames(pub Vec<FontName>);
impl FontNames {
    pub fn empty() -> Self {
        FontNames(vec![])
    }
    pub fn windows_ui(lang: &Lang) -> Self {
        if lang!("zh-Hans").matches(lang, true, false) {
            ["Segoe UI", "Microsoft YaHei", "Segoe Ui Emoji", "sans-serif"].into()
        } else if lang!("zh-Hant").matches(lang, true, false) {
            ["Segoe UI", "Microsoft Jhenghei", "Segoe Ui Emoji", "sans-serif"].into()
        } else if lang!(ja).matches(lang, true, false) {
            ["Segoe UI", "Yu Gothic UI", "Meiryo UI", "Segoe Ui Emoji", "sans-serif"].into()
        } else if lang!(ko).matches(lang, true, false) {
            ["Segoe UI", "Malgun Gothic", "Dotom", "Segoe Ui Emoji", "sans-serif"].into()
        } else {
            ["Segoe UI", "Segoe Ui Emoji", "sans-serif"].into()
        }
    }
    pub fn mac_ui(lang: &Lang) -> Self {
        if lang!("zh-Hans").matches(lang, true, false) {
            [
                "-apple-system",
                "PingFang SC",
                "Hiragino Sans GB",
                "Apple Color Emoji",
                "sans-serif",
            ]
            .into()
        } else if lang!("zh-Hant").matches(lang, true, false) {
            ["-apple-system", "PingFang TC", "Apple Color Emoji", "sans-serif"].into()
        } else if lang!(ja).matches(lang, true, false) {
            ["-apple-system", "Hiragino Kaku Gothic Pro", "Apple Color Emoji", "sans-serif"].into()
        } else if lang!(ko).matches(lang, true, false) {
            [
                "-apple-system",
                "Nanum Gothic",
                "Apple SD Gothic Neo",
                "AppleGothic",
                "Apple Color Emoji",
                "sans-serif",
            ]
            .into()
        } else {
            ["-apple-system", "Apple Color Emoji", "sans-serif"].into()
        }
    }
    pub fn linux_ui(lang: &Lang) -> Self {
        if lang!("zh-Hans").matches(lang, true, false) {
            [
                "Ubuntu",
                "Droid Sans",
                "Source Han Sans SC",
                "Source Han Sans CN",
                "Source Han Sans",
                "Noto Color Emoji",
                "sans-serif",
            ]
            .into()
        } else if lang!("zh-Hant").matches(lang, true, false) {
            [
                "Ubuntu",
                "Droid Sans",
                "Source Han Sans TC",
                "Source Han Sans TW",
                "Source Han Sans",
                "Noto Color Emoji",
                "sans-serif",
            ]
            .into()
        } else if lang!(ja).matches(lang, true, false) {
            [
                "system-ui",
                "Ubuntu",
                "Droid Sans",
                "Source Han Sans J",
                "Source Han Sans JP",
                "Source Han Sans",
                "Noto Color Emoji",
                "sans-serif",
            ]
            .into()
        } else if lang!(ko).matches(lang, true, false) {
            [
                "system-ui",
                "Ubuntu",
                "Droid Sans",
                "Source Han Sans K",
                "Source Han Sans JR",
                "Source Han Sans",
                "UnDotum",
                "FBaekmuk Gulim",
                "Noto Color Emoji",
                "sans-serif",
            ]
            .into()
        } else {
            ["system-ui", "Ubuntu", "Droid Sans", "Noto Color Emoji", "sans-serif"].into()
        }
    }
    pub fn system_ui(lang: &Lang) -> Self {
        if cfg!(windows) {
            Self::windows_ui(lang)
        } else if cfg!(target_os = "linux") {
            Self::linux_ui(lang)
        } else if cfg!(target_os = "macos") {
            Self::mac_ui(lang)
        } else {
            [FontName::sans_serif()].into()
        }
    }
    pub fn push(&mut self, font_name: impl Into<FontName>) {
        self.0.push(font_name.into())
    }
}
impl Default for FontNames {
    fn default() -> Self {
        Self::system_ui(&Lang::default())
    }
}
impl fmt::Debug for FontNames {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_tuple("FontNames").field(&self.0).finish()
        } else if self.0.is_empty() {
            write!(f, "[]")
        } else if self.0.len() == 1 {
            write!(f, "{:?}", self.0[0])
        } else {
            write!(f, "[{:?}, ", self.0[0])?;
            for name in &self.0[1..] {
                write!(f, "{name:?}, ")?;
            }
            write!(f, "]")
        }
    }
}
impl fmt::Display for FontNames {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut iter = self.0.iter();
        if let Some(name) = iter.next() {
            write!(f, "{name}")?;
            for name in iter {
                write!(f, ", {name}")?;
            }
        }
        Ok(())
    }
}
impl_from_and_into_var! {
    fn from(font_name: &'static str) -> FontNames {
        FontNames(vec![FontName::new(font_name)])
    }
    fn from(font_name: String) -> FontNames {
        FontNames(vec![FontName::new(font_name)])
    }
    fn from(font_name: Txt) -> FontNames {
        FontNames(vec![FontName::new(font_name)])
    }
    fn from(font_names: Vec<FontName>) -> FontNames {
        FontNames(font_names)
    }
    fn from(font_names: Vec<&'static str>) -> FontNames {
        FontNames(font_names.into_iter().map(FontName::new).collect())
    }
    fn from(font_names: Vec<String>) -> FontNames {
        FontNames(font_names.into_iter().map(FontName::new).collect())
    }
    fn from(font_name: FontName) -> FontNames {
        FontNames(vec![font_name])
    }
}
impl ops::Deref for FontNames {
    type Target = Vec<FontName>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl ops::DerefMut for FontNames {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
impl std::iter::Extend<FontName> for FontNames {
    fn extend<T: IntoIterator<Item = FontName>>(&mut self, iter: T) {
        self.0.extend(iter)
    }
}
impl IntoIterator for FontNames {
    type Item = FontName;
    type IntoIter = std::vec::IntoIter<FontName>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}
impl<const N: usize> From<[FontName; N]> for FontNames {
    fn from(font_names: [FontName; N]) -> Self {
        FontNames(font_names.into())
    }
}
impl<const N: usize> IntoVar<FontNames> for [FontName; N] {
    type Var = LocalVar<FontNames>;
    fn into_var(self) -> Self::Var {
        LocalVar(self.into())
    }
}
impl<const N: usize> From<[&'static str; N]> for FontNames {
    fn from(font_names: [&'static str; N]) -> Self {
        FontNames(font_names.into_iter().map(FontName::new).collect())
    }
}
impl<const N: usize> IntoVar<FontNames> for [&'static str; N] {
    type Var = LocalVar<FontNames>;
    fn into_var(self) -> Self::Var {
        LocalVar(self.into())
    }
}
impl<const N: usize> From<[String; N]> for FontNames {
    fn from(font_names: [String; N]) -> Self {
        FontNames(font_names.into_iter().map(FontName::new).collect())
    }
}
impl<const N: usize> IntoVar<FontNames> for [String; N] {
    type Var = LocalVar<FontNames>;
    fn into_var(self) -> Self::Var {
        LocalVar(self.into())
    }
}
impl<const N: usize> From<[Txt; N]> for FontNames {
    fn from(font_names: [Txt; N]) -> Self {
        FontNames(font_names.into_iter().map(FontName::new).collect())
    }
}
impl<const N: usize> IntoVar<FontNames> for [Txt; N] {
    type Var = LocalVar<FontNames>;
    fn into_var(self) -> Self::Var {
        LocalVar(self.into())
    }
}
event! {
    pub static FONT_CHANGED_EVENT: FontChangedArgs;
}
event_args! {
    pub struct FontChangedArgs {
        pub change: FontChange,
        ..
        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
            list.search_all()
        }
    }
}
#[derive(Clone, Debug)]
pub enum FontChange {
    SystemFonts,
    CustomFonts,
    Refresh,
    GenericFont(FontName, Lang),
    Fallback(Lang),
}
#[derive(Default)]
pub struct FontManager {}
impl AppExtension for FontManager {
    fn event_preview(&mut self, update: &mut EventUpdate) {
        if RAW_FONT_CHANGED_EVENT.has(update) {
            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::SystemFonts));
        } else if let Some(args) = RAW_FONT_AA_CHANGED_EVENT.on(update) {
            FONTS_SV.read().font_aa.set(args.aa);
        } else if FONT_CHANGED_EVENT.has(update) {
            FONTS_SV.write().on_fonts_changed();
        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update) {
            let mut fonts = FONTS_SV.write();
            fonts.font_aa.set(args.font_aa);
            if args.is_respawn {
                fonts.loader.on_view_process_respawn();
            }
        }
    }
    fn update(&mut self) {
        let mut fonts = FONTS_SV.write();
        {
            let mut f = GENERIC_FONTS_SV.write();
            for request in std::mem::take(&mut f.requests) {
                request(&mut f);
            }
        }
        let mut changed = false;
        for (request, responder) in std::mem::take(&mut fonts.loader.unregister_requests) {
            let r = if let Some(removed) = fonts.loader.custom_fonts.remove(&request) {
                for removed in removed {
                    removed.on_refresh();
                }
                changed = true;
                true
            } else {
                false
            };
            responder.respond(r);
        }
        if changed {
            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
        }
        if fonts.prune_requested {
            fonts.on_prune();
        }
    }
}
app_local! {
    static FONTS_SV: FontsService = FontsService {
        loader: FontFaceLoader::new(),
        prune_requested: false,
        font_aa: var(FontAntiAliasing::Default),
    };
}
struct FontsService {
    loader: FontFaceLoader,
    prune_requested: bool,
    font_aa: ArcVar<FontAntiAliasing>,
}
impl FontsService {
    fn on_fonts_changed(&mut self) {
        self.loader.on_refresh();
        self.prune_requested = false;
    }
    fn on_prune(&mut self) {
        self.loader.on_prune();
        self.prune_requested = false;
    }
}
pub struct FONTS;
impl FONTS {
    pub fn refresh(&self) {
        FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Refresh));
    }
    pub fn prune(&self) {
        let mut ft = FONTS_SV.write();
        if !ft.prune_requested {
            ft.prune_requested = true;
            UPDATES.update(None);
        }
    }
    pub fn generics(&self) -> &'static GenericFonts {
        &GenericFonts {}
    }
    pub fn register(&self, custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
        FontFaceLoader::register(custom_font)
    }
    pub fn unregister(&self, custom_family: FontName) -> ResponseVar<bool> {
        FONTS_SV.write().loader.unregister(custom_family)
    }
    pub fn list(
        &self,
        families: &[FontName],
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
        lang: &Lang,
    ) -> ResponseVar<FontFaceList> {
        if let Some(cached) = FONTS_SV.read().loader.try_list(families, style, weight, stretch, lang) {
            return cached;
        }
        FONTS_SV.write().loader.load_list(families, style, weight, stretch, lang)
    }
    pub fn find(
        &self,
        family: &FontName,
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
        lang: &Lang,
    ) -> ResponseVar<Option<FontFace>> {
        if let Some(cached) = FONTS_SV.read().loader.try_cached(family, style, weight, stretch, lang) {
            return cached;
        }
        FONTS_SV.write().loader.load(family, style, weight, stretch, lang)
    }
    pub fn normal(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
        self.find(family, FontStyle::Normal, FontWeight::NORMAL, FontStretch::NORMAL, lang)
    }
    pub fn italic(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
        self.find(family, FontStyle::Italic, FontWeight::NORMAL, FontStretch::NORMAL, lang)
    }
    pub fn bold(&self, family: &FontName, lang: &Lang) -> ResponseVar<Option<FontFace>> {
        self.find(family, FontStyle::Normal, FontWeight::BOLD, FontStretch::NORMAL, lang)
    }
    pub fn custom_fonts(&self) -> Vec<FontName> {
        FONTS_SV.read().loader.custom_fonts.keys().cloned().collect()
    }
    pub fn system_fonts(&self) -> Vec<FontName> {
        font_kit::source::SystemSource::new()
            .all_families()
            .unwrap_or_default()
            .into_iter()
            .map(FontName::from)
            .collect()
    }
    pub fn system_font_aa(&self) -> impl Var<FontAntiAliasing> {
        FONTS_SV.read().font_aa.read_only()
    }
}
impl From<font_kit::metrics::Metrics> for FontFaceMetrics {
    fn from(m: font_kit::metrics::Metrics) -> Self {
        FontFaceMetrics {
            units_per_em: m.units_per_em,
            ascent: m.ascent,
            descent: m.descent,
            line_gap: m.line_gap,
            underline_position: m.underline_position,
            underline_thickness: m.underline_thickness,
            cap_height: m.cap_height,
            x_height: m.x_height,
            bounds: euclid::rect(
                m.bounding_box.origin_x(),
                m.bounding_box.origin_y(),
                m.bounding_box.width(),
                m.bounding_box.height(),
            ),
        }
    }
}
#[derive(PartialEq, Eq, Hash)]
struct FontInstanceKey(Px, Box<[(harfbuzz_rs::Tag, i32)]>);
impl FontInstanceKey {
    pub fn new(size: Px, variations: &[harfbuzz_rs::Variation]) -> Self {
        let variations_key: Vec<_> = variations.iter().map(|p| (p.tag(), (p.value() * 1000.0) as i32)).collect();
        FontInstanceKey(size, variations_key.into_boxed_slice())
    }
}
#[derive(Clone)]
pub struct FontFace(Arc<LoadedFontFace>);
struct LoadedFontFace {
    data: FontDataRef,
    face: harfbuzz_rs::Shared<harfbuzz_rs::Face<'static>>,
    face_index: u32,
    display_name: FontName,
    family_name: FontName,
    postscript_name: Option<String>,
    is_monospace: bool,
    properties: font_kit::properties::Properties,
    metrics: FontFaceMetrics,
    color_palettes: ColorPalettes,
    color_glyphs: ColorGlyphs,
    has_ligatures: bool,
    lig_carets: LigatureCaretList,
    m: Mutex<FontFaceMut>,
}
struct FontFaceMut {
    font_kit: FontKitCache,
    instances: HashMap<FontInstanceKey, Font>,
    render_ids: Vec<RenderFontFace>,
    unregistered: bool,
}
impl fmt::Debug for FontFace {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let m = self.0.m.lock();
        f.debug_struct("FontFace")
            .field("display_name", &self.0.display_name)
            .field("family_name", &self.0.family_name)
            .field("postscript_name", &self.0.postscript_name)
            .field("is_monospace", &self.0.is_monospace)
            .field("properties", &self.0.properties)
            .field("metrics", &self.0.metrics)
            .field("color_palettes.len()", &self.0.color_palettes.len())
            .field("color_glyphs.len()", &self.0.color_glyphs.len())
            .field("instances.len()", &m.instances.len())
            .field("render_keys.len()", &m.render_ids.len())
            .field("unregistered", &m.unregistered)
            .finish_non_exhaustive()
    }
}
impl PartialEq for FontFace {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}
impl Eq for FontFace {}
impl FontFace {
    pub fn empty() -> Self {
        FontFace(Arc::new(LoadedFontFace {
            data: FontDataRef::from_static(&[]),
            face: harfbuzz_rs::Face::empty().to_shared(),
            face_index: 0,
            display_name: FontName::from("<empty>"),
            family_name: FontName::from("<empty>"),
            postscript_name: None,
            is_monospace: true,
            properties: font_kit::properties::Properties {
                style: FontStyle::Normal.into(),
                weight: FontWeight::NORMAL.into(),
                stretch: FontStretch::NORMAL.into(),
            },
            metrics: FontFaceMetrics {
                units_per_em: 2048,
                ascent: 1616.0,
                descent: -432.0,
                line_gap: 0.0,
                underline_position: -205.0,
                underline_thickness: 102.0,
                cap_height: 1616.0,
                x_height: 1616.0,
                bounds: euclid::Box2D::new(euclid::point2(0.0, -432.0), euclid::point2(1291.0, 1616.0)).to_rect(),
            },
            color_palettes: ColorPalettes::empty(),
            color_glyphs: ColorGlyphs::empty(),
            has_ligatures: false,
            lig_carets: LigatureCaretList::empty(),
            m: Mutex::new(FontFaceMut {
                font_kit: FontKitCache::default(),
                instances: HashMap::default(),
                render_ids: vec![],
                unregistered: false,
            }),
        }))
    }
    pub fn is_empty(&self) -> bool {
        self.0.data.is_empty()
    }
    async fn load_custom(custom_font: CustomFont) -> Result<Self, FontLoadingError> {
        let bytes;
        let face_index;
        match custom_font.source {
            FontSource::File(path, index) => {
                bytes = FontDataRef(Arc::new(task::wait(|| std::fs::read(path)).await?));
                face_index = index;
            }
            FontSource::Memory(arc, index) => {
                bytes = arc;
                face_index = index;
            }
            FontSource::Alias(other_font) => {
                let result = FONTS_SV
                    .write()
                    .loader
                    .load_resolved(&other_font, custom_font.style, custom_font.weight, custom_font.stretch);
                return match result.wait_into_rsp().await {
                    Some(other_font) => Ok(FontFace(Arc::new(LoadedFontFace {
                        data: other_font.0.data.clone(),
                        face: harfbuzz_rs::Face::new(other_font.0.data.clone(), other_font.0.face_index).to_shared(),
                        face_index: other_font.0.face_index,
                        display_name: custom_font.name.clone(),
                        family_name: custom_font.name,
                        postscript_name: None,
                        properties: other_font.0.properties,
                        is_monospace: other_font.0.is_monospace,
                        metrics: other_font.0.metrics.clone(),
                        m: Mutex::new(FontFaceMut {
                            font_kit: other_font.0.m.lock().font_kit.clone(),
                            instances: Default::default(),
                            render_ids: Default::default(),
                            unregistered: Default::default(),
                        }),
                        color_palettes: other_font.0.color_palettes.clone(),
                        color_glyphs: other_font.0.color_glyphs.clone(),
                        has_ligatures: other_font.0.has_ligatures,
                        lig_carets: other_font.0.lig_carets.clone(),
                    }))),
                    None => Err(FontLoadingError::NoSuchFontInCollection),
                };
            }
        }
        let font = font_kit::handle::Handle::Memory {
            bytes: Arc::clone(&bytes.0),
            font_index: face_index,
        }
        .load()?;
        let face = harfbuzz_rs::Face::new(bytes.clone(), face_index);
        if face.glyph_count() == 0 {
            return Err(FontLoadingError::UnknownFormat);
        }
        let color_palettes = ColorPalettes::load(&font)?;
        let color_glyphs = if color_palettes.is_empty() {
            ColorGlyphs::empty()
        } else {
            ColorGlyphs::load(&font)?
        };
        let has_ligatures = font.load_font_table(GSUB).is_some();
        let lig_carets = if has_ligatures {
            LigatureCaretList::empty()
        } else {
            LigatureCaretList::load(&font)?
        };
        Ok(FontFace(Arc::new(LoadedFontFace {
            data: bytes,
            face: face.to_shared(),
            face_index,
            display_name: custom_font.name.clone(),
            family_name: custom_font.name,
            postscript_name: None,
            properties: font_kit::properties::Properties {
                style: custom_font.style.into(),
                weight: custom_font.weight.into(),
                stretch: custom_font.stretch.into(),
            },
            is_monospace: font.is_monospace(),
            metrics: font.metrics().into(),
            color_palettes,
            color_glyphs,
            has_ligatures,
            lig_carets,
            m: Mutex::new(FontFaceMut {
                font_kit: {
                    let mut font_kit = FontKitCache::default();
                    font_kit.get_or_init(move || font);
                    font_kit
                },
                instances: Default::default(),
                render_ids: Default::default(),
                unregistered: Default::default(),
            }),
        })))
    }
    fn load(handle: font_kit::handle::Handle) -> Result<Self, FontLoadingError> {
        let _span = tracing::trace_span!("FontFace::load").entered();
        let bytes;
        let face_index;
        match handle {
            font_kit::handle::Handle::Path { path, font_index } => {
                bytes = FontDataRef(Arc::new(std::fs::read(path)?));
                face_index = font_index;
            }
            font_kit::handle::Handle::Memory { bytes: arc, font_index } => {
                bytes = FontDataRef(arc);
                face_index = font_index;
            }
        };
        let font = font_kit::handle::Handle::Memory {
            bytes: Arc::clone(&bytes.0),
            font_index: face_index,
        }
        .load()?;
        let face = harfbuzz_rs::Face::new(bytes.clone(), face_index);
        if face.glyph_count() == 0 {
            return Err(FontLoadingError::UnknownFormat);
        }
        let color_palettes = ColorPalettes::load(&font)?;
        let color_glyphs = if color_palettes.is_empty() {
            ColorGlyphs::empty()
        } else {
            ColorGlyphs::load(&font)?
        };
        let has_ligatures = font.load_font_table(GSUB).is_some();
        let lig_carets = if has_ligatures {
            LigatureCaretList::empty()
        } else {
            LigatureCaretList::load(&font)?
        };
        let metrics = font.metrics();
        if metrics.units_per_em == 0 {
            tracing::debug!("font {:?} units_per_em 0", font.family_name());
            return Err(FontLoadingError::UnknownFormat);
        }
        Ok(FontFace(Arc::new(LoadedFontFace {
            data: bytes,
            face: face.to_shared(),
            face_index,
            display_name: font.full_name().into(),
            family_name: font.family_name().into(),
            postscript_name: font.postscript_name(),
            properties: font.properties(),
            is_monospace: font.is_monospace(),
            metrics: metrics.into(),
            color_palettes,
            color_glyphs,
            has_ligatures,
            lig_carets,
            m: Mutex::new(FontFaceMut {
                font_kit: {
                    let mut font_kit = FontKitCache::default();
                    font_kit.get_or_init(move || font);
                    font_kit
                },
                instances: Default::default(),
                render_ids: Default::default(),
                unregistered: Default::default(),
            }),
        })))
    }
    fn on_refresh(&self) {
        let mut m = self.0.m.lock();
        m.instances.clear();
        m.unregistered = true;
    }
    fn render_face(&self, renderer: &ViewRenderer) -> zng_view_api::font::FontFaceId {
        let mut m = self.0.m.lock();
        for r in m.render_ids.iter() {
            if &r.renderer == renderer {
                return r.face_id;
            }
        }
        let key = match renderer.add_font_face((*self.0.data.0).clone(), self.0.face_index) {
            Ok(k) => k,
            Err(ViewProcessOffline) => {
                tracing::debug!("respawned calling `add_font`, will return dummy font key");
                return zng_view_api::font::FontFaceId::INVALID;
            }
        };
        m.render_ids.push(RenderFontFace::new(renderer, key));
        key
    }
    pub fn harfbuzz_face(&self) -> &harfbuzz_rs::Shared<harfbuzz_rs::Face<'static>> {
        &self.0.face
    }
    pub fn font_kit(&self) -> Option<Rc<font_kit::font::Font>> {
        if self.is_empty() {
            None
        } else {
            Some(self.0.m.lock().font_kit.get_or_init(|| {
                font_kit::handle::Handle::Memory {
                    bytes: Arc::clone(&self.0.data.0),
                    font_index: self.0.face_index,
                }
                .load()
                .unwrap()
            }))
        }
    }
    pub fn bytes(&self) -> &FontDataRef {
        &self.0.data
    }
    pub fn display_name(&self) -> &FontName {
        &self.0.display_name
    }
    pub fn family_name(&self) -> &FontName {
        &self.0.family_name
    }
    pub fn postscript_name(&self) -> Option<&str> {
        self.0.postscript_name.as_deref()
    }
    pub fn index(&self) -> u32 {
        self.0.face_index
    }
    pub fn style(&self) -> FontStyle {
        self.0.properties.style.into()
    }
    pub fn weight(&self) -> FontWeight {
        self.0.properties.weight.into()
    }
    pub fn stretch(&self) -> FontStretch {
        self.0.properties.stretch.into()
    }
    pub fn is_monospace(&self) -> bool {
        self.0.is_monospace
    }
    pub fn metrics(&self) -> &FontFaceMetrics {
        &self.0.metrics
    }
    pub fn sized(&self, font_size: Px, variations: RFontVariations) -> Font {
        let key = FontInstanceKey::new(font_size, &variations);
        let mut m = self.0.m.lock();
        if !m.unregistered {
            m.instances
                .entry(key)
                .or_insert_with(|| Font::new(self.clone(), font_size, variations))
                .clone()
        } else {
            tracing::debug!(target: "font_loading", "creating font from unregistered `{}`, will not cache", self.0.display_name);
            Font::new(self.clone(), font_size, variations)
        }
    }
    pub fn synthesis_for(&self, style: FontStyle, weight: FontWeight) -> FontSynthesis {
        let mut synth = FontSynthesis::DISABLED;
        if style != FontStyle::Normal && self.style() == FontStyle::Normal {
            synth |= FontSynthesis::OBLIQUE;
        }
        if weight > self.weight() {
            synth |= FontSynthesis::BOLD;
        }
        synth
    }
    pub fn is_cached(&self) -> bool {
        !self.0.m.lock().unregistered
    }
    pub fn color_palettes(&self) -> &ColorPalettes {
        &self.0.color_palettes
    }
    pub fn color_glyphs(&self) -> &ColorGlyphs {
        &self.0.color_glyphs
    }
    pub fn has_ligatures(&self) -> bool {
        self.0.has_ligatures
    }
    pub fn has_ligature_caret_offsets(&self) -> bool {
        !self.0.lig_carets.is_empty()
    }
}
#[derive(Clone)]
pub struct Font(Arc<LoadedFont>);
struct LoadedFont {
    face: FontFace,
    font: harfbuzz_rs::Shared<harfbuzz_rs::Font<'static>>,
    size: Px,
    variations: RFontVariations,
    metrics: FontMetrics,
    render_keys: Mutex<Vec<RenderFont>>,
    small_word_cache: RwLock<HashMap<WordCacheKey<[u8; Font::SMALL_WORD_LEN]>, ShapedSegmentData>>,
    word_cache: RwLock<HashMap<WordCacheKey<String>, ShapedSegmentData>>,
}
impl fmt::Debug for Font {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Font")
            .field("face", &self.0.face)
            .field("size", &self.0.size)
            .field("metrics", &self.0.metrics)
            .field("render_keys.len()", &self.0.render_keys.lock().len())
            .field("small_word_cache.len()", &self.0.small_word_cache.read().len())
            .field("word_cache.len()", &self.0.word_cache.read().len())
            .finish()
    }
}
impl PartialEq for Font {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}
impl Eq for Font {}
impl Font {
    const SMALL_WORD_LEN: usize = 8;
    fn to_small_word(s: &str) -> Option<[u8; Self::SMALL_WORD_LEN]> {
        if s.len() <= Self::SMALL_WORD_LEN {
            let mut a = [b'\0'; Self::SMALL_WORD_LEN];
            a[..s.len()].copy_from_slice(s.as_bytes());
            Some(a)
        } else {
            None
        }
    }
    fn new(face: FontFace, size: Px, variations: RFontVariations) -> Self {
        let ppem = size.0 as u32;
        let mut font = harfbuzz_rs::Font::new(face.harfbuzz_face().clone());
        font.set_ppem(ppem, ppem);
        font.set_variations(&variations);
        Font(Arc::new(LoadedFont {
            metrics: face.metrics().sized(size),
            font: font.to_shared(),
            face,
            size,
            variations,
            render_keys: Mutex::new(vec![]),
            small_word_cache: RwLock::default(),
            word_cache: RwLock::default(),
        }))
    }
    fn render_font(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
        let _span = tracing::trace_span!("Font::render_font").entered();
        let mut render_keys = self.0.render_keys.lock();
        for r in render_keys.iter() {
            if &r.renderer == renderer && r.synthesis == synthesis {
                return r.font_id;
            }
        }
        let font_key = self.0.face.render_face(renderer);
        let opt = zng_view_api::font::FontOptions {
            synthetic_oblique: synthesis.contains(FontSynthesis::OBLIQUE),
            synthetic_bold: synthesis.contains(FontSynthesis::BOLD),
            ..Default::default()
        };
        let variations = self.0.variations.iter().map(|v| (v.tag().to_bytes(), v.value())).collect();
        let key = match renderer.add_font(font_key, self.0.size, opt, variations) {
            Ok(k) => k,
            Err(ViewProcessOffline) => {
                tracing::debug!("respawned calling `add_font_instance`, will return dummy font key");
                return zng_view_api::font::FontId::INVALID;
            }
        };
        render_keys.push(RenderFont::new(renderer, synthesis, key));
        key
    }
    pub fn face(&self) -> &FontFace {
        &self.0.face
    }
    pub fn harfbuzz_font(&self) -> &harfbuzz_rs::Shared<harfbuzz_rs::Font<'static>> {
        &self.0.font
    }
    pub fn size(&self) -> Px {
        self.0.size
    }
    pub fn variations(&self) -> &RFontVariations {
        &self.0.variations
    }
    pub fn metrics(&self) -> &FontMetrics {
        &self.0.metrics
    }
    pub fn ligature_caret_offsets(
        &self,
        lig: zng_view_api::font::GlyphIndex,
    ) -> impl ExactSizeIterator<Item = f32> + DoubleEndedIterator + '_ {
        let face = &self.0.face.0;
        face.lig_carets.carets(lig).iter().map(move |&o| match o {
            ligature_util::LigatureCaret::Coordinate(o) => {
                let size_scale = 1.0 / face.metrics.units_per_em as f32 * self.0.size.0 as f32;
                o as f32 * size_scale
            }
            ligature_util::LigatureCaret::GlyphContourPoint(i) => {
                if let Some((x, _)) = self.harfbuzz_font().get_glyph_contour_point(lig, i as _) {
                    x as f32 * self.0.metrics.size_scale
                } else {
                    0.0
                }
            }
        })
    }
}
impl zng_app::render::Font for Font {
    fn is_empty_fallback(&self) -> bool {
        self.face().is_empty()
    }
    fn renderer_id(&self, renderer: &ViewRenderer, synthesis: FontSynthesis) -> zng_view_api::font::FontId {
        self.render_font(renderer, synthesis)
    }
}
#[derive(Debug, Clone)]
pub struct FontFaceList {
    fonts: Box<[FontFace]>,
    requested_style: FontStyle,
    requested_weight: FontWeight,
    requested_stretch: FontStretch,
}
impl FontFaceList {
    pub fn empty() -> Self {
        Self {
            fonts: Box::new([FontFace::empty()]),
            requested_style: FontStyle::Normal,
            requested_weight: FontWeight::NORMAL,
            requested_stretch: FontStretch::NORMAL,
        }
    }
    pub fn requested_style(&self) -> FontStyle {
        self.requested_style
    }
    pub fn requested_weight(&self) -> FontWeight {
        self.requested_weight
    }
    pub fn requested_stretch(&self) -> FontStretch {
        self.requested_stretch
    }
    pub fn best(&self) -> &FontFace {
        &self.fonts[0]
    }
    pub fn face_synthesis(&self, face_index: usize) -> FontSynthesis {
        if let Some(face) = self.fonts.get(face_index) {
            face.synthesis_for(self.requested_style, self.requested_weight)
        } else {
            FontSynthesis::DISABLED
        }
    }
    pub fn iter(&self) -> std::slice::Iter<FontFace> {
        self.fonts.iter()
    }
    pub fn len(&self) -> usize {
        self.fonts.len()
    }
    pub fn is_empty(&self) -> bool {
        self.fonts[0].is_empty() && self.fonts.len() == 1
    }
    pub fn sized(&self, font_size: Px, variations: RFontVariations) -> FontList {
        FontList {
            fonts: self.fonts.iter().map(|f| f.sized(font_size, variations.clone())).collect(),
            requested_style: self.requested_style,
            requested_weight: self.requested_weight,
            requested_stretch: self.requested_stretch,
        }
    }
}
impl PartialEq for FontFaceList {
    fn eq(&self, other: &Self) -> bool {
        self.requested_style == other.requested_style
            && self.requested_weight == other.requested_weight
            && self.requested_stretch == other.requested_stretch
            && self.fonts.len() == other.fonts.len()
            && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
    }
}
impl Eq for FontFaceList {}
impl std::ops::Deref for FontFaceList {
    type Target = [FontFace];
    fn deref(&self) -> &Self::Target {
        &self.fonts
    }
}
impl<'a> std::iter::IntoIterator for &'a FontFaceList {
    type Item = &'a FontFace;
    type IntoIter = std::slice::Iter<'a, FontFace>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}
impl std::ops::Index<usize> for FontFaceList {
    type Output = FontFace;
    fn index(&self, index: usize) -> &Self::Output {
        &self.fonts[index]
    }
}
#[derive(Debug, Clone)]
pub struct FontList {
    fonts: Box<[Font]>,
    requested_style: FontStyle,
    requested_weight: FontWeight,
    requested_stretch: FontStretch,
}
#[allow(clippy::len_without_is_empty)] impl FontList {
    pub fn best(&self) -> &Font {
        &self.fonts[0]
    }
    pub fn requested_size(&self) -> Px {
        self.fonts[0].size()
    }
    pub fn requested_style(&self) -> FontStyle {
        self.requested_style
    }
    pub fn requested_weight(&self) -> FontWeight {
        self.requested_weight
    }
    pub fn requested_stretch(&self) -> FontStretch {
        self.requested_stretch
    }
    pub fn face_synthesis(&self, font_index: usize) -> FontSynthesis {
        if let Some(font) = self.fonts.get(font_index) {
            font.0.face.synthesis_for(self.requested_style, self.requested_weight)
        } else {
            FontSynthesis::DISABLED
        }
    }
    pub fn iter(&self) -> std::slice::Iter<Font> {
        self.fonts.iter()
    }
    pub fn len(&self) -> usize {
        self.fonts.len()
    }
    pub fn is_sized_from(&self, faces: &FontFaceList) -> bool {
        if self.len() != faces.len() {
            return false;
        }
        for (font, face) in self.iter().zip(faces.iter()) {
            if font.face() != face {
                return false;
            }
        }
        true
    }
}
impl PartialEq for FontList {
    fn eq(&self, other: &Self) -> bool {
        self.requested_style == other.requested_style
            && self.requested_weight == other.requested_weight
            && self.requested_stretch == other.requested_stretch
            && self.fonts.len() == other.fonts.len()
            && self.fonts.iter().zip(other.fonts.iter()).all(|(a, b)| a == b)
    }
}
impl Eq for FontList {}
impl std::ops::Deref for FontList {
    type Target = [Font];
    fn deref(&self) -> &Self::Target {
        &self.fonts
    }
}
impl<'a> std::iter::IntoIterator for &'a FontList {
    type Item = &'a Font;
    type IntoIter = std::slice::Iter<'a, Font>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}
impl<I: SliceIndex<[Font]>> std::ops::Index<I> for FontList {
    type Output = I::Output;
    fn index(&self, index: I) -> &I::Output {
        &self.fonts[index]
    }
}
struct FontFaceLoader {
    custom_fonts: HashMap<FontName, Vec<FontFace>>,
    unregister_requests: Vec<(FontName, ResponderVar<bool>)>,
    system_fonts_cache: HashMap<FontName, Vec<SystemFontFace>>,
    list_cache: HashMap<Box<[FontName]>, Vec<FontFaceListQuery>>,
}
struct SystemFontFace {
    properties: (FontStyle, FontWeight, FontStretch),
    result: ResponseVar<Option<FontFace>>,
}
struct FontFaceListQuery {
    properties: (FontStyle, FontWeight, FontStretch),
    lang: Lang,
    result: ResponseVar<FontFaceList>,
}
impl FontFaceLoader {
    fn new() -> Self {
        FontFaceLoader {
            custom_fonts: HashMap::new(),
            unregister_requests: vec![],
            system_fonts_cache: HashMap::new(),
            list_cache: HashMap::new(),
        }
    }
    fn on_view_process_respawn(&mut self) {
        let sys_fonts = self.system_fonts_cache.values().flatten().filter_map(|f| f.result.rsp().flatten());
        for face in self.custom_fonts.values().flatten().cloned().chain(sys_fonts) {
            let mut m = face.0.m.lock();
            m.render_ids.clear();
            for inst in m.instances.values() {
                inst.0.render_keys.lock().clear();
            }
        }
    }
    fn on_refresh(&mut self) {
        for (_, sys_family) in self.system_fonts_cache.drain() {
            for sys_font in sys_family {
                sys_font.result.with(|r| {
                    if let Some(Some(face)) = r.done() {
                        face.on_refresh();
                    }
                });
            }
        }
    }
    fn on_prune(&mut self) {
        self.system_fonts_cache.retain(|_, v| {
            v.retain(|sff| {
                if sff.result.strong_count() == 1 {
                    sff.result.with(|r| {
                        match r.done() {
                            Some(Some(face)) => Arc::strong_count(&face.0) > 1, Some(None) => false,                                None => true,                                       }
                    })
                } else {
                    true
                }
            });
            !v.is_empty()
        });
        self.list_cache.clear();
    }
    fn register(custom_font: CustomFont) -> ResponseVar<Result<FontFace, FontLoadingError>> {
        let resp = task::respond(FontFace::load_custom(custom_font));
        resp.hook(|args| {
            if let Some(done) = args.value().done() {
                if let Ok(face) = done {
                    let mut fonts = FONTS_SV.write();
                    let family = fonts.loader.custom_fonts.entry(face.0.family_name.clone()).or_default();
                    let existing = family.iter().position(|f| f.0.properties == face.0.properties);
                    if let Some(i) = existing {
                        family[i] = face.clone();
                    } else {
                        family.push(face.clone());
                    }
                    FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::CustomFonts));
                }
                false
            } else {
                true
            }
        })
        .perm();
        resp
    }
    fn unregister(&mut self, custom_family: FontName) -> ResponseVar<bool> {
        let (responder, response) = response_var();
        if !self.unregister_requests.is_empty() {
            UPDATES.update(None);
        }
        self.unregister_requests.push((custom_family, responder));
        response
    }
    fn try_list(
        &self,
        families: &[FontName],
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
        lang: &Lang,
    ) -> Option<ResponseVar<FontFaceList>> {
        if let Some(queries) = self.list_cache.get(families) {
            for q in queries {
                if q.properties == (style, weight, stretch) && &q.lang == lang {
                    return Some(q.result.clone());
                }
            }
        }
        None
    }
    fn load_list(
        &mut self,
        families: &[FontName],
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
        lang: &Lang,
    ) -> ResponseVar<FontFaceList> {
        if let Some(r) = self.try_list(families, style, weight, stretch, lang) {
            return r;
        }
        let mut list = Vec::with_capacity(families.len() + 1);
        let mut pending = vec![];
        {
            let fallback = [GenericFonts {}.fallback(lang)];
            let mut used = HashSet::with_capacity(families.len());
            for name in families.iter().chain(&fallback) {
                if !used.insert(name) {
                    continue;
                }
                let face = self.load(name, style, weight, stretch, lang);
                if face.is_done() {
                    if let Some(face) = face.into_rsp().unwrap() {
                        list.push(face);
                    }
                } else {
                    pending.push((list.len(), face));
                }
            }
        }
        let r = if pending.is_empty() {
            if list.is_empty() {
                tracing::error!(target: "font_loading", "failed to load fallback font");
                list.push(FontFace::empty());
            }
            response_done_var(FontFaceList {
                fonts: list.into_boxed_slice(),
                requested_style: style,
                requested_weight: weight,
                requested_stretch: stretch,
            })
        } else {
            task::respond(async move {
                for (i, pending) in pending.into_iter().rev() {
                    if let Some(rsp) = pending.wait_into_rsp().await {
                        list.insert(i, rsp);
                    }
                }
                if list.is_empty() {
                    tracing::error!(target: "font_loading", "failed to load fallback font");
                    list.push(FontFace::empty());
                }
                FontFaceList {
                    fonts: list.into_boxed_slice(),
                    requested_style: style,
                    requested_weight: weight,
                    requested_stretch: stretch,
                }
            })
        };
        self.list_cache
            .entry(families.iter().cloned().collect())
            .or_insert_with(|| Vec::with_capacity(1))
            .push(FontFaceListQuery {
                properties: (style, weight, stretch),
                lang: lang.clone(),
                result: r.clone(),
            });
        r
    }
    fn try_cached(
        &self,
        font_name: &FontName,
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
        lang: &Lang,
    ) -> Option<ResponseVar<Option<FontFace>>> {
        let resolved = GenericFonts {}.resolve(font_name, lang);
        let font_name = resolved.as_ref().unwrap_or(font_name);
        self.try_resolved(font_name, style, weight, stretch)
    }
    fn load(
        &mut self,
        font_name: &FontName,
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
        lang: &Lang,
    ) -> ResponseVar<Option<FontFace>> {
        let resolved = GenericFonts {}.resolve(font_name, lang);
        let font_name = resolved.as_ref().unwrap_or(font_name);
        self.load_resolved(font_name, style, weight, stretch)
    }
    fn try_resolved(
        &self,
        font_name: &FontName,
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
    ) -> Option<ResponseVar<Option<FontFace>>> {
        if let Some(custom_family) = self.custom_fonts.get(font_name) {
            let custom = Self::match_custom(custom_family, style, weight, stretch);
            return Some(response_done_var(Some(custom)));
        }
        if let Some(cached_sys_family) = self.system_fonts_cache.get(font_name) {
            for sys_face in cached_sys_family.iter() {
                if sys_face.properties == (style, weight, stretch) {
                    return Some(sys_face.result.clone());
                }
            }
        }
        None
    }
    fn load_resolved(
        &mut self,
        font_name: &FontName,
        style: FontStyle,
        weight: FontWeight,
        stretch: FontStretch,
    ) -> ResponseVar<Option<FontFace>> {
        if let Some(cached) = self.try_resolved(font_name, style, weight, stretch) {
            return cached;
        }
        let load = task::wait(clmv!(font_name, || {
            let handle = match Self::get_system(&font_name, style, weight, stretch) {
                Some(h) => h,
                None => {
                    #[cfg(debug_assertions)]
                    static NOT_FOUND: Mutex<Option<HashSet<FontName>>> = Mutex::new(None);
                    #[cfg(debug_assertions)]
                    if NOT_FOUND.lock().get_or_insert_with(HashSet::default).insert(font_name.clone()) {
                        tracing::debug!(r#"font "{font_name}" not found"#);
                    }
                    return None;
                }
            };
            match FontFace::load(handle) {
                Ok(f) => Some(f),
                Err(FontLoadingError::UnknownFormat) => None,
                Err(e) => {
                    tracing::error!(target: "font_loading", "failed to load system font, {e}\nquery: {:?}", (font_name, style, weight, stretch));
                    None
                }
            }
        }));
        let result = task::respond(async_clmv!(font_name, {
            match task::with_deadline(load, 10.secs()).await {
                Ok(r) => r,
                Err(_) => {
                    tracing::error!(target: "font_loading", "timeout loading {font_name:?}");
                    None
                }
            }
        }));
        self.system_fonts_cache
            .entry(font_name.clone())
            .or_insert_with(|| Vec::with_capacity(1))
            .push(SystemFontFace {
                properties: (style, weight, stretch),
                result: result.clone(),
            });
        result
    }
    fn get_system(font_name: &FontName, style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Option<font_kit::handle::Handle> {
        let _span = tracing::trace_span!("FontFaceLoader::get_system").entered();
        match_util::best(font_name, style, weight, stretch)
    }
    fn match_custom(faces: &[FontFace], style: FontStyle, weight: FontWeight, stretch: FontStretch) -> FontFace {
        if faces.len() == 1 {
            return faces[0].clone();
        }
        let mut set = Vec::with_capacity(faces.len());
        let mut set_dist = 0.0f64; let wrong_side = if stretch <= FontStretch::NORMAL {
            |s| s > FontStretch::NORMAL
        } else {
            |s| s <= FontStretch::NORMAL
        };
        for face in faces {
            let mut dist = (face.stretch().0 - stretch.0).abs() as f64;
            if wrong_side(face.stretch()) {
                dist += f32::MAX as f64 + 1.0;
            }
            if set.is_empty() {
                set.push(face);
                set_dist = dist;
            } else if dist < set_dist {
                set_dist = dist;
                set.clear();
                set.push(face);
            } else if (dist - set_dist).abs() < 0.0001 {
                set.push(face);
            }
        }
        if set.len() == 1 {
            return set[0].clone();
        }
        let style_pref = match style {
            FontStyle::Normal => [FontStyle::Normal, FontStyle::Oblique, FontStyle::Italic],
            FontStyle::Italic => [FontStyle::Italic, FontStyle::Oblique, FontStyle::Normal],
            FontStyle::Oblique => [FontStyle::Oblique, FontStyle::Italic, FontStyle::Normal],
        };
        let mut best_style = style_pref.len();
        for face in &set {
            let i = style_pref.iter().position(|&s| s == face.style()).unwrap();
            if i < best_style {
                best_style = i;
            }
        }
        set.retain(|f| f.style() == style_pref[best_style]);
        if set.len() == 1 {
            return set[0].clone();
        }
        let add_penalty = if weight.0 >= 400.0 && weight.0 <= 500.0 {
            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
                if face.weight() < weight {
                    *dist += 100.0;
                } else if face.weight().0 > 500.0 {
                    *dist += 600.0;
                }
            }
        } else if weight.0 < 400.0 {
            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
                if face.weight() > weight {
                    *dist += weight.0 as f64;
                }
            }
        } else {
            debug_assert!(weight.0 > 500.0);
            |face: &FontFace, weight: FontWeight, dist: &mut f64| {
                if face.weight() < weight {
                    *dist += f32::MAX as f64;
                }
            }
        };
        let mut best = set[0];
        let mut best_dist = f64::MAX;
        for face in &set {
            let mut dist = (face.weight().0 - weight.0).abs() as f64;
            add_penalty(face, weight, &mut dist);
            if dist < best_dist {
                best_dist = dist;
                best = face;
            }
        }
        best.clone()
    }
}
struct RenderFontFace {
    renderer: ViewRenderer,
    face_id: zng_view_api::font::FontFaceId,
}
impl RenderFontFace {
    fn new(renderer: &ViewRenderer, face_id: zng_view_api::font::FontFaceId) -> Self {
        RenderFontFace {
            renderer: renderer.clone(),
            face_id,
        }
    }
}
impl Drop for RenderFontFace {
    fn drop(&mut self) {
        let _ = self.renderer.delete_font_face(self.face_id);
    }
}
struct RenderFont {
    renderer: ViewRenderer,
    synthesis: FontSynthesis,
    font_id: zng_view_api::font::FontId,
}
impl RenderFont {
    fn new(renderer: &ViewRenderer, synthesis: FontSynthesis, font_id: zng_view_api::font::FontId) -> RenderFont {
        RenderFont {
            renderer: renderer.clone(),
            synthesis,
            font_id,
        }
    }
}
impl Drop for RenderFont {
    fn drop(&mut self) {
        let _ = self.renderer.delete_font(self.font_id);
    }
}
app_local! {
    static GENERIC_FONTS_SV: GenericFontsService = GenericFontsService::new();
}
struct GenericFontsService {
    serif: LangMap<FontName>,
    sans_serif: LangMap<FontName>,
    monospace: LangMap<FontName>,
    cursive: LangMap<FontName>,
    fantasy: LangMap<FontName>,
    fallback: LangMap<FontName>,
    requests: Vec<Box<dyn FnOnce(&mut GenericFontsService) + Send + Sync>>,
}
impl GenericFontsService {
    fn new() -> Self {
        fn default(name: impl Into<FontName>) -> LangMap<FontName> {
            let mut f = LangMap::with_capacity(1);
            f.insert(lang!(und), name.into());
            f
        }
        let serif = "serif";
        let sans_serif = "sans-serif";
        let monospace = "monospace";
        let cursive = "cursive";
        let fantasy = "fantasy";
        let fallback = if cfg!(windows) {
            "Segoe UI Symbol"
        } else if cfg!(target_os = "linux") {
            "Standard Symbols PS"
        } else {
            "sans-serif"
        };
        GenericFontsService {
            serif: default(serif),
            sans_serif: default(sans_serif),
            monospace: default(monospace),
            cursive: default(cursive),
            fantasy: default(fantasy),
            fallback: default(fallback),
            requests: vec![],
        }
    }
}
pub struct GenericFonts {}
impl GenericFonts {}
macro_rules! impl_fallback_accessors {
    ($($name:ident=$name_str:tt),+ $(,)?) => {$($crate::paste! {
    #[doc = "Gets the fallback *"$name_str "* font for the given language."]
    #[doc = "Note that the returned name can still be the generic `\""$name_str "\"`, this delegates the resolution to the operating system."]
    pub fn $name(&self, lang: &Lang) -> FontName {
        GENERIC_FONTS_SV.read().$name.get(lang).unwrap().clone()
    }
    #[doc = "Sets the fallback *"$name_str "* font for the given language."]
    pub fn [<set_ $name>]<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
        let mut g = GENERIC_FONTS_SV.write();
        let font_name = font_name.into();
        if g.requests.is_empty() {
            UPDATES.update(None);
        }
        g.requests.push(Box::new(move |g| {
            g.$name.insert(lang.clone(), font_name);
            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::GenericFont(FontName::$name(), lang)));
        }));
    }
    })+};
}
impl GenericFonts {
    impl_fallback_accessors! {
        serif="serif", sans_serif="sans-serif", monospace="monospace", cursive="cursive", fantasy="fantasy"
    }
    pub fn fallback(&self, lang: &Lang) -> FontName {
        GENERIC_FONTS_SV.read().fallback.get(lang).unwrap().clone()
    }
    pub fn set_fallback<F: Into<FontName>>(&self, lang: Lang, font_name: F) {
        let mut g = GENERIC_FONTS_SV.write();
        if g.requests.is_empty() {
            UPDATES.update(None);
        }
        let font_name = font_name.into();
        g.requests.push(Box::new(move |g| {
            FONT_CHANGED_EVENT.notify(FontChangedArgs::now(FontChange::Fallback(lang.clone())));
            g.fallback.insert(lang, font_name);
        }));
    }
    pub fn resolve(&self, name: &FontName, lang: &Lang) -> Option<FontName> {
        if name == &FontName::serif() {
            Some(self.serif(lang))
        } else if name == &FontName::sans_serif() {
            Some(self.sans_serif(lang))
        } else if name == &FontName::monospace() {
            Some(self.monospace(lang))
        } else if name == &FontName::cursive() {
            Some(self.cursive(lang))
        } else if name == &FontName::fantasy() {
            Some(self.fantasy(lang))
        } else {
            None
        }
    }
}
#[derive(Clone)]
#[allow(clippy::rc_buffer)]
pub struct FontDataRef(pub Arc<Vec<u8>>);
impl FontDataRef {
    pub fn from_static(data: &'static [u8]) -> Self {
        FontDataRef(Arc::new(data.to_vec()))
    }
}
impl fmt::Debug for FontDataRef {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "FontDataRef(Arc<{} bytes>>)", self.0.len())
    }
}
impl std::ops::Deref for FontDataRef {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}
impl From<FontDataRef> for harfbuzz_rs::Shared<harfbuzz_rs::Blob<'static>> {
    fn from(d: FontDataRef) -> Self {
        harfbuzz_rs::Blob::with_bytes_owned(d, |d| d).to_shared()
    }
}
#[derive(Debug, Clone)]
enum FontSource {
    File(PathBuf, u32),
    Memory(FontDataRef, u32),
    Alias(FontName),
}
#[derive(Debug, Clone)]
pub struct CustomFont {
    name: FontName,
    source: FontSource,
    stretch: FontStretch,
    style: FontStyle,
    weight: FontWeight,
}
impl CustomFont {
    pub fn from_file<N: Into<FontName>, P: Into<PathBuf>>(name: N, path: P, font_index: u32) -> Self {
        CustomFont {
            name: name.into(),
            source: FontSource::File(path.into(), font_index),
            stretch: FontStretch::NORMAL,
            style: FontStyle::Normal,
            weight: FontWeight::NORMAL,
        }
    }
    pub fn from_bytes<N: Into<FontName>>(name: N, data: FontDataRef, font_index: u32) -> Self {
        CustomFont {
            name: name.into(),
            source: FontSource::Memory(data, font_index),
            stretch: FontStretch::NORMAL,
            style: FontStyle::Normal,
            weight: FontWeight::NORMAL,
        }
    }
    pub fn from_other<N: Into<FontName>, O: Into<FontName>>(name: N, other_font: O) -> Self {
        CustomFont {
            name: name.into(),
            source: FontSource::Alias(other_font.into()),
            stretch: FontStretch::NORMAL,
            style: FontStyle::Normal,
            weight: FontWeight::NORMAL,
        }
    }
    pub fn stretch(mut self, stretch: FontStretch) -> Self {
        self.stretch = stretch;
        self
    }
    pub fn style(mut self, style: FontStyle) -> Self {
        self.style = style;
        self
    }
    pub fn weight(mut self, weight: FontWeight) -> Self {
        self.weight = weight;
        self
    }
}
impl From<font_kit::family_name::FamilyName> for FontName {
    fn from(family_name: font_kit::family_name::FamilyName) -> Self {
        use font_kit::family_name::FamilyName::*;
        match family_name {
            Title(title) => FontName::new(title),
            Serif => FontName::serif(),
            SansSerif => FontName::sans_serif(),
            Monospace => FontName::monospace(),
            Cursive => FontName::cursive(),
            Fantasy => FontName::fantasy(),
        }
    }
}
impl From<FontName> for font_kit::family_name::FamilyName {
    fn from(font_name: FontName) -> Self {
        use font_kit::family_name::FamilyName::*;
        match font_name.name() {
            "serif" => Serif,
            "sans-serif" => SansSerif,
            "monospace" => Monospace,
            "cursive" => Cursive,
            "fantasy" => Fantasy,
            _ => Title(font_name.text.into()),
        }
    }
}
#[derive(Clone, Copy, serde::Serialize, serde::Deserialize, Transitionable)]
#[serde(transparent)]
pub struct FontStretch(pub f32);
impl fmt::Debug for FontStretch {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = self.name();
        if name.is_empty() {
            f.debug_tuple("FontStretch").field(&self.0).finish()
        } else {
            if f.alternate() {
                write!(f, "FontStretch::")?;
            }
            write!(f, "{name}")
        }
    }
}
impl PartialOrd for FontStretch {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(about_eq_ord(self.0, other.0, EQ_EPSILON))
    }
}
impl PartialEq for FontStretch {
    fn eq(&self, other: &Self) -> bool {
        about_eq(self.0, other.0, EQ_EPSILON)
    }
}
impl std::hash::Hash for FontStretch {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        about_eq_hash(self.0, EQ_EPSILON, state)
    }
}
impl Default for FontStretch {
    fn default() -> FontStretch {
        FontStretch::NORMAL
    }
}
impl FontStretch {
    pub const ULTRA_CONDENSED: FontStretch = FontStretch(0.5);
    pub const EXTRA_CONDENSED: FontStretch = FontStretch(0.625);
    pub const CONDENSED: FontStretch = FontStretch(0.75);
    pub const SEMI_CONDENSED: FontStretch = FontStretch(0.875);
    pub const NORMAL: FontStretch = FontStretch(1.0);
    pub const SEMI_EXPANDED: FontStretch = FontStretch(1.125);
    pub const EXPANDED: FontStretch = FontStretch(1.25);
    pub const EXTRA_EXPANDED: FontStretch = FontStretch(1.5);
    pub const ULTRA_EXPANDED: FontStretch = FontStretch(2.0);
    pub fn name(self) -> &'static str {
        macro_rules! name {
            ($($CONST:ident;)+) => {$(
                if self == Self::$CONST {
                    return stringify!($CONST);
                }
            )+}
        }
        name! {
            ULTRA_CONDENSED;
            EXTRA_CONDENSED;
            CONDENSED;
            SEMI_CONDENSED;
            NORMAL;
            SEMI_EXPANDED;
            EXPANDED;
            EXTRA_EXPANDED;
            ULTRA_EXPANDED;
        }
        ""
    }
}
impl_from_and_into_var! {
    fn from(fct: Factor) -> FontStretch {
        FontStretch(fct.0)
    }
    fn from(pct: FactorPercent) -> FontStretch {
        FontStretch(pct.fct().0)
    }
    fn from(fct: f32) -> FontStretch {
        FontStretch(fct)
    }
}
impl From<FontStretch> for font_kit::properties::Stretch {
    fn from(value: FontStretch) -> Self {
        font_kit::properties::Stretch(value.0)
    }
}
impl From<font_kit::properties::Stretch> for FontStretch {
    fn from(value: font_kit::properties::Stretch) -> Self {
        FontStretch(value.0)
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
pub enum FontStyle {
    #[default]
    Normal,
    Italic,
    Oblique,
}
impl fmt::Debug for FontStyle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "FontStyle::")?;
        }
        match self {
            Self::Normal => write!(f, "Normal"),
            Self::Italic => write!(f, "Italic"),
            Self::Oblique => write!(f, "Oblique"),
        }
    }
}
impl From<FontStyle> for font_kit::properties::Style {
    fn from(value: FontStyle) -> Self {
        use font_kit::properties::Style::*;
        match value {
            FontStyle::Normal => Normal,
            FontStyle::Italic => Italic,
            FontStyle::Oblique => Oblique,
        }
    }
}
impl From<font_kit::properties::Style> for FontStyle {
    fn from(value: font_kit::properties::Style) -> Self {
        use font_kit::properties::Style::*;
        match value {
            Normal => FontStyle::Normal,
            Italic => FontStyle::Italic,
            Oblique => FontStyle::Oblique,
        }
    }
}
#[derive(Clone, Copy, Transitionable, serde::Serialize, serde::Deserialize)]
pub struct FontWeight(pub f32);
impl Default for FontWeight {
    fn default() -> FontWeight {
        FontWeight::NORMAL
    }
}
impl fmt::Debug for FontWeight {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = self.name();
        if name.is_empty() {
            f.debug_tuple("FontWeight").field(&self.0).finish()
        } else {
            if f.alternate() {
                write!(f, "FontWeight::")?;
            }
            write!(f, "{name}")
        }
    }
}
impl PartialOrd for FontWeight {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(about_eq_ord(self.0, other.0, EQ_EPSILON_100))
    }
}
impl PartialEq for FontWeight {
    fn eq(&self, other: &Self) -> bool {
        about_eq(self.0, other.0, EQ_EPSILON_100)
    }
}
impl std::hash::Hash for FontWeight {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        about_eq_hash(self.0, EQ_EPSILON_100, state)
    }
}
impl FontWeight {
    pub const THIN: FontWeight = FontWeight(100.0);
    pub const EXTRA_LIGHT: FontWeight = FontWeight(200.0);
    pub const LIGHT: FontWeight = FontWeight(300.0);
    pub const NORMAL: FontWeight = FontWeight(400.0);
    pub const MEDIUM: FontWeight = FontWeight(500.0);
    pub const SEMIBOLD: FontWeight = FontWeight(600.0);
    pub const BOLD: FontWeight = FontWeight(700.0);
    pub const EXTRA_BOLD: FontWeight = FontWeight(800.0);
    pub const BLACK: FontWeight = FontWeight(900.0);
    pub fn name(self) -> &'static str {
        macro_rules! name {
                ($($CONST:ident;)+) => {$(
                    if self == Self::$CONST {
                        return stringify!($CONST);
                    }
                )+}
            }
        name! {
            THIN;
            EXTRA_LIGHT;
            LIGHT;
            NORMAL;
            MEDIUM;
            SEMIBOLD;
            BOLD;
            EXTRA_BOLD;
            BLACK;
        }
        ""
    }
}
impl_from_and_into_var! {
    fn from(weight: u32) -> FontWeight {
        FontWeight(weight as f32)
    }
    fn from(weight: f32) -> FontWeight {
        FontWeight(weight)
    }
}
impl From<FontWeight> for font_kit::properties::Weight {
    fn from(value: FontWeight) -> Self {
        font_kit::properties::Weight(value.0)
    }
}
impl From<font_kit::properties::Weight> for FontWeight {
    fn from(value: font_kit::properties::Weight) -> Self {
        FontWeight(value.0)
    }
}
#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum LineBreak {
    Auto,
    Loose,
    Normal,
    Strict,
    Anywhere,
}
impl Default for LineBreak {
    fn default() -> Self {
        LineBreak::Auto
    }
}
impl fmt::Debug for LineBreak {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "LineBreak::")?;
        }
        match self {
            LineBreak::Auto => write!(f, "Auto"),
            LineBreak::Loose => write!(f, "Loose"),
            LineBreak::Normal => write!(f, "Normal"),
            LineBreak::Strict => write!(f, "Strict"),
            LineBreak::Anywhere => write!(f, "Anywhere"),
        }
    }
}
#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Hyphens {
    None,
    Manual,
    Auto,
}
impl Default for Hyphens {
    fn default() -> Self {
        Hyphens::Auto
    }
}
impl fmt::Debug for Hyphens {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "Hyphens::")?;
        }
        match self {
            Hyphens::None => write!(f, "None"),
            Hyphens::Manual => write!(f, "Manual"),
            Hyphens::Auto => write!(f, "Auto"),
        }
    }
}
#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum WordBreak {
    Normal,
    BreakAll,
    KeepAll,
}
impl Default for WordBreak {
    fn default() -> Self {
        WordBreak::Normal
    }
}
impl fmt::Debug for WordBreak {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "WordBreak::")?;
        }
        match self {
            WordBreak::Normal => write!(f, "Normal"),
            WordBreak::BreakAll => write!(f, "BreakAll"),
            WordBreak::KeepAll => write!(f, "KeepAll"),
        }
    }
}
#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Justify {
    Auto,
    InterWord,
    InterLetter,
}
impl Default for Justify {
    fn default() -> Self {
        Justify::Auto
    }
}
impl fmt::Debug for Justify {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "Justify::")?;
        }
        match self {
            Justify::Auto => write!(f, "Auto"),
            Justify::InterWord => write!(f, "InterWord"),
            Justify::InterLetter => write!(f, "InterLetter"),
        }
    }
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct FontFaceMetrics {
    pub units_per_em: u32,
    pub ascent: f32,
    pub descent: f32,
    pub line_gap: f32,
    pub underline_position: f32,
    pub underline_thickness: f32,
    pub cap_height: f32,
    pub x_height: f32,
    pub bounds: euclid::Rect<f32, ()>,
}
impl FontFaceMetrics {
    pub fn sized(&self, font_size_px: Px) -> FontMetrics {
        let size_scale = 1.0 / self.units_per_em as f32 * font_size_px.0 as f32;
        let s = move |f: f32| Px((f * size_scale).round() as i32);
        FontMetrics {
            size_scale,
            ascent: s(self.ascent),
            descent: s(self.descent),
            line_gap: s(self.line_gap),
            underline_position: s(self.underline_position),
            underline_thickness: s(self.underline_thickness),
            cap_height: s(self.cap_height),
            x_height: (s(self.x_height)),
            bounds: {
                let b = self.bounds;
                PxRect::new(
                    PxPoint::new(s(b.origin.x), s(b.origin.y)),
                    PxSize::new(s(b.size.width), s(b.size.height)),
                )
            },
        }
    }
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct FontMetrics {
    pub size_scale: f32,
    pub ascent: Px,
    pub descent: Px,
    pub line_gap: Px,
    pub underline_position: Px,
    pub underline_thickness: Px,
    pub cap_height: Px,
    pub x_height: Px,
    pub bounds: PxRect,
}
impl FontMetrics {
    pub fn line_height(&self) -> Px {
        self.ascent - self.descent + self.line_gap
    }
}
#[derive(Clone)]
pub enum TextTransformFn {
    None,
    Uppercase,
    Lowercase,
    Custom(Arc<dyn Fn(&Txt) -> Cow<Txt> + Send + Sync>),
}
impl TextTransformFn {
    pub fn transform<'t>(&self, text: &'t Txt) -> Cow<'t, Txt> {
        match self {
            TextTransformFn::None => Cow::Borrowed(text),
            TextTransformFn::Uppercase => {
                if text.chars().any(|c| !c.is_uppercase()) {
                    Cow::Owned(text.to_uppercase().into())
                } else {
                    Cow::Borrowed(text)
                }
            }
            TextTransformFn::Lowercase => {
                if text.chars().any(|c| !c.is_lowercase()) {
                    Cow::Owned(text.to_lowercase().into())
                } else {
                    Cow::Borrowed(text)
                }
            }
            TextTransformFn::Custom(fn_) => fn_(text),
        }
    }
    pub fn custom(fn_: impl Fn(&Txt) -> Cow<Txt> + Send + Sync + 'static) -> Self {
        TextTransformFn::Custom(Arc::new(fn_))
    }
}
impl fmt::Debug for TextTransformFn {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if f.alternate() {
            write!(f, "TextTransformFn::")?;
        }
        match self {
            TextTransformFn::None => write!(f, "None"),
            TextTransformFn::Uppercase => write!(f, "Uppercase"),
            TextTransformFn::Lowercase => write!(f, "Lowercase"),
            TextTransformFn::Custom(_) => write!(f, "Custom"),
        }
    }
}
impl PartialEq for TextTransformFn {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Custom(l0), Self::Custom(r0)) => Arc::ptr_eq(l0, r0),
            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
        }
    }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum WhiteSpace {
    Preserve,
    Merge,
    MergeAll,
}
impl Default for WhiteSpace {
    fn default() -> Self {
        WhiteSpace::Preserve
    }
}
impl WhiteSpace {
    pub fn transform(self, text: &Txt) -> Cow<Txt> {
        match self {
            WhiteSpace::Preserve => Cow::Borrowed(text),
            WhiteSpace::Merge => {
                let is_white_space = |c: char| c.is_whitespace() && !"\n\r\u{85}".contains(c);
                let t = text.trim_matches(&is_white_space);
                let mut prev_space = false;
                for c in t.chars() {
                    if is_white_space(c) {
                        if prev_space || c != '\u{20}' {
                            let mut r = String::new();
                            let mut sep = "";
                            for part in t.split(is_white_space).filter(|s| !s.is_empty()) {
                                r.push_str(sep);
                                r.push_str(part);
                                sep = "\u{20}";
                            }
                            return Cow::Owned(Txt::from_str(&r));
                        } else {
                            prev_space = true;
                        }
                    } else {
                        prev_space = false;
                    }
                }
                if t.len() != text.len() {
                    Cow::Owned(Txt::from_str(t))
                } else {
                    Cow::Borrowed(text)
                }
            }
            WhiteSpace::MergeAll => {
                let t = text.trim();
                let mut prev_space = false;
                for c in t.chars() {
                    if c.is_whitespace() {
                        if prev_space || c != '\u{20}' {
                            let mut r = String::new();
                            let mut sep = "";
                            for part in t.split_whitespace() {
                                r.push_str(sep);
                                r.push_str(part);
                                sep = "\u{20}";
                            }
                            return Cow::Owned(Txt::from_str(&r));
                        } else {
                            prev_space = true;
                        }
                    } else {
                        prev_space = false;
                    }
                }
                if t.len() != text.len() {
                    Cow::Owned(Txt::from_str(t))
                } else {
                    Cow::Borrowed(text)
                }
            }
        }
    }
}
impl fmt::Debug for WhiteSpace {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            write!(f, "WhiteSpace::")?;
        }
        match self {
            WhiteSpace::Preserve => write!(f, "Preserve"),
            WhiteSpace::Merge => write!(f, "Merge"),
            WhiteSpace::MergeAll => write!(f, "MergeAll"),
        }
    }
}
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub struct CaretIndex {
    pub index: usize,
    pub line: usize,
}
impl PartialEq for CaretIndex {
    fn eq(&self, other: &Self) -> bool {
        self.index == other.index
    }
}
impl Eq for CaretIndex {}
impl CaretIndex {
    pub const ZERO: CaretIndex = CaretIndex { index: 0, line: 0 };
}
impl PartialOrd for CaretIndex {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for CaretIndex {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.index.cmp(&other.index)
    }
}
#[derive(Debug, Clone)]
pub enum FontLoadingError {
    UnknownFormat,
    NoSuchFontInCollection,
    Parse,
    NoFilesystem,
    Io(Arc<std::io::Error>),
}
impl PartialEq for FontLoadingError {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Io(l0), Self::Io(r0)) => Arc::ptr_eq(l0, r0),
            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
        }
    }
}
impl From<font_kit::error::FontLoadingError> for FontLoadingError {
    fn from(ve: font_kit::error::FontLoadingError) -> Self {
        match ve {
            font_kit::error::FontLoadingError::UnknownFormat => Self::UnknownFormat,
            font_kit::error::FontLoadingError::NoSuchFontInCollection => Self::NoSuchFontInCollection,
            font_kit::error::FontLoadingError::Parse => Self::Parse,
            font_kit::error::FontLoadingError::NoFilesystem => Self::NoFilesystem,
            font_kit::error::FontLoadingError::Io(e) => Self::Io(Arc::new(e)),
        }
    }
}
impl From<std::io::Error> for FontLoadingError {
    fn from(error: std::io::Error) -> FontLoadingError {
        Self::Io(Arc::new(error))
    }
}
impl fmt::Display for FontLoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let e = match self {
            Self::UnknownFormat => font_kit::error::FontLoadingError::UnknownFormat,
            Self::NoSuchFontInCollection => font_kit::error::FontLoadingError::NoSuchFontInCollection,
            Self::Parse => font_kit::error::FontLoadingError::Parse,
            Self::NoFilesystem => font_kit::error::FontLoadingError::NoFilesystem,
            Self::Io(e) => {
                return fmt::Display::fmt(e, f);
            }
        };
        fmt::Display::fmt(&e, f)
    }
}
impl std::error::Error for FontLoadingError {
    fn cause(&self) -> Option<&dyn std::error::Error> {
        match self {
            FontLoadingError::Io(e) => Some(e),
            _ => None,
        }
    }
}
#[cfg(test)]
mod tests {
    use zng_app::APP;
    use super::*;
    #[test]
    fn generic_fonts_default() {
        let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
        assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(und)))
    }
    #[test]
    fn generic_fonts_fallback() {
        let _app = APP.minimal().extend(FontManager::default()).run_headless(false);
        assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(en_US)));
        assert_eq!(FontName::sans_serif(), GenericFonts {}.sans_serif(&lang!(es)));
    }
    #[test]
    fn generic_fonts_get1() {
        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
        GenericFonts {}.set_sans_serif(lang!(en_US), "Test Value");
        app.update(false).assert_wait();
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
    }
    #[test]
    fn generic_fonts_get2() {
        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
        GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
        app.update(false).assert_wait();
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Test Value");
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
    }
    #[test]
    fn generic_fonts_get_best() {
        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
        GenericFonts {}.set_sans_serif(lang!(en), "Test Value");
        GenericFonts {}.set_sans_serif(lang!(en_US), "Best");
        app.update(false).assert_wait();
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "Best");
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en")), "Test Value");
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("und")), "sans-serif");
    }
    #[test]
    fn generic_fonts_get_no_lang_match() {
        let mut app = APP.minimal().extend(FontManager::default()).run_headless(false);
        GenericFonts {}.set_sans_serif(lang!(es_US), "Test Value");
        app.update(false).assert_wait();
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("en-US")), "sans-serif");
        assert_eq!(&GenericFonts {}.sans_serif(&lang!("es")), "Test Value");
    }
}