font_features.rs

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