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