gpui/text_system/
font_features.rs

1use std::borrow::Cow;
2use std::sync::Arc;
3
4use schemars::{JsonSchema, json_schema};
5
6/// The OpenType features that can be configured for a given font.
7#[derive(Default, Clone, Eq, PartialEq, Hash)]
8pub struct FontFeatures(pub Arc<Vec<(String, u32)>>);
9
10impl FontFeatures {
11    /// Disables `calt`.
12    pub fn disable_ligatures() -> Self {
13        Self(Arc::new(vec![("calt".into(), 0)]))
14    }
15
16    /// Get the tag name list of the font OpenType features
17    /// only enabled or disabled features are returned
18    pub fn tag_value_list(&self) -> &[(String, u32)] {
19        self.0.as_slice()
20    }
21
22    /// Returns whether the `calt` feature is enabled.
23    ///
24    /// Returns `None` if the feature is not present.
25    pub fn is_calt_enabled(&self) -> Option<bool> {
26        self.0
27            .iter()
28            .find(|(feature, _)| feature == "calt")
29            .map(|(_, value)| *value == 1)
30    }
31}
32
33impl std::fmt::Debug for FontFeatures {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        let mut debug = f.debug_struct("FontFeatures");
36        for (tag, value) in self.tag_value_list() {
37            debug.field(tag, value);
38        }
39
40        debug.finish()
41    }
42}
43
44#[derive(Debug, serde::Serialize, serde::Deserialize)]
45#[serde(untagged)]
46enum FeatureValue {
47    Bool(bool),
48    Number(serde_json::Number),
49}
50
51impl<'de> serde::Deserialize<'de> for FontFeatures {
52    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53    where
54        D: serde::Deserializer<'de>,
55    {
56        use serde::de::{MapAccess, Visitor};
57        use std::fmt;
58
59        struct FontFeaturesVisitor;
60
61        impl<'de> Visitor<'de> for FontFeaturesVisitor {
62            type Value = FontFeatures;
63
64            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
65                formatter.write_str("a map of font features")
66            }
67
68            fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
69            where
70                M: MapAccess<'de>,
71            {
72                let mut feature_list = Vec::new();
73
74                while let Some((key, value)) =
75                    access.next_entry::<String, Option<FeatureValue>>()?
76                {
77                    if !is_valid_feature_tag(&key) {
78                        log::error!("Incorrect font feature tag: {}", key);
79                        continue;
80                    }
81                    if let Some(value) = value {
82                        match value {
83                            FeatureValue::Bool(enable) => {
84                                if enable {
85                                    feature_list.push((key, 1));
86                                } else {
87                                    feature_list.push((key, 0));
88                                }
89                            }
90                            FeatureValue::Number(value) => {
91                                if value.is_u64() {
92                                    feature_list.push((key, value.as_u64().unwrap() as u32));
93                                } else {
94                                    log::error!(
95                                        "Incorrect font feature value {} for feature tag {}",
96                                        value,
97                                        key
98                                    );
99                                    continue;
100                                }
101                            }
102                        }
103                    }
104                }
105
106                Ok(FontFeatures(Arc::new(feature_list)))
107            }
108        }
109
110        let features = deserializer.deserialize_map(FontFeaturesVisitor)?;
111        Ok(features)
112    }
113}
114
115impl serde::Serialize for FontFeatures {
116    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117    where
118        S: serde::Serializer,
119    {
120        use serde::ser::SerializeMap;
121
122        let mut map = serializer.serialize_map(None)?;
123
124        for (tag, value) in self.tag_value_list() {
125            map.serialize_entry(tag, value)?;
126        }
127
128        map.end()
129    }
130}
131
132impl JsonSchema for FontFeatures {
133    fn schema_name() -> Cow<'static, str> {
134        "FontFeatures".into()
135    }
136
137    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
138        json_schema!({
139            "type": "object",
140            "patternProperties": {
141                "[0-9a-zA-Z]{4}$": {
142                    "type": ["boolean", "integer"],
143                    "minimum": 0,
144                    "multipleOf": 1
145                }
146            },
147            "additionalProperties": false
148        })
149    }
150}
151
152fn is_valid_feature_tag(tag: &str) -> bool {
153    tag.len() == 4 && tag.chars().all(|c| c.is_ascii_alphanumeric())
154}