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