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