font_features.rs

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