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}