1use crate::SharedString;
2use itertools::Itertools;
3use schemars::{
4 schema::{InstanceType, Schema, SchemaObject, SingleOrVec},
5 JsonSchema,
6};
7
8macro_rules! create_definitions {
9 ($($(#[$meta:meta])* ($name:ident, $idx:expr)),* $(,)?) => {
10
11 /// The OpenType features that can be configured for a given font.
12 #[derive(Default, Clone, Eq, PartialEq, Hash)]
13 pub struct FontFeatures {
14 enabled: u64,
15 disabled: u64,
16 other_enabled: SharedString,
17 other_disabled: SharedString,
18 }
19
20 impl FontFeatures {
21 $(
22 /// Get the current value of the corresponding OpenType feature
23 pub fn $name(&self) -> Option<bool> {
24 if (self.enabled & (1 << $idx)) != 0 {
25 Some(true)
26 } else if (self.disabled & (1 << $idx)) != 0 {
27 Some(false)
28 } else {
29 None
30 }
31 }
32 )*
33
34 /// Get the tag name list of the font OpenType features
35 /// only enabled or disabled features are returned
36 pub fn tag_value_list(&self) -> Vec<(String, bool)> {
37 let mut result = Vec::new();
38 $(
39 {
40 let value = if (self.enabled & (1 << $idx)) != 0 {
41 Some(true)
42 } else if (self.disabled & (1 << $idx)) != 0 {
43 Some(false)
44 } else {
45 None
46 };
47 if let Some(enable) = value {
48 let tag_name = stringify!($name).to_owned();
49 result.push((tag_name, enable));
50 }
51 }
52 )*
53 {
54 for name in self.other_enabled.as_ref().chars().chunks(4).into_iter() {
55 result.push((name.collect::<String>(), true));
56 }
57 for name in self.other_disabled.as_ref().chars().chunks(4).into_iter() {
58 result.push((name.collect::<String>(), false));
59 }
60 }
61 result
62 }
63 }
64
65 impl std::fmt::Debug for FontFeatures {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 let mut debug = f.debug_struct("FontFeatures");
68 $(
69 if let Some(value) = self.$name() {
70 debug.field(stringify!($name), &value);
71 };
72 )*
73 #[cfg(target_os = "windows")]
74 {
75 for name in self.other_enabled.as_ref().chars().chunks(4).into_iter() {
76 debug.field(name.collect::<String>().as_str(), &true);
77 }
78 for name in self.other_disabled.as_ref().chars().chunks(4).into_iter() {
79 debug.field(name.collect::<String>().as_str(), &false);
80 }
81 }
82 debug.finish()
83 }
84 }
85
86 impl<'de> serde::Deserialize<'de> for FontFeatures {
87 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88 where
89 D: serde::Deserializer<'de>,
90 {
91 use serde::de::{MapAccess, Visitor};
92 use std::fmt;
93
94 struct FontFeaturesVisitor;
95
96 impl<'de> Visitor<'de> for FontFeaturesVisitor {
97 type Value = FontFeatures;
98
99 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
100 formatter.write_str("a map of font features")
101 }
102
103 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
104 where
105 M: MapAccess<'de>,
106 {
107 let mut enabled: u64 = 0;
108 let mut disabled: u64 = 0;
109 let mut other_enabled = "".to_owned();
110 let mut other_disabled = "".to_owned();
111
112 while let Some((key, value)) = access.next_entry::<String, Option<bool>>()? {
113 let idx = match key.as_str() {
114 $(stringify!($name) => Some($idx),)*
115 other_feature => {
116 if other_feature.len() != 4 || !other_feature.is_ascii() {
117 log::error!("Incorrect feature name: {}", other_feature);
118 continue;
119 }
120 None
121 },
122 };
123 if let Some(idx) = idx {
124 match value {
125 Some(true) => enabled |= 1 << idx,
126 Some(false) => disabled |= 1 << idx,
127 None => {}
128 };
129 } else {
130 match value {
131 Some(true) => other_enabled.push_str(key.as_str()),
132 Some(false) => other_disabled.push_str(key.as_str()),
133 None => {}
134 };
135 }
136 }
137 let other_enabled = if other_enabled.is_empty() {
138 "".into()
139 } else {
140 other_enabled.into()
141 };
142 let other_disabled = if other_disabled.is_empty() {
143 "".into()
144 } else {
145 other_disabled.into()
146 };
147 Ok(FontFeatures { enabled, disabled, other_enabled, other_disabled })
148 }
149 }
150
151 let features = deserializer.deserialize_map(FontFeaturesVisitor)?;
152 Ok(features)
153 }
154 }
155
156 impl serde::Serialize for FontFeatures {
157 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
158 where
159 S: serde::Serializer,
160 {
161 use serde::ser::SerializeMap;
162
163 let mut map = serializer.serialize_map(None)?;
164
165 $(
166 {
167 let feature = stringify!($name);
168 if let Some(value) = self.$name() {
169 map.serialize_entry(feature, &value)?;
170 }
171 }
172 )*
173
174 #[cfg(target_os = "windows")]
175 {
176 for name in self.other_enabled.as_ref().chars().chunks(4).into_iter() {
177 map.serialize_entry(name.collect::<String>().as_str(), &true)?;
178 }
179 for name in self.other_disabled.as_ref().chars().chunks(4).into_iter() {
180 map.serialize_entry(name.collect::<String>().as_str(), &false)?;
181 }
182 }
183
184 map.end()
185 }
186 }
187
188 impl JsonSchema for FontFeatures {
189 fn schema_name() -> String {
190 "FontFeatures".into()
191 }
192
193 fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> Schema {
194 let mut schema = SchemaObject::default();
195 let properties = &mut schema.object().properties;
196 let feature_schema = Schema::Object(SchemaObject {
197 instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Boolean))),
198 ..Default::default()
199 });
200
201 $(
202 properties.insert(stringify!($name).to_owned(), feature_schema.clone());
203 )*
204
205 schema.into()
206 }
207 }
208 };
209}
210
211create_definitions!(
212 (calt, 0),
213 (case, 1),
214 (cpsp, 2),
215 (frac, 3),
216 (liga, 4),
217 (onum, 5),
218 (ordn, 6),
219 (pnum, 7),
220 (ss01, 8),
221 (ss02, 9),
222 (ss03, 10),
223 (ss04, 11),
224 (ss05, 12),
225 (ss06, 13),
226 (ss07, 14),
227 (ss08, 15),
228 (ss09, 16),
229 (ss10, 17),
230 (ss11, 18),
231 (ss12, 19),
232 (ss13, 20),
233 (ss14, 21),
234 (ss15, 22),
235 (ss16, 23),
236 (ss17, 24),
237 (ss18, 25),
238 (ss19, 26),
239 (ss20, 27),
240 (subs, 28),
241 (sups, 29),
242 (swsh, 30),
243 (titl, 31),
244 (tnum, 32),
245 (zero, 33),
246);