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}