zng_ext_font/
font_features.rs

1//! Font features and variation types.
2
3use std::{
4    collections::{HashMap, HashSet, hash_map},
5    fmt,
6    marker::PhantomData,
7    num::NonZeroU32,
8    ops,
9};
10
11use num_enum::FromPrimitive;
12
13/// Name of a font feature.
14///
15/// # Examples
16///
17/// ```
18/// # use zng_ext_font::font_features::*;
19/// let historical_lig: FontFeatureName = b"hlig".into();
20/// ```
21#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
22pub struct FontFeatureName(pub [u8; 4]);
23impl FontFeatureName {
24    /// As UTF-8.
25    pub fn as_str(&self) -> &str {
26        std::str::from_utf8(&self.0).unwrap_or_default()
27    }
28}
29impl From<&'static [u8; 4]> for FontFeatureName {
30    fn from(name: &'static [u8; 4]) -> Self {
31        FontFeatureName(*name)
32    }
33}
34impl From<FontFeatureName> for ttf_parser::Tag {
35    fn from(value: FontFeatureName) -> Self {
36        ttf_parser::Tag::from_bytes(&value.0)
37    }
38}
39impl ops::Deref for FontFeatureName {
40    type Target = [u8; 4];
41
42    fn deref(&self) -> &Self::Target {
43        &self.0
44    }
45}
46impl fmt::Debug for FontFeatureName {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        if self.as_str().is_empty() {
49            write!(f, "{:?}", self.0)
50        } else {
51            write!(f, "{}", self.as_str())
52        }
53    }
54}
55impl fmt::Display for FontFeatureName {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "{self:?}")
58    }
59}
60
61/// The raw value used when a feature is set to `true`.
62pub const FEATURE_ENABLED: u32 = 1;
63/// The raw value used when a feature is set to `false`.
64pub const FEATURE_DISABLED: u32 = 0;
65
66type FontFeaturesMap = HashMap<FontFeatureName, u32>;
67
68/// Font features configuration.
69#[derive(Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
70pub struct FontFeatures(FontFeaturesMap);
71impl FontFeatures {
72    /// New default.
73    pub fn new() -> FontFeatures {
74        FontFeatures::default()
75    }
76
77    /// New builder.
78    pub fn builder() -> FontFeaturesBuilder {
79        FontFeaturesBuilder::default()
80    }
81
82    /// Set or override the features of `self` from `other`.
83    ///
84    /// Returns the previous state of all affected names.
85    pub fn set_all(&mut self, other: &FontFeatures) -> Vec<(FontFeatureName, Option<u32>)> {
86        let mut prev = Vec::with_capacity(other.0.len());
87        for (&name, &state) in other.0.iter() {
88            prev.push((name, self.0.insert(name, state)));
89        }
90        prev
91    }
92
93    /// Restore feature states that where overridden in [`set_all`](Self::set_all).
94    pub fn restore(&mut self, prev: Vec<(FontFeatureName, Option<u32>)>) {
95        for (name, state) in prev {
96            match state {
97                Some(state) => {
98                    self.0.insert(name, state);
99                }
100                None => {
101                    self.0.remove(&name);
102                }
103            }
104        }
105    }
106
107    /// Access to the named feature.
108    pub fn feature(&mut self, name: FontFeatureName) -> FontFeature {
109        FontFeature(self.0.entry(name))
110    }
111
112    /// Access to a set of named features that are managed together.
113    ///
114    /// # Panics
115    ///
116    /// If `names` has less than 2 names.
117    pub fn feature_set(&mut self, names: &'static [FontFeatureName]) -> FontFeatureSet {
118        assert!(names.len() >= 2);
119        FontFeatureSet {
120            features: &mut self.0,
121            names,
122        }
123    }
124
125    /// Access to a set of named features where only one of the features can be enabled at a time.
126    ///
127    /// # Panics
128    ///
129    /// If `S::names()` has less than 2 names.
130    pub fn feature_exclusive_set<S: FontFeatureExclusiveSetState>(&mut self) -> FontFeatureExclusiveSet<S> {
131        assert!(S::names().len() >= 2);
132        FontFeatureExclusiveSet {
133            features: &mut self.0,
134            _t: PhantomData,
135        }
136    }
137
138    /// Access to a set of named features where only one or more features can be enabled but each combination
139    /// represents a single distinct *state*.
140    ///
141    /// # Panics
142    ///
143    /// If `S::names()` has less than 2 entries.
144    pub fn feature_exclusive_sets<S: FontFeatureExclusiveSetsState>(&mut self) -> FontFeatureExclusiveSets<S> {
145        assert!(S::names().len() >= 2);
146        FontFeatureExclusiveSets {
147            features: &mut self.0,
148            _t: PhantomData,
149        }
150    }
151
152    /// Generate the harfbuzz font features.
153    pub fn finalize(&self) -> RFontFeatures {
154        self.0
155            .iter()
156            .map(|(&n, &s)| rustybuzz::Feature::new(ttf_parser::Tag::from(n), s, 0..usize::MAX))
157            .collect()
158    }
159}
160impl fmt::Debug for FontFeatures {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        let mut map = f.debug_map();
163        for (name, state) in self.0.iter() {
164            map.entry(&name.as_str(), state);
165        }
166        map.finish()
167    }
168}
169
170/// Finalized [`FontFeatures`].
171///
172/// This is a vec of [harfbuzz features](https://docs.rs/rustybuzz/0.17.0/rustybuzz/struct.Feature.html).
173pub type RFontFeatures = Vec<rustybuzz::Feature>;
174
175/// A builder for [`FontFeatures`].
176///
177/// # Examples
178///
179/// ```
180/// # use zng_ext_font::font_features::*;
181/// let features = FontFeatures::builder().kerning(false).build();
182/// ```
183#[derive(Default)]
184pub struct FontFeaturesBuilder(FontFeatures);
185impl FontFeaturesBuilder {
186    /// Finish building.
187    pub fn build(self) -> FontFeatures {
188        self.0
189    }
190
191    /// Set the named feature.
192    pub fn feature(mut self, name: FontFeatureName, state: impl Into<FontFeatureState>) -> Self {
193        self.0.feature(name).set(state);
194        self
195    }
196
197    /// Sets all the named features to the same value.
198    ///
199    /// # Panics
200    ///
201    /// If `names` has less than 2 names.
202    pub fn feature_set(mut self, names: &'static [FontFeatureName], state: impl Into<FontFeatureState>) -> Self {
203        self.0.feature_set(names).set(state);
204        self
205    }
206
207    /// Sets a single feature of a set of features.
208    ///
209    /// # Panics
210    ///
211    /// If `S::names()` has less than 2 names.
212    pub fn feature_exclusive_set<S: FontFeatureExclusiveSetState>(mut self, state: impl Into<S>) -> Self {
213        self.0.feature_exclusive_set::<S>().set(state);
214        self
215    }
216
217    /// Sets the features that represent the `state`.
218    ///
219    /// # Panics
220    ///
221    /// If `S::names()` has less than 2 entries.
222    pub fn feature_exclusive_sets<S: FontFeatureExclusiveSetsState>(mut self, state: impl Into<S>) -> Self {
223        self.0.feature_exclusive_sets::<S>().set(state);
224        self
225    }
226}
227
228/// Generate `FontFeature` methods in `FontFeatures` and builder methods in `FontFeaturesBuilder`
229/// that set the feature.
230macro_rules! font_features {
231    ($(
232        $(#[$docs:meta])*
233        fn $name:ident($feat0_or_Enum:tt $(, $feat1:tt)?) $(-> $Helper:ident)?;
234    )+) => {
235        impl FontFeatures {$(
236            font_features!{feature $(#[$docs])* fn $name($feat0_or_Enum $(, $feat1)?) $(-> $Helper)?; }
237        )+}
238
239        impl FontFeaturesBuilder {$(
240            font_features!{builder $(#[$docs])* fn $name($($feat0_or_Enum -> $Helper)?); }
241        )+}
242    };
243
244    (feature $(#[$docs:meta])* fn $name:ident($feat0:tt, $feat1:tt); ) => {
245        $(#[$docs])*
246
247        pub fn $name(&mut self) -> FontFeatureSet {
248            static FEATS: [FontFeatureName; 2] = [FontFeatureName(*$feat0), FontFeatureName(*$feat1)];
249            self.feature_set(&FEATS)
250        }
251
252    };
253
254    (feature $(#[$docs:meta])* fn $name:ident($feat0:tt);) => {
255        $(#[$docs])*
256
257        pub fn $name(&mut self) -> FontFeature {
258            self.feature(FontFeatureName(*$feat0))
259        }
260
261    };
262
263    (feature $(#[$docs:meta])* fn $name:ident($Enum:ident) -> $Helper:ident;) => {
264        $(#[$docs])*
265
266        pub fn $name(&mut self) -> $Helper<$Enum> {
267            $Helper { features: &mut self.0, _t: PhantomData }
268        }
269
270    };
271
272    (builder $(#[$docs:meta])* fn $name:ident();) => {
273        $(#[$docs])*
274
275        pub fn $name(mut self, state: impl Into<FontFeatureState>) -> Self {
276            self.0.$name().set(state);
277            self
278        }
279
280    };
281
282    (builder $(#[$docs:meta])* fn $name:ident($Enum:ident -> $Helper:ident);) => {
283        $(#[$docs])*
284
285        pub fn $name(mut self, state: impl Into<$Enum>) -> Self {
286            self.0.$name().set(state);
287            self
288        }
289
290    };
291}
292
293font_features! {
294    /// Font capital glyph variants.
295    ///
296    /// See [`CapsVariant`] for more details.
297    fn caps(CapsVariant) -> FontFeatureExclusiveSets;
298
299    /// Allow glyphs boundaries to overlap for a more pleasant reading.
300    ///
301    /// This corresponds to the `kern` feature.
302    ///
303    /// `Auto` always activates these kerning.
304    fn kerning(b"kern");
305
306    /// The most common ligatures, like for `fi`, `ffi`, `th` or similar.
307    ///
308    /// This corresponds to OpenType `liga` and `clig` features.
309    ///
310    /// `Auto` always activates these ligatures.
311    fn common_lig(b"liga", b"clig");
312
313    /// Ligatures specific to the font, usually decorative.
314    ///
315    /// This corresponds to OpenType `dlig` feature.
316    ///
317    /// `Auto` usually disables these ligatures.
318    fn discretionary_lig(b"dlig");
319
320    /// Ligatures used historically, in old books, like the German tz digraph being displayed ß.
321    ///
322    /// This corresponds to OpenType `hlig` feature.
323    ///
324    /// `Auto` usually disables these ligatures.
325    fn historical_lig(b"hlig");
326
327    /// Alternative letters that adapt to their surrounding letters.
328    ///
329    /// This corresponds to OpenType `calt` feature.
330    ///
331    /// `Auto` usually activates this feature.
332    fn contextual_alt(b"calt");
333
334    /// Force usage of ordinal special glyphs, 1a becomes 1ª.
335    ///
336    /// This corresponds to OpenType `ordn` feature.
337    ///
338    /// `Auto` deactivates this feature.
339    fn ordinal(b"ordn");
340
341    /// Force use of a slashed zero for `0`.
342    ///
343    /// This corresponds to OpenType `zero` feature.
344    ///
345    /// `Auto` deactivates this feature.
346    fn slashed_zero(b"zero");
347
348    /// Use swashes flourish style.
349    ///
350    /// Fonts can have alternative swash styles, you can select then by enabling a number.
351    ///
352    /// This corresponds to OpenType `swsh` and `cswh` feature.
353    ///
354    /// `Auto` does not use swashes.
355    fn swash(b"swsh", b"cswh");
356
357    /// Use stylistic alternatives.
358    ///
359    /// Fonts can have multiple alternative styles, you can select then by enabling a number.
360    ///
361    /// This corresponds to OpenType `salt` feature.
362    ///
363    /// `Auto` does not use alternative styles.
364    fn stylistic(b"salt");
365
366    /// Use glyphs that were common in the past but not today.
367    ///
368    /// This corresponds to OpenType `hist` feature.
369    ///
370    /// `Auto` does not use alternative styles.
371    fn historical_forms(b"hist");
372
373    /// Replace letter with fleurons, dingbats and border elements.
374    ///
375    /// Fonts can have multiple alternative styles, you can select then by enabling a number.
376    ///
377    /// This corresponds to OpenType `ornm` feature.
378    ///
379    /// `Auto` does not enable this by default, but some fonts are purely dingbats glyphs.
380    fn ornaments(b"ornm");
381
382    /// Font annotation alternatives, like circled digits or inverted characters.
383    ///
384    /// Fonts can have multiple alternative styles, you can select then by enabling a number.
385    ///
386    /// This corresponds to OpenType `nalt` feature.
387    ///
388    /// `Auto` does not use alternative styles.
389    fn annotation(b"nalt");
390
391    /// Font numeric glyph variants.
392    ///
393    /// See [`NumVariant`] for more details.
394    fn numeric(NumVariant) -> FontFeatureExclusiveSet;
395
396    /// Font numeric spacing variants.
397    ///
398    /// See [`NumSpacing`] for more details.
399    fn num_spacing(NumSpacing) -> FontFeatureExclusiveSet;
400
401    /// Font numeric spacing variants.
402    ///
403    /// See [`NumSpacing`] for more details.
404    fn num_fraction(NumFraction) -> FontFeatureExclusiveSet;
405
406    /// Font stylistic alternatives for sets of characters.
407    ///
408    /// See [`FontStyleSet`] for more details.
409    fn style_set(FontStyleSet) -> FontFeatureExclusiveSet;
410
411    /// Font stylistic alternatives for individual characters.
412    ///
413    /// See [`CharVariant`] for more details.
414    fn char_variant(CharVariant) -> FontFeatureExclusiveSet;
415
416    /// Font sub/super script alternatives.
417    ///
418    /// See [`FontPosition`] for more details.
419    fn position(FontPosition) -> FontFeatureExclusiveSet;
420
421    /// Force the use of ruby (rubi) glyph variants.
422    ///
423    /// This corresponds to OpenType `ruby` feature.
424    ///
425    /// `Auto` does not force the use of ruby variants.
426    fn ruby(b"ruby");
427
428    /// Japanese logographic set selection.
429    ///
430    /// See [`JpVariant`] for more details.
431    fn jp_variant(JpVariant) -> FontFeatureExclusiveSet;
432
433    /// Use kana glyphs optimized for horizontal writing.
434    ///
435    /// This corresponds to OpenType `hkna` feature.
436    fn horizontal_kana(b"hkna");
437
438    /// Chinese logographic set selection.
439    ///
440    /// See [`CnVariant`] for more details.
441    fn cn_variant(CnVariant) -> FontFeatureExclusiveSet;
442
443    /// East Asian figure width control
444    ///
445    /// See [`EastAsianWidth`] for more details.
446    fn ea_width(EastAsianWidth) -> FontFeatureExclusiveSet;
447}
448
449/// Represents a feature in a [`FontFeatures`] configuration.
450pub struct FontFeature<'a>(hash_map::Entry<'a, FontFeatureName, u32>);
451impl FontFeature<'_> {
452    /// Gets the OpenType name of the feature.
453    pub fn name(&self) -> FontFeatureName {
454        *self.0.key()
455    }
456
457    /// Gets the current state of the feature.
458    pub fn state(&self) -> FontFeatureState {
459        match &self.0 {
460            hash_map::Entry::Occupied(e) => FontFeatureState(Some(*e.get())),
461            hash_map::Entry::Vacant(_) => FontFeatureState::auto(),
462        }
463    }
464
465    /// If the feature is explicitly enabled.
466    pub fn is_enabled(&self) -> bool {
467        self.state().is_enabled()
468    }
469
470    /// If the feature is explicitly disabled.
471    pub fn is_disabled(&self) -> bool {
472        self.state().is_disabled()
473    }
474
475    /// If the feature is auto enabled.
476    pub fn is_auto(&self) -> bool {
477        self.state().is_auto()
478    }
479
480    /// Set the feature state.
481    ///
482    /// Returns the previous state.
483    pub fn set(self, state: impl Into<FontFeatureState>) -> FontFeatureState {
484        let prev = self.state();
485        match state.into().0 {
486            Some(n) => self.set_explicit(n),
487            None => self.auto(),
488        }
489        prev
490    }
491
492    fn set_explicit(self, state: u32) {
493        match self.0 {
494            hash_map::Entry::Occupied(mut e) => {
495                e.insert(state);
496            }
497            hash_map::Entry::Vacant(e) => {
498                e.insert(state);
499            }
500        }
501    }
502
503    /// Enable the feature.
504    pub fn enable(self) {
505        self.set_explicit(FEATURE_ENABLED);
506    }
507
508    /// Enable the feature with alternative selection.
509    pub fn enable_alt(self, alt: NonZeroU32) {
510        self.set_explicit(alt.get())
511    }
512
513    /// Disable the feature.
514    pub fn disable(self) {
515        self.set_explicit(FEATURE_DISABLED);
516    }
517
518    /// Set the feature to auto.
519    pub fn auto(self) {
520        if let hash_map::Entry::Occupied(e) = self.0 {
521            e.remove();
522        }
523    }
524}
525impl fmt::Debug for FontFeature<'_> {
526    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527        write!(f, "b\"{}\": {:?}", self.name(), self.state())
528    }
529}
530
531/// Represents a set of features in a [`FontFeatures`] configuration, the features state is managed together.
532pub struct FontFeatureSet<'a> {
533    features: &'a mut FontFeaturesMap,
534    names: &'static [FontFeatureName],
535}
536impl FontFeatureSet<'_> {
537    /// Gets the OpenType name of the features.
538    pub fn names(&self) -> &'static [FontFeatureName] {
539        self.names
540    }
541
542    /// Gets the current state of the features.
543    ///
544    /// Returns `Auto` if the features are mixed.
545    pub fn state(&self) -> FontFeatureState {
546        if let Some(&a) = self.features.get(&self.names[0]) {
547            for name in &self.names[1..] {
548                if self.features.get(name) != Some(&a) {
549                    return FontFeatureState::auto();
550                }
551            }
552            FontFeatureState(Some(a))
553        } else {
554            FontFeatureState::auto()
555        }
556    }
557
558    /// If the features are explicitly enabled.
559    pub fn is_enabled(&self) -> bool {
560        self.state().is_enabled()
561    }
562
563    /// If the features are explicitly disabled.
564    pub fn is_disabled(&self) -> bool {
565        self.state().is_disabled()
566    }
567
568    /// If the features are auto enabled , or in a mixed state.
569    pub fn is_auto(&self) -> bool {
570        self.state().is_auto()
571    }
572
573    /// Set the feature state.
574    ///
575    /// Returns the previous state.
576    pub fn set(self, state: impl Into<FontFeatureState>) -> FontFeatureState {
577        let prev = self.state();
578        match state.into().0 {
579            Some(n) => self.set_explicit(n),
580            None => self.auto(),
581        }
582        prev
583    }
584
585    fn set_explicit(self, state: u32) {
586        for name in self.names {
587            self.features.insert(*name, state);
588        }
589    }
590
591    /// Enable the feature.
592    pub fn enable(self) {
593        self.set_explicit(FEATURE_ENABLED);
594    }
595
596    /// Disable the feature.
597    pub fn disable(self) {
598        self.set_explicit(FEATURE_DISABLED);
599    }
600
601    /// Set the feature to auto.
602    pub fn auto(self) {
603        for name in self.names {
604            self.features.remove(name);
605        }
606    }
607}
608impl fmt::Debug for FontFeatureSet<'_> {
609    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
610        write!(f, "{:?}: {:?}", self.names, self.state())
611    }
612}
613
614/// Represents a set of exclusive boolean in a [`FontFeatures`] configuration, only one
615/// of the feature is enabled at a time.
616pub struct FontFeatureExclusiveSet<'a, S: FontFeatureExclusiveSetState> {
617    features: &'a mut FontFeaturesMap,
618    _t: PhantomData<S>,
619}
620impl<S: FontFeatureExclusiveSetState> FontFeatureExclusiveSet<'_, S> {
621    /// Gets the OpenType names of all the features affected.
622    pub fn names(&self) -> &'static [FontFeatureName] {
623        S::names()
624    }
625
626    /// Gets the current state of the features.
627    pub fn state(&self) -> S {
628        let mut state = 0;
629
630        for (i, name) in S::names().iter().enumerate() {
631            if let Some(&s) = self.features.get(name) {
632                if s == FEATURE_ENABLED && state == 0 {
633                    state = i + 1; // found state.
634                    continue;
635                }
636            }
637            // found `auto`, a custom state set externally or a second feature activated externally.
638            return S::auto();
639        }
640        S::from_variant(state as u32)
641    }
642    fn take_state(&mut self) -> S {
643        let mut state = 0;
644        let mut skip = false;
645
646        for (i, name) in S::names().iter().enumerate() {
647            if let Some(s) = self.features.remove(name) {
648                if skip {
649                    continue;
650                }
651
652                if s == FEATURE_ENABLED && state == 0 {
653                    state = i + 1; // found state.
654                    continue;
655                }
656            }
657            // found `auto`, a custom state set externally or a second feature activated externally.
658            skip = true;
659        }
660
661        S::from_variant(state as u32)
662    }
663
664    /// If state is `Auto`.
665    pub fn is_auto(&self) -> bool {
666        self.state() == S::auto()
667    }
668
669    /// Sets the features.
670    ///
671    /// Returns the previous state.
672    pub fn set(&mut self, state: impl Into<S>) -> S {
673        let prev = self.take_state();
674        if let Some(state) = state.into().variant() {
675            self.features.insert(self.names()[state as usize - 1], FEATURE_ENABLED);
676        }
677        prev
678    }
679}
680impl<S: FontFeatureExclusiveSetState + fmt::Debug> fmt::Debug for FontFeatureExclusiveSet<'_, S> {
681    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682        fmt::Debug::fmt(&self.state(), f)
683    }
684}
685
686/// Represents a set of exclusive boolean in a [`FontFeatures`] configuration, one or more
687/// of the features can be active at the same time but they always map to a single *state*.
688pub struct FontFeatureExclusiveSets<'a, S: FontFeatureExclusiveSetsState> {
689    features: &'a mut FontFeaturesMap,
690    _t: PhantomData<S>,
691}
692impl<S: FontFeatureExclusiveSetsState> FontFeatureExclusiveSets<'_, S> {
693    /// Gets the OpenType names of all the features affected.
694    pub fn names(&self) -> &'static [&'static [FontFeatureName]] {
695        S::names()
696    }
697
698    /// Gets the current state of the features.
699    pub fn state(&self) -> S {
700        let mut active = HashSet::new();
701        for &names in self.names() {
702            for name in names {
703                if let Some(&s) = self.features.get(name) {
704                    if s != FEATURE_ENABLED {
705                        // custom external state, we only set to FEATURE_ENABLED.
706                        return S::auto();
707                    } else {
708                        active.insert(*name);
709                    }
710                }
711            }
712        }
713
714        if !active.is_empty() {
715            'names: for (i, &names) in self.names().iter().enumerate() {
716                if names.len() == active.len() {
717                    for name in names {
718                        if !active.contains(name) {
719                            continue 'names;
720                        }
721                    }
722                    return S::from_variant(i as u32 + 1);
723                }
724            }
725        }
726
727        S::auto()
728    }
729    fn take_state(&mut self) -> S {
730        let mut active = HashSet::new();
731        let mut force_auto = false;
732
733        for &names in self.names() {
734            for name in names {
735                if let Some(s) = self.features.remove(name) {
736                    if force_auto {
737                        continue;
738                    }
739
740                    if s != FEATURE_ENABLED {
741                        // custom external state, we only set to FEATURE_ENABLED.
742                        force_auto = true;
743                    } else {
744                        active.insert(name);
745                    }
746                }
747            }
748        }
749
750        if !force_auto && !active.is_empty() {
751            'names: for (i, &names) in self.names().iter().enumerate() {
752                if names.len() == active.len() {
753                    for name in names {
754                        if !active.contains(name) {
755                            continue 'names;
756                        }
757                    }
758                    return S::from_variant(i as u32 + 1);
759                }
760            }
761        }
762
763        S::auto()
764    }
765
766    /// If state is `Auto`.
767    pub fn is_auto(&self) -> bool {
768        self.state() == S::auto()
769    }
770
771    /// Sets the features.
772    ///
773    /// Returns the previous state.
774    pub fn set(&mut self, state: impl Into<S>) -> S {
775        let prev = self.take_state();
776        if let Some(state) = state.into().variant() {
777            for name in self.names()[state as usize - 1] {
778                self.features.insert(*name, FEATURE_ENABLED);
779            }
780        }
781        prev
782    }
783}
784impl<S: FontFeatureExclusiveSetsState + fmt::Debug> fmt::Debug for FontFeatureExclusiveSets<'_, S> {
785    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
786        fmt::Debug::fmt(&self.state(), f)
787    }
788}
789
790/// Represents `enum` like types that represents a exclusive set of features + `Auto`.
791pub trait FontFeatureExclusiveSetState: Copy + PartialEq + 'static {
792    /// All the names of features, must have more then one name.
793    fn names() -> &'static [FontFeatureName];
794    /// `None` if `Auto` or `Some(NonZeroUsize)` if is a feature.
795    fn variant(self) -> Option<u32>;
796    /// New from feature variant.
797    ///
798    /// Returns `Auto` if `v == 0 || v > Self::names().len()`.
799    fn from_variant(v: u32) -> Self;
800    /// New `Auto`.
801    fn auto() -> Self;
802}
803
804/// Represents `enum` like types that represents a exclusive set of features + `Auto`.
805/// Some variants can have multiple features.
806pub trait FontFeatureExclusiveSetsState: Copy + PartialEq + 'static {
807    /// All the names of features, must have more then one sub-set.
808    fn names() -> &'static [&'static [FontFeatureName]];
809    /// `None` if `Auto` or `Some(NonZeroUsize)` if is a feature.
810    fn variant(self) -> Option<u32>;
811    /// New from feature variant.
812    ///
813    /// Returns `Auto` if `v == 0 || v > Self::names().len()`.
814    fn from_variant(v: u32) -> Self;
815    /// New `Auto`.
816    fn auto() -> Self;
817}
818
819/// State of a [font feature](FontFeatures).
820#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
821pub struct FontFeatureState(Option<u32>);
822impl FontFeatureState {
823    /// Automatic state.
824    pub const fn auto() -> Self {
825        FontFeatureState(None)
826    }
827
828    /// Enabled state.
829    pub const fn enabled() -> Self {
830        FontFeatureState(Some(1))
831    }
832
833    /// Enabled state with alternative selected.
834    pub const fn enabled_alt(alt: NonZeroU32) -> Self {
835        FontFeatureState(Some(alt.get()))
836    }
837
838    /// Disabled state.
839    pub const fn disabled() -> Self {
840        FontFeatureState(Some(0))
841    }
842
843    /// Is [`auto`](Self::auto).
844    pub fn is_auto(self) -> bool {
845        self.0.is_none()
846    }
847
848    /// Is [`enabled`](Self::enabled) or [`enabled_alt`](Self::enabled_alt).
849    pub fn is_enabled(self) -> bool {
850        if let Some(n) = self.0 {
851            if n >= 1 {
852                return true;
853            }
854        }
855        false
856    }
857
858    /// Is [`disabled`](Self::disabled).
859    pub fn is_disabled(self) -> bool {
860        self == Self::disabled()
861    }
862
863    /// Gets the enabled alternative.
864    pub fn alt(self) -> Option<u32> {
865        if let Some(n) = self.0 {
866            if n >= 1 {
867                return Some(n);
868            }
869        }
870        None
871    }
872}
873impl fmt::Debug for FontFeatureState {
874    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
875        match self.0 {
876            Some(n) => {
877                if n == FEATURE_DISABLED {
878                    write!(f, "FontFeatureState::disabled()")
879                } else if n == FEATURE_ENABLED {
880                    write!(f, "FontFeatureState::enabled()")
881                } else {
882                    write!(f, "FontFeatureState::enabled_alt({n})")
883                }
884            }
885            None => write!(f, "FontFeatureState::auto()"),
886        }
887    }
888}
889impl_from_and_into_var! {
890    fn from(enabled: bool) -> FontFeatureState {
891        if enabled {
892            FontFeatureState::enabled()
893        } else {
894            FontFeatureState::disabled()
895        }
896    }
897
898    /// `0` is disabled, `>=1` is enabled with the alt value.
899    fn from(alt: u32) -> FontFeatureState {
900        FontFeatureState(Some(alt))
901    }
902}
903
904/// Font capital letters variant features.
905#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
906#[repr(u8)]
907pub enum CapsVariant {
908    /// No caps variant.
909    #[default]
910    Auto,
911
912    /// Enable small caps alternative for lowercase letters.
913    ///
914    /// This corresponds to OpenType `smcp` feature.
915    SmallCaps,
916
917    /// Enable small caps alternative for lower and upper case letters.
918    ///
919    /// This corresponds to OpenType `smcp` and `c2sc` features.
920    AllSmallCaps,
921
922    /// Enable petite caps alternative for lowercase letters.
923    ///
924    /// This corresponds to OpenType `pcap` feature.
925    Petite,
926
927    /// Enable petite caps alternative for lower and upper case letters.
928    ///
929    /// This corresponds to OpenType `pcap` and `c2pc` features.
930    AllPetite,
931
932    /// Enables unicase, using small caps for upper case letters mixed with normal lowercase letters.
933    ///
934    /// This corresponds to OpenType `unic` feature.
935    Unicase,
936
937    /// Enable title caps alternatives. This uses alternative uppercase glyphs designed for all uppercase words.
938    ///
939    /// This corresponds to OpenType `titl` feature.
940    TitlingCaps,
941}
942impl fmt::Debug for CapsVariant {
943    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
944        if f.alternate() {
945            write!(f, "CapsVariant::")?;
946        }
947        match self {
948            CapsVariant::Auto => write!(f, "Auto"),
949            CapsVariant::SmallCaps => write!(f, "SmallCaps"),
950            CapsVariant::AllSmallCaps => write!(f, "AllSmallCaps"),
951            CapsVariant::Petite => write!(f, "Petite"),
952            CapsVariant::AllPetite => write!(f, "AllPetite"),
953            CapsVariant::Unicase => write!(f, "Unicase"),
954            CapsVariant::TitlingCaps => write!(f, "TitlingCaps"),
955        }
956    }
957}
958impl Default for CapsVariant {
959    /// [`CapsVariant::Auto`]
960    fn default() -> Self {
961        CapsVariant::Auto
962    }
963}
964impl FontFeatureExclusiveSetsState for CapsVariant {
965    fn names() -> &'static [&'static [FontFeatureName]] {
966        static N0: [FontFeatureName; 1] = [FontFeatureName(*b"smcp")];
967        static N1: [FontFeatureName; 2] = [FontFeatureName(*b"c2sc"), FontFeatureName(*b"smcp")];
968        static N2: [FontFeatureName; 1] = [FontFeatureName(*b"pcap")];
969        static N3: [FontFeatureName; 2] = [FontFeatureName(*b"c2pc"), FontFeatureName(*b"pcap")];
970        static N4: [FontFeatureName; 1] = [FontFeatureName(*b"unic")];
971        static N5: [FontFeatureName; 1] = [FontFeatureName(*b"titl")];
972        static NAMES: [&[FontFeatureName]; 6] = [&N0, &N1, &N2, &N3, &N4, &N5];
973        &NAMES
974    }
975
976    fn variant(self) -> Option<u32> {
977        if self == CapsVariant::Auto { None } else { Some(self as u32) }
978    }
979
980    fn from_variant(v: u32) -> Self {
981        Self::from(v as u8)
982    }
983
984    fn auto() -> Self {
985        CapsVariant::Auto
986    }
987}
988
989/// Font numeric variant features.
990#[derive(Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
991#[repr(u8)]
992pub enum NumVariant {
993    /// Uses the default numeric glyphs, in most fonts this is the same as `Lining`, some fonts use the `OldStyle`.
994    #[default]
995    Auto,
996    /// Uses numeric glyphs that rest on the baseline.
997    ///
998    /// This corresponds to OpenType `lnum` feature.
999    Lining,
1000    /// Uses old-style numeric glyphs, where some numbers, like 3, 4, 7, 9 have descenders.
1001    ///
1002    /// This corresponds to OpenType `onum` feature.
1003    OldStyle,
1004}
1005impl fmt::Debug for NumVariant {
1006    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1007        if f.alternate() {
1008            write!(f, "NumVariant::")?;
1009        }
1010        match self {
1011            NumVariant::Auto => write!(f, "Auto"),
1012            NumVariant::Lining => write!(f, "Lining"),
1013            NumVariant::OldStyle => write!(f, "OldStyle"),
1014        }
1015    }
1016}
1017impl Default for NumVariant {
1018    /// [`NumVariant::Auto`]
1019    fn default() -> Self {
1020        NumVariant::Auto
1021    }
1022}
1023impl FontFeatureExclusiveSetState for NumVariant {
1024    fn names() -> &'static [FontFeatureName] {
1025        static NAMES: [FontFeatureName; 2] = [FontFeatureName(*b"lnum"), FontFeatureName(*b"onum")];
1026        &NAMES
1027    }
1028
1029    fn variant(self) -> Option<u32> {
1030        match self {
1031            NumVariant::Auto => None,
1032            NumVariant::Lining => Some(1),
1033            NumVariant::OldStyle => Some(2),
1034        }
1035    }
1036
1037    fn from_variant(v: u32) -> Self {
1038        Self::from(v as u8)
1039    }
1040
1041    fn auto() -> Self {
1042        NumVariant::Auto
1043    }
1044}
1045
1046/// Font numeric spacing features.
1047#[derive(Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
1048#[repr(u8)]
1049pub enum NumSpacing {
1050    /// Uses the default numeric width, usually this is `Tabular` for *monospace* fonts and `Proportional` for the others.
1051    #[default]
1052    Auto,
1053    /// Numeric glyphs take different space depending on the design of the glyph.
1054    ///
1055    /// This corresponds to OpenType `pnum` feature.
1056    Proportional,
1057    /// Numeric glyphs take the same space even if the glyphs design width is different.
1058    ///
1059    /// This corresponds to OpenType `tnum` feature.
1060    Tabular,
1061}
1062impl fmt::Debug for NumSpacing {
1063    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1064        if f.alternate() {
1065            write!(f, "NumSpacing::")?;
1066        }
1067        match self {
1068            NumSpacing::Auto => write!(f, "Auto"),
1069            NumSpacing::Proportional => write!(f, "Proportional"),
1070            NumSpacing::Tabular => write!(f, "Tabular"),
1071        }
1072    }
1073}
1074impl Default for NumSpacing {
1075    /// [`NumSpacing::Auto`]
1076    fn default() -> Self {
1077        NumSpacing::Auto
1078    }
1079}
1080impl FontFeatureExclusiveSetState for NumSpacing {
1081    fn names() -> &'static [FontFeatureName] {
1082        static NAMES: [FontFeatureName; 2] = [FontFeatureName(*b"pnum"), FontFeatureName(*b"tnum")];
1083        &NAMES
1084    }
1085
1086    fn variant(self) -> Option<u32> {
1087        match self {
1088            NumSpacing::Auto => None,
1089            NumSpacing::Proportional => Some(1),
1090            NumSpacing::Tabular => Some(2),
1091        }
1092    }
1093
1094    fn from_variant(v: u32) -> Self {
1095        Self::from(v as u8)
1096    }
1097
1098    fn auto() -> Self {
1099        NumSpacing::Auto
1100    }
1101}
1102
1103/// Font numeric fraction features.
1104#[derive(Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
1105#[repr(u8)]
1106pub enum NumFraction {
1107    /// Don't use fraction variants.
1108    #[default]
1109    Auto,
1110    /// Variant where the numerator and denominator are made smaller and separated by a slash.
1111    ///
1112    /// This corresponds to OpenType `frac` feature.
1113    Diagonal,
1114    /// Variant where the numerator and denominator are made smaller, stacked and separated by a horizontal line.
1115    ///
1116    /// This corresponds to OpenType `afrc` feature.
1117    Stacked,
1118}
1119impl fmt::Debug for NumFraction {
1120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1121        if f.alternate() {
1122            write!(f, "NumFraction::")?;
1123        }
1124        match self {
1125            NumFraction::Auto => write!(f, "Auto"),
1126            NumFraction::Diagonal => write!(f, "Diagonal"),
1127            NumFraction::Stacked => write!(f, "Stacked"),
1128        }
1129    }
1130}
1131impl Default for NumFraction {
1132    /// [`NumFraction::Auto`]
1133    fn default() -> Self {
1134        NumFraction::Auto
1135    }
1136}
1137impl FontFeatureExclusiveSetState for NumFraction {
1138    fn names() -> &'static [FontFeatureName] {
1139        static NAMES: [FontFeatureName; 2] = [FontFeatureName(*b"frac"), FontFeatureName(*b"afrc")];
1140        &NAMES
1141    }
1142
1143    fn variant(self) -> Option<u32> {
1144        match self {
1145            NumFraction::Auto => None,
1146            NumFraction::Diagonal => Some(1),
1147            NumFraction::Stacked => Some(2),
1148        }
1149    }
1150
1151    fn from_variant(v: u32) -> Self {
1152        Self::from(v as u8)
1153    }
1154
1155    fn auto() -> Self {
1156        NumFraction::Auto
1157    }
1158}
1159
1160/// All possible [style_set](FontFeatures::style_set) features.
1161///
1162/// The styles depend on the font, it is recommended you create an `enum` with named sets that
1163/// converts into this one for each font you wish to use.
1164#[derive(Copy, Clone, Eq, PartialEq, Hash, FromPrimitive)]
1165#[repr(u8)]
1166#[allow(missing_docs)]
1167pub enum FontStyleSet {
1168    /// Don't use alternative style set.
1169    #[default]
1170    Auto = 0,
1171
1172    S01,
1173    S02,
1174    S03,
1175    S04,
1176    S05,
1177    S06,
1178    S07,
1179    S08,
1180    S09,
1181    S10,
1182
1183    S11,
1184    S12,
1185    S13,
1186    S14,
1187    S15,
1188    S16,
1189    S17,
1190    S18,
1191    S19,
1192    S20,
1193}
1194impl Default for FontStyleSet {
1195    /// [`FontStyleSet::Auto`]
1196    fn default() -> Self {
1197        FontStyleSet::Auto
1198    }
1199}
1200impl fmt::Debug for FontStyleSet {
1201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1202        if f.alternate() {
1203            write!(f, "FontStyleSet::")?;
1204        }
1205        let n = *self as u8;
1206        if n == 0 { write!(f, "Auto") } else { write!(f, "S{n:0<2}") }
1207    }
1208}
1209impl_from_and_into_var! {
1210    fn from(set: u8) -> FontStyleSet;
1211}
1212
1213impl FontFeatureExclusiveSetState for FontStyleSet {
1214    fn names() -> &'static [FontFeatureName] {
1215        static NAMES: [FontFeatureName; 20] = [
1216            FontFeatureName(*b"ss01"),
1217            FontFeatureName(*b"ss02"),
1218            FontFeatureName(*b"ss03"),
1219            FontFeatureName(*b"ss04"),
1220            FontFeatureName(*b"ss05"),
1221            FontFeatureName(*b"ss06"),
1222            FontFeatureName(*b"ss07"),
1223            FontFeatureName(*b"ss08"),
1224            FontFeatureName(*b"ss09"),
1225            FontFeatureName(*b"ss10"),
1226            FontFeatureName(*b"ss11"),
1227            FontFeatureName(*b"ss12"),
1228            FontFeatureName(*b"ss13"),
1229            FontFeatureName(*b"ss14"),
1230            FontFeatureName(*b"ss15"),
1231            FontFeatureName(*b"ss16"),
1232            FontFeatureName(*b"ss17"),
1233            FontFeatureName(*b"ss18"),
1234            FontFeatureName(*b"ss19"),
1235            FontFeatureName(*b"ss20"),
1236        ];
1237        &NAMES
1238    }
1239
1240    fn variant(self) -> Option<u32> {
1241        if self == FontStyleSet::Auto { None } else { Some(self as u32) }
1242    }
1243
1244    fn from_variant(v: u32) -> Self {
1245        Self::from(v as u8)
1246    }
1247
1248    fn auto() -> Self {
1249        FontStyleSet::Auto
1250    }
1251}
1252
1253/// All possible [char_variant](FontFeatures::char_variant) features (`cv00..=cv99`).
1254///
1255/// The styles depend on the font, it is recommended you create `const`s with named variants to use with a specific font.
1256#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
1257pub struct CharVariant(u8);
1258impl CharVariant {
1259    /// New variant.
1260    ///
1261    /// `v == 0 || v > 99` is Auto, `v >= 1 && v <= 99` maps to their variant.
1262    pub const fn new(v: u8) -> Self {
1263        if v > 99 { CharVariant(0) } else { CharVariant(v) }
1264    }
1265
1266    /// New auto.
1267    pub const fn auto() -> Self {
1268        CharVariant(0)
1269    }
1270
1271    /// Is auto.
1272    pub const fn is_auto(self) -> bool {
1273        self.0 == 0
1274    }
1275}
1276impl_from_and_into_var! {
1277    /// `v == 0 || v > 99` is Auto, `v >= 1 && v <= 99` maps to their variant.
1278    fn from(v: u8) -> CharVariant {
1279        CharVariant::new(v)
1280    }
1281}
1282impl FontFeatureExclusiveSetState for CharVariant {
1283    fn names() -> &'static [FontFeatureName] {
1284        static NAMES: [FontFeatureName; 100] = [
1285            FontFeatureName(*b"cv01"),
1286            FontFeatureName(*b"cv02"),
1287            FontFeatureName(*b"cv03"),
1288            FontFeatureName(*b"cv04"),
1289            FontFeatureName(*b"cv05"),
1290            FontFeatureName(*b"cv06"),
1291            FontFeatureName(*b"cv07"),
1292            FontFeatureName(*b"cv08"),
1293            FontFeatureName(*b"cv09"),
1294            FontFeatureName(*b"cv20"),
1295            FontFeatureName(*b"cv21"),
1296            FontFeatureName(*b"cv22"),
1297            FontFeatureName(*b"cv23"),
1298            FontFeatureName(*b"cv24"),
1299            FontFeatureName(*b"cv25"),
1300            FontFeatureName(*b"cv26"),
1301            FontFeatureName(*b"cv27"),
1302            FontFeatureName(*b"cv28"),
1303            FontFeatureName(*b"cv29"),
1304            FontFeatureName(*b"cv30"),
1305            FontFeatureName(*b"cv31"),
1306            FontFeatureName(*b"cv32"),
1307            FontFeatureName(*b"cv33"),
1308            FontFeatureName(*b"cv34"),
1309            FontFeatureName(*b"cv35"),
1310            FontFeatureName(*b"cv36"),
1311            FontFeatureName(*b"cv37"),
1312            FontFeatureName(*b"cv38"),
1313            FontFeatureName(*b"cv39"),
1314            FontFeatureName(*b"cv40"),
1315            FontFeatureName(*b"cv41"),
1316            FontFeatureName(*b"cv42"),
1317            FontFeatureName(*b"cv43"),
1318            FontFeatureName(*b"cv44"),
1319            FontFeatureName(*b"cv45"),
1320            FontFeatureName(*b"cv46"),
1321            FontFeatureName(*b"cv47"),
1322            FontFeatureName(*b"cv48"),
1323            FontFeatureName(*b"cv49"),
1324            FontFeatureName(*b"cv50"),
1325            FontFeatureName(*b"cv51"),
1326            FontFeatureName(*b"cv52"),
1327            FontFeatureName(*b"cv53"),
1328            FontFeatureName(*b"cv54"),
1329            FontFeatureName(*b"cv55"),
1330            FontFeatureName(*b"cv56"),
1331            FontFeatureName(*b"cv57"),
1332            FontFeatureName(*b"cv58"),
1333            FontFeatureName(*b"cv59"),
1334            FontFeatureName(*b"cv60"),
1335            FontFeatureName(*b"cv61"),
1336            FontFeatureName(*b"cv62"),
1337            FontFeatureName(*b"cv63"),
1338            FontFeatureName(*b"cv64"),
1339            FontFeatureName(*b"cv65"),
1340            FontFeatureName(*b"cv66"),
1341            FontFeatureName(*b"cv67"),
1342            FontFeatureName(*b"cv68"),
1343            FontFeatureName(*b"cv69"),
1344            FontFeatureName(*b"cv70"),
1345            FontFeatureName(*b"cv71"),
1346            FontFeatureName(*b"cv72"),
1347            FontFeatureName(*b"cv73"),
1348            FontFeatureName(*b"cv74"),
1349            FontFeatureName(*b"cv75"),
1350            FontFeatureName(*b"cv76"),
1351            FontFeatureName(*b"cv77"),
1352            FontFeatureName(*b"cv78"),
1353            FontFeatureName(*b"cv79"),
1354            FontFeatureName(*b"cv70"),
1355            FontFeatureName(*b"cv71"),
1356            FontFeatureName(*b"cv72"),
1357            FontFeatureName(*b"cv73"),
1358            FontFeatureName(*b"cv74"),
1359            FontFeatureName(*b"cv75"),
1360            FontFeatureName(*b"cv76"),
1361            FontFeatureName(*b"cv77"),
1362            FontFeatureName(*b"cv78"),
1363            FontFeatureName(*b"cv79"),
1364            FontFeatureName(*b"cv80"),
1365            FontFeatureName(*b"cv81"),
1366            FontFeatureName(*b"cv82"),
1367            FontFeatureName(*b"cv83"),
1368            FontFeatureName(*b"cv84"),
1369            FontFeatureName(*b"cv85"),
1370            FontFeatureName(*b"cv86"),
1371            FontFeatureName(*b"cv87"),
1372            FontFeatureName(*b"cv88"),
1373            FontFeatureName(*b"cv89"),
1374            FontFeatureName(*b"cv90"),
1375            FontFeatureName(*b"cv91"),
1376            FontFeatureName(*b"cv92"),
1377            FontFeatureName(*b"cv93"),
1378            FontFeatureName(*b"cv94"),
1379            FontFeatureName(*b"cv95"),
1380            FontFeatureName(*b"cv96"),
1381            FontFeatureName(*b"cv97"),
1382            FontFeatureName(*b"cv98"),
1383            FontFeatureName(*b"cv99"),
1384            FontFeatureName(*b"cv99"),
1385        ];
1386        &NAMES
1387    }
1388
1389    fn variant(self) -> Option<u32> {
1390        if self.is_auto() { None } else { Some(self.0 as u32) }
1391    }
1392
1393    fn from_variant(v: u32) -> Self {
1394        if v > 99 { CharVariant::auto() } else { CharVariant(v as u8) }
1395    }
1396
1397    fn auto() -> Self {
1398        CharVariant::auto()
1399    }
1400}
1401
1402/// Sub-script and super-script variants.
1403#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
1404#[repr(u8)]
1405pub enum FontPosition {
1406    /// Don't use sub/super script positions.
1407    #[default]
1408    Auto,
1409    /// Uses sub-script position and alternative glyphs.
1410    ///
1411    /// This corresponds to OpenType `subs` feature.
1412    Sub,
1413    /// Uses super-script position and alternative glyphs.
1414    ///
1415    /// This corresponds to OpenType `sups` feature.
1416    Super,
1417}
1418impl fmt::Debug for FontPosition {
1419    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1420        if f.alternate() {
1421            write!(f, "FontPosition::")?;
1422        }
1423        match self {
1424            FontPosition::Auto => write!(f, "Auto"),
1425            FontPosition::Sub => write!(f, "Sub"),
1426            FontPosition::Super => write!(f, "Super"),
1427        }
1428    }
1429}
1430impl Default for FontPosition {
1431    /// [`FontPosition::Auto`]
1432    fn default() -> Self {
1433        FontPosition::Auto
1434    }
1435}
1436impl FontFeatureExclusiveSetState for FontPosition {
1437    fn names() -> &'static [FontFeatureName] {
1438        static NAMES: [FontFeatureName; 2] = [FontFeatureName(*b"subs"), FontFeatureName(*b"sups")];
1439        &NAMES
1440    }
1441
1442    fn variant(self) -> Option<u32> {
1443        match self {
1444            FontPosition::Auto => None,
1445            FontPosition::Sub => Some(1),
1446            FontPosition::Super => Some(2),
1447        }
1448    }
1449
1450    fn from_variant(v: u32) -> Self {
1451        Self::from(v as u8)
1452    }
1453
1454    fn auto() -> Self {
1455        FontPosition::Auto
1456    }
1457}
1458
1459/// Logographic glyph variants for Japanese fonts.
1460#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
1461#[repr(u8)]
1462pub enum JpVariant {
1463    /// Uses the font default glyphs.
1464    #[default]
1465    Auto,
1466
1467    /// JIS X 0208-1978 (first standard)
1468    ///
1469    /// This corresponds to OpenType `jp78` feature.
1470    Jis78,
1471    /// JIS X 0208-1983 (second standard)
1472    ///
1473    /// This corresponds to OpenType `jp83` feature.
1474    Jis83,
1475    /// JIS X 0208-1990 (third standard)
1476    ///
1477    /// This corresponds to OpenType `jp90` feature.
1478    Jis90,
1479
1480    /// JIS X 0213 (2004)
1481    ///
1482    /// This corresponds to OpenType `jp04` feature.
1483    Jis04,
1484    /// NLC new shapes for JIS (2000).
1485    ///
1486    /// This corresponds to OpenType `nlck` feature.
1487    NlcKanji,
1488}
1489impl fmt::Debug for JpVariant {
1490    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1491        if f.alternate() {
1492            write!(f, "JpVariant::")?;
1493        }
1494        match self {
1495            JpVariant::Auto => write!(f, "Auto"),
1496            JpVariant::Jis78 => write!(f, "Jis78"),
1497            JpVariant::Jis83 => write!(f, "Jis83"),
1498            JpVariant::Jis90 => write!(f, "Jis90"),
1499            JpVariant::Jis04 => write!(f, "Jis04"),
1500            JpVariant::NlcKanji => write!(f, "NlcKanji"),
1501        }
1502    }
1503}
1504impl Default for JpVariant {
1505    /// [`JpVariant::Auto`]
1506    fn default() -> Self {
1507        JpVariant::Auto
1508    }
1509}
1510impl FontFeatureExclusiveSetState for JpVariant {
1511    fn names() -> &'static [FontFeatureName] {
1512        static NAMES: [FontFeatureName; 5] = [
1513            FontFeatureName(*b"jp78"),
1514            FontFeatureName(*b"jp83"),
1515            FontFeatureName(*b"jp90"),
1516            FontFeatureName(*b"jp04"),
1517            FontFeatureName(*b"nlck"),
1518        ];
1519        &NAMES
1520    }
1521
1522    fn variant(self) -> Option<u32> {
1523        if self == JpVariant::Auto { None } else { Some(self as u32) }
1524    }
1525
1526    fn from_variant(v: u32) -> Self {
1527        Self::from(v as u8)
1528    }
1529
1530    fn auto() -> Self {
1531        JpVariant::Auto
1532    }
1533}
1534/// Logographic glyph variants for Chinese fonts.
1535#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
1536#[repr(u8)]
1537pub enum CnVariant {
1538    /// Uses the font default glyphs.
1539    #[default]
1540    Auto,
1541    /// Simplified Chinese glyphs.
1542    ///
1543    /// This corresponds to OpenType `smpl` feature.
1544    Simplified,
1545    /// Traditional Chinese glyphs.
1546    ///
1547    /// This corresponds to OpenType `trad` feature.
1548    Traditional,
1549}
1550impl fmt::Debug for CnVariant {
1551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1552        if f.alternate() {
1553            write!(f, "CnVariant")?;
1554        }
1555        match self {
1556            CnVariant::Auto => write!(f, "Auto"),
1557            CnVariant::Simplified => write!(f, "Simplified"),
1558            CnVariant::Traditional => write!(f, "Traditional"),
1559        }
1560    }
1561}
1562impl Default for CnVariant {
1563    /// [`CnVariant::Auto`]
1564    fn default() -> Self {
1565        CnVariant::Auto
1566    }
1567}
1568impl FontFeatureExclusiveSetState for CnVariant {
1569    fn names() -> &'static [FontFeatureName] {
1570        static NAMES: [FontFeatureName; 2] = [FontFeatureName(*b"smpl"), FontFeatureName(*b"trad")];
1571        &NAMES
1572    }
1573
1574    fn variant(self) -> Option<u32> {
1575        match self {
1576            CnVariant::Auto => None,
1577            CnVariant::Simplified => Some(1),
1578            CnVariant::Traditional => Some(2),
1579        }
1580    }
1581
1582    fn from_variant(v: u32) -> Self {
1583        Self::from(v as u8)
1584    }
1585
1586    fn auto() -> Self {
1587        CnVariant::Auto
1588    }
1589}
1590
1591/// The sizing and spacing of figures used for East Asian characters.
1592#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
1593#[repr(u8)]
1594pub enum EastAsianWidth {
1595    /// Uses the font default glyphs and spacing.
1596    #[default]
1597    Auto,
1598
1599    /// Uses the set of glyphs designed for proportional spacing.
1600    ///
1601    /// This corresponds to OpenType `pwid` feature.
1602    Proportional,
1603
1604    /// Uses the set of glyphs designed for full-width but re-spaced to take proportional space.
1605    ///
1606    /// This corresponds to OpenType `palt` feature.
1607    ProportionalAlt,
1608
1609    /// Like [`Proportional`](Self::Proportional) but only affects kana and kana related glyphs.
1610    ///
1611    /// This corresponds to OpenType `pkna` feature.
1612    ProportionalKana,
1613
1614    /// Uses the set of glyphs designed for full-width monospace.
1615    ///
1616    /// This corresponds to OpenType `fwid` feature.
1617    Full,
1618
1619    /// Uses the set of glyphs designed for half-width monospace.
1620    ///
1621    /// This corresponds to OpenType `hwid` feature.
1622    Half,
1623
1624    /// Uses the set of glyphs designed for full-width but re-spaced to take half-width monospace.
1625    ///
1626    /// This corresponds to OpenType `halt` feature.
1627    HalfAlt,
1628
1629    /// Uses the set of glyphs designed for a third-width monospace.
1630    ///
1631    /// This corresponds to OpenType `twid` feature.
1632    Third,
1633
1634    /// Uses the set of glyphs designed for a quarter-width monospace.
1635    ///
1636    /// This corresponds to OpenType `qwid` feature.
1637    Quarter,
1638}
1639impl fmt::Debug for EastAsianWidth {
1640    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1641        if f.alternate() {
1642            write!(f, "EastAsianWidth::")?;
1643        }
1644        match self {
1645            EastAsianWidth::Auto => write!(f, "Auto"),
1646            EastAsianWidth::Proportional => write!(f, "Proportional"),
1647            EastAsianWidth::ProportionalAlt => write!(f, "ProportionalAlt"),
1648            EastAsianWidth::ProportionalKana => write!(f, "ProportionalKana"),
1649            EastAsianWidth::Full => write!(f, "Full"),
1650            EastAsianWidth::Half => write!(f, "Half"),
1651            EastAsianWidth::HalfAlt => write!(f, "HalfAlt"),
1652            EastAsianWidth::Third => write!(f, "Third"),
1653            EastAsianWidth::Quarter => write!(f, "Quarter"),
1654        }
1655    }
1656}
1657impl Default for EastAsianWidth {
1658    /// [`EastAsianWidth::Auto`]
1659    fn default() -> Self {
1660        EastAsianWidth::Auto
1661    }
1662}
1663impl FontFeatureExclusiveSetState for EastAsianWidth {
1664    fn names() -> &'static [FontFeatureName] {
1665        static NAMES: [FontFeatureName; 8] = [
1666            FontFeatureName(*b"pwid"),
1667            FontFeatureName(*b"palt"),
1668            FontFeatureName(*b"pkna"),
1669            FontFeatureName(*b"fwid"),
1670            FontFeatureName(*b"hwid"),
1671            FontFeatureName(*b"halt"),
1672            FontFeatureName(*b"twid"),
1673            FontFeatureName(*b"qwid"),
1674        ];
1675        &NAMES
1676    }
1677
1678    fn variant(self) -> Option<u32> {
1679        if self == EastAsianWidth::Auto { None } else { Some(self as u32) }
1680    }
1681
1682    fn from_variant(v: u32) -> Self {
1683        Self::from(v as u8)
1684    }
1685
1686    fn auto() -> Self {
1687        EastAsianWidth::Auto
1688    }
1689}
1690
1691/// Name of a font variation axis.
1692///
1693/// # Examples
1694///
1695/// ```
1696/// # use zng_ext_font::font_features::*;
1697/// let historical_lig: FontVariationName = b"BLDB".into();
1698/// ```
1699#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
1700pub struct FontVariationName(pub [u8; 4]);
1701impl FontVariationName {
1702    /// As UTF-8.
1703    pub fn as_str(&self) -> &str {
1704        std::str::from_utf8(&self.0).unwrap_or_default()
1705    }
1706}
1707impl From<&'static [u8; 4]> for FontVariationName {
1708    fn from(name: &'static [u8; 4]) -> Self {
1709        FontVariationName(*name)
1710    }
1711}
1712impl From<FontVariationName> for ttf_parser::Tag {
1713    fn from(value: FontVariationName) -> Self {
1714        ttf_parser::Tag::from_bytes(&value.0)
1715    }
1716}
1717impl ops::Deref for FontVariationName {
1718    type Target = [u8; 4];
1719
1720    fn deref(&self) -> &Self::Target {
1721        &self.0
1722    }
1723}
1724impl fmt::Debug for FontVariationName {
1725    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1726        if self.as_str().is_empty() {
1727            write!(f, "{:?}", self.0)
1728        } else {
1729            write!(f, "{}", self.as_str())
1730        }
1731    }
1732}
1733impl fmt::Display for FontVariationName {
1734    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1735        write!(f, "{self:?}")
1736    }
1737}
1738
1739/// A small map of font variations.
1740///
1741/// Use [`font_variations!`] to manually initialize.
1742#[derive(Default, Clone, PartialEq)]
1743pub struct FontVariations(Vec<(FontVariationName, f32)>);
1744impl FontVariations {
1745    /// New empty.
1746    pub fn new() -> Self {
1747        Self::default()
1748    }
1749
1750    /// New empty with pre-allocated capacity.
1751    pub fn with_capacity(capacity: usize) -> Self {
1752        Self(Vec::with_capacity(capacity))
1753    }
1754
1755    /// New font variations from pairs of name, value.
1756    pub fn from_pairs(pairs: &[(FontVariationName, f32)]) -> Self {
1757        let mut r = Self::with_capacity(pairs.len());
1758        for (name, value) in pairs {
1759            r.insert(*name, *value);
1760        }
1761        r
1762    }
1763
1764    /// Insert the font variation, returns the previous value if the variation was already set.
1765    pub fn insert(&mut self, name: FontVariationName, value: f32) -> Option<f32> {
1766        if let Some(entry) = self.0.iter_mut().find(|v| v.0 == name) {
1767            let prev = Some(entry.1);
1768            entry.1 = value;
1769            prev
1770        } else {
1771            self.0.push((name, value));
1772            None
1773        }
1774    }
1775
1776    /// Remove the font variation, returns the value if the variation was set.
1777    pub fn remove(&mut self, name: FontVariationName) -> Option<f32> {
1778        if let Some(i) = self.0.iter().position(|v| v.0 == name) {
1779            Some(self.0.swap_remove(i).1)
1780        } else {
1781            None
1782        }
1783    }
1784
1785    /// If the variation is set.
1786    pub fn contains(&self, name: FontVariationName) -> bool {
1787        self.0.iter().any(|v| v.0 == name)
1788    }
1789
1790    /// Gets a copy of the variation value if it is set.
1791    pub fn get(&self, name: FontVariationName) -> Option<f32> {
1792        self.0.iter().find(|v| v.0 == name).map(|v| v.1)
1793    }
1794
1795    /// Exclusive borrow the variation value if it is set.
1796    pub fn get_mut(&mut self, name: FontVariationName) -> Option<&mut f32> {
1797        self.0.iter_mut().find(|v| v.0 == name).map(|v| &mut v.1)
1798    }
1799
1800    /// Count of font variations set.
1801    pub fn len(&self) -> usize {
1802        self.0.len()
1803    }
1804
1805    /// If not font variation is set.
1806    pub fn is_empty(&self) -> bool {
1807        self.0.is_empty()
1808    }
1809
1810    /// Finalize variations config for use in a font.
1811    pub fn finalize(&self) -> RFontVariations {
1812        self.0
1813            .iter()
1814            .map(|(name, value)| rustybuzz::Variation {
1815                tag: (*name).into(),
1816                value: *value,
1817            })
1818            .collect()
1819    }
1820}
1821impl fmt::Debug for FontVariations {
1822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1823        if f.alternate() {
1824            f.debug_tuple("FontVariations").field(&self.0).finish()
1825        } else {
1826            write!(f, "[")?;
1827            let mut first = false;
1828            for entry in &self.0 {
1829                if first {
1830                    first = false;
1831                } else {
1832                    write!(f, ", ")?;
1833                }
1834                write!(f, r#", b"{}": {}"#, entry.0, entry.1)?;
1835            }
1836            write!(f, "]")
1837        }
1838    }
1839}
1840
1841/// Initialize a [`FontVariations`] map.
1842///
1843/// # Examples
1844///
1845/// ```
1846/// # use zng_ext_font::font_features::*;
1847/// # fn assert_type(_: FontVariations) { }
1848/// let variations = font_variations! {
1849///     b"SKLA": 1000.0,
1850///     b"TRMG": 750.0
1851/// };
1852/// # assert_type(variations);
1853/// ```
1854#[macro_export]
1855macro_rules! font_variations {
1856    [$(
1857        $name:tt : $value: expr
1858    ),* $(,)?] => {
1859        $crate::font_features::FontVariations::from_pairs(&[
1860            $(
1861                ($name.into(), $value),
1862            )*
1863        ])
1864    }
1865}
1866#[doc(inline)]
1867pub use font_variations;
1868use zng_var::impl_from_and_into_var;
1869
1870/// Finalized [`FontVariations`].
1871///
1872/// This is a vec of [harfbuzz variations](https://docs.rs/rustybuzz/0.17.0/rustybuzz/struct.Variation.html).
1873pub type RFontVariations = Vec<rustybuzz::Variation>;