font_features.rs

  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);