fea_rs/
common.rs

1//! Types and helpers shared across modules
2
3use std::fmt::{Display, Formatter};
4
5use fontdrasil::types::GlyphName;
6use write_fonts::tables::gpos::builders::AnchorBuilder;
7pub use write_fonts::types::GlyphId16;
8
9mod glyph_class;
10mod glyph_map;
11
12pub(crate) use glyph_class::GlyphClass;
13
14pub use glyph_class::GlyphSet;
15pub use glyph_map::GlyphMap;
16
17/// A glyph or glyph class.
18///
19/// Various places in the FEA spec accept either a single glyph or a glyph class.
20#[derive(Debug, Clone)]
21pub(crate) enum GlyphOrClass {
22    /// A resolved GlyphId
23    Glyph(GlyphId16),
24    /// A resolved glyph class
25    Class(GlyphClass),
26    /// An explicit `<NULL>` glyph
27    Null,
28}
29
30/// Either a glyph name or a CID
31#[derive(Debug, Clone, PartialEq, Eq, Hash)]
32pub enum GlyphIdent {
33    /// A glyph name
34    Name(GlyphName),
35    /// a CID
36    Cid(u16),
37}
38
39#[derive(Clone, Debug, Default)]
40pub(crate) struct MarkClass {
41    pub(crate) members: Vec<(GlyphClass, Option<AnchorBuilder>)>,
42}
43
44impl<T: Into<GlyphName>> From<T> for GlyphIdent {
45    fn from(src: T) -> Self {
46        GlyphIdent::Name(src.into())
47    }
48}
49
50impl Display for GlyphIdent {
51    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
52        match self {
53            GlyphIdent::Name(name) => write!(f, "{}", name),
54            GlyphIdent::Cid(cid) => write!(f, "Cid({})", cid),
55        }
56    }
57}
58
59impl GlyphOrClass {
60    pub(crate) fn len(&self) -> usize {
61        match self {
62            GlyphOrClass::Class(cls) => cls.len(),
63            _ => 1,
64        }
65    }
66
67    pub(crate) fn is_class(&self) -> bool {
68        matches!(self, GlyphOrClass::Class(_))
69    }
70
71    pub(crate) fn is_null(&self) -> bool {
72        matches!(self, GlyphOrClass::Null)
73    }
74
75    pub(crate) fn to_class(&self) -> Option<GlyphClass> {
76        match self {
77            GlyphOrClass::Glyph(gid) => Some((*gid).into()),
78            GlyphOrClass::Class(class) => Some(class.clone()),
79            GlyphOrClass::Null => None,
80        }
81    }
82
83    pub(crate) fn to_glyph(&self) -> Option<GlyphId16> {
84        match self {
85            GlyphOrClass::Glyph(gid) => Some(*gid),
86            _ => None,
87        }
88    }
89
90    /// If this is a glyph or a class with exactly one, return it.
91    pub(crate) fn single_glyph(&self) -> Option<GlyphId16> {
92        match self {
93            GlyphOrClass::Glyph(gid) => Some(*gid),
94            GlyphOrClass::Class(class) if class.len() == 1 => class.iter().next(),
95            _ => None,
96        }
97    }
98
99    pub(crate) fn iter(&self) -> impl Iterator<Item = GlyphId16> + '_ {
100        let mut idx = 0;
101        std::iter::from_fn(move || {
102            let next = match &self {
103                GlyphOrClass::Glyph(id) if idx == 0 => Some(*id),
104                GlyphOrClass::Class(cls) => cls.items().get(idx).copied(),
105                _ => None,
106            };
107            idx += 1;
108            next
109        })
110    }
111
112    /// an iterator that loops forever, and which returns NOTDEF for null.
113    ///
114    /// this is used to create the replacement targets for class -> glyph or
115    /// class -> null substitutions.
116    pub(crate) fn into_iter_for_target(self) -> impl Iterator<Item = GlyphId16> {
117        let mut idx = 0;
118        std::iter::from_fn(move || {
119            let next = match &self {
120                GlyphOrClass::Glyph(id) if idx == 0 => Some(*id),
121                GlyphOrClass::Null if idx == 0 => Some(GlyphId16::NOTDEF),
122                GlyphOrClass::Class(cls) => cls.items().get(idx).copied(),
123                _ => None,
124            };
125            idx += 1;
126            idx %= self.len();
127            next
128        })
129    }
130}