Skip to main content

read_fonts/model/
glyph.rs

1//! Glyph representations: outlines, bitmaps and color.
2//!
3//! A font can contain a glyph in several forms. [`GlyphSource`] identifies
4//! the tables that provide them, and [`GlyphSourceOrder`] sets which ones to
5//! prefer. Collections and glyphs report a narrower type,
6//! [`outline::OutlineSource`] or [`bitmap::BitmapSource`], and each converts
7//! into a [`GlyphSource`].
8//!
9//! The collections [`outline::OutlineGlyphs`] and [`bitmap::BitmapStrikes`]
10//! resolve an order against a font once, and [`color::ColorGlyphs`] reads
11//! `COLR`. Glyph metrics use an order to measure ink.
12
13pub mod bitmap;
14pub mod color;
15pub mod outline;
16
17/// A table that can provide a glyph.
18#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
19#[repr(u8)]
20pub enum GlyphSource {
21    /// PNG bitmaps in `sbix`.
22    Sbix = 1,
23    /// Color bitmaps in `CBDT`.
24    Cbdt,
25    /// Monochrome and grayscale bitmaps in `EBDT`.
26    Ebdt,
27    /// Layered color glyphs in `COLR`, version 0 or 1.
28    Colr,
29    /// Variable composites in `VARC`.
30    ///
31    /// The components are drawn from one of the outline sources below.
32    Varc,
33    /// TrueType outlines in `glyf`.
34    Glyf,
35    /// Variable PostScript outlines in `CFF2`.
36    Cff2,
37    /// PostScript outlines in `CFF`.
38    Cff,
39    /// Charstrings of a Type 1 font.
40    Type1,
41}
42
43impl GlyphSource {
44    const ALL: [Self; 9] = [
45        Self::Sbix,
46        Self::Cbdt,
47        Self::Ebdt,
48        Self::Colr,
49        Self::Varc,
50        Self::Glyf,
51        Self::Cff2,
52        Self::Cff,
53        Self::Type1,
54    ];
55
56    /// Returns the source for a packed nibble.
57    const fn from_nibble(nibble: u8) -> Option<Self> {
58        let index = nibble as usize;
59        if index == 0 || index > Self::ALL.len() {
60            None
61        } else {
62            Some(Self::ALL[index - 1])
63        }
64    }
65}
66
67/// An ordered list of glyph sources, most preferred first.
68///
69/// Holds every source, four bits each, in eight bytes. It can be used as a
70/// constant and passed by value. A source listed more than once keeps only
71/// its first position.
72///
73/// How an order is used depends on the consumer:
74///
75/// - Outlines use one base source per font. This is the first of `Glyf`,
76///   `Cff2`, `Cff` and `Type1` that the font contains. If `Varc` comes before
77///   the base source, it applies to the glyphs it covers, and its components
78///   are drawn from the base source.
79/// - Bitmaps use the first of `Sbix`, `Cbdt` and `Ebdt` that the font
80///   contains.
81/// - Glyph extents try each source in the font, in order, until one provides
82///   extents for the glyph.
83#[derive(Copy, Clone, PartialEq, Eq, Hash)]
84pub struct GlyphSourceOrder(u64);
85
86impl GlyphSourceOrder {
87    /// The default order for measuring glyphs: `sbix`, `CBDT`, `COLR`,
88    /// `VARC`, `glyf`, `CFF2`, `CFF`, then Type 1 charstrings.
89    ///
90    /// Color bitmaps come first, then color glyphs, then outlines with
91    /// `VARC` before its base source. `EBDT` is not included, so monochrome
92    /// and grayscale bitmaps are used only when a caller asks for them.
93    pub const DEFAULT: Self = Self::new(&[
94        GlyphSource::Sbix,
95        GlyphSource::Cbdt,
96        GlyphSource::Colr,
97        GlyphSource::Varc,
98        GlyphSource::Glyf,
99        GlyphSource::Cff2,
100        GlyphSource::Cff,
101        GlyphSource::Type1,
102    ]);
103
104    /// Every source: `sbix`, `CBDT`, `EBDT`, `COLR`, `VARC`, `glyf`, `CFF2`,
105    /// `CFF`, then Type 1 charstrings.
106    ///
107    /// The order of [`DEFAULT`](Self::DEFAULT), with `EBDT` after the color
108    /// bitmaps.
109    pub const ALL: Self = Self::new(&[
110        GlyphSource::Sbix,
111        GlyphSource::Cbdt,
112        GlyphSource::Ebdt,
113        GlyphSource::Colr,
114        GlyphSource::Varc,
115        GlyphSource::Glyf,
116        GlyphSource::Cff2,
117        GlyphSource::Cff,
118        GlyphSource::Type1,
119    ]);
120
121    /// The outline sources: `VARC`, `glyf`, `CFF2`, `CFF`, then Type 1
122    /// charstrings.
123    pub const OUTLINES: Self = Self::new(&[
124        GlyphSource::Varc,
125        GlyphSource::Glyf,
126        GlyphSource::Cff2,
127        GlyphSource::Cff,
128        GlyphSource::Type1,
129    ]);
130
131    /// The outline sources without `VARC`: `glyf`, `CFF2`, `CFF`, then Type
132    /// 1 charstrings.
133    ///
134    /// Every glyph is drawn from the base source.
135    pub const BASE_OUTLINES: Self = Self::new(&[
136        GlyphSource::Glyf,
137        GlyphSource::Cff2,
138        GlyphSource::Cff,
139        GlyphSource::Type1,
140    ]);
141
142    /// The bitmap sources: `sbix`, `CBDT`, then `EBDT`.
143    pub const BITMAPS: Self = Self::new(&[GlyphSource::Sbix, GlyphSource::Cbdt, GlyphSource::Ebdt]);
144
145    /// The color bitmap sources: `sbix`, then `CBDT`.
146    pub const COLOR_BITMAPS: Self = Self::new(&[GlyphSource::Sbix, GlyphSource::Cbdt]);
147
148    /// The monochrome and grayscale bitmap source: `EBDT`.
149    pub const MONO_BITMAPS: Self = Self::new(&[GlyphSource::Ebdt]);
150
151    /// The color glyph source: `COLR`.
152    pub const COLOR: Self = Self::new(&[GlyphSource::Colr]);
153
154    /// No sources.
155    pub const EMPTY: Self = Self(0);
156
157    /// The most sources an order holds: each one at most once.
158    pub(crate) const MAX_LEN: usize = GlyphSource::ALL.len();
159
160    /// Creates an order from `sources`.
161    ///
162    /// A source listed more than once keeps only its first position.
163    pub const fn new(sources: &[GlyphSource]) -> Self {
164        let mut packed = 0u64;
165        let mut len = 0;
166        let mut i = 0;
167        while i < sources.len() && len < Self::MAX_LEN {
168            let source = sources[i];
169            i += 1;
170            if Self(packed).contains(source) {
171                continue;
172            }
173            packed |= (source as u64) << (len * 4);
174            len += 1;
175        }
176        Self(packed)
177    }
178
179    /// Returns `true` if the order contains `source`.
180    pub const fn contains(self, source: GlyphSource) -> bool {
181        let mut packed = self.0;
182        while packed != 0 {
183            if (packed & 0xF) as u8 == source as u8 {
184                return true;
185            }
186            packed >>= 4;
187        }
188        false
189    }
190
191    /// Returns `true` if the order contains no sources.
192    pub const fn is_empty(self) -> bool {
193        self.0 == 0
194    }
195
196    /// Returns an iterator over the sources, most preferred first.
197    pub fn iter(self) -> impl Iterator<Item = GlyphSource> {
198        let mut packed = self.0;
199        core::iter::from_fn(move || {
200            let source = GlyphSource::from_nibble((packed & 0xF) as u8)?;
201            packed >>= 4;
202            Some(source)
203        })
204    }
205}
206
207impl Default for GlyphSourceOrder {
208    fn default() -> Self {
209        Self::DEFAULT
210    }
211}
212
213impl core::fmt::Debug for GlyphSourceOrder {
214    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
215        f.debug_list().entries(self.iter()).finish()
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222    use alloc::vec::Vec;
223    use bitmap::BitmapSource;
224    use outline::OutlineSource;
225
226    // Each source converts to the narrow type of its kind and back, and to
227    // no other.
228    #[test]
229    fn narrow_sources_round_trip() {
230        for source in GlyphSource::ALL {
231            let outline = OutlineSource::try_from(source);
232            let bitmap = BitmapSource::try_from(source);
233            assert_eq!(outline.map(GlyphSource::from).unwrap_or(source), source);
234            assert_eq!(bitmap.map(GlyphSource::from).unwrap_or(source), source);
235            let kinds = usize::from(outline.is_ok()) + usize::from(bitmap.is_ok());
236            assert_eq!(
237                kinds,
238                usize::from(source != GlyphSource::Colr),
239                "{source:?}"
240            );
241        }
242    }
243
244    // The named orders hold the sources their documentation lists.
245    #[test]
246    fn named_orders_hold_their_sources() {
247        use GlyphSource::*;
248        let sources = |order: GlyphSourceOrder| order.iter().collect::<Vec<_>>();
249        assert_eq!(sources(GlyphSourceOrder::ALL), GlyphSource::ALL);
250        assert_eq!(
251            sources(GlyphSourceOrder::DEFAULT),
252            [Sbix, Cbdt, Colr, Varc, Glyf, Cff2, Cff, Type1]
253        );
254        assert_eq!(sources(GlyphSourceOrder::BITMAPS), [Sbix, Cbdt, Ebdt]);
255        assert_eq!(sources(GlyphSourceOrder::COLOR_BITMAPS), [Sbix, Cbdt]);
256        assert_eq!(sources(GlyphSourceOrder::MONO_BITMAPS), [Ebdt]);
257        assert_eq!(sources(GlyphSourceOrder::COLOR), [Colr]);
258        assert_eq!(
259            sources(GlyphSourceOrder::OUTLINES),
260            [Varc, Glyf, Cff2, Cff, Type1]
261        );
262        assert_eq!(
263            sources(GlyphSourceOrder::BASE_OUTLINES),
264            [Glyf, Cff2, Cff, Type1]
265        );
266        assert!(GlyphSourceOrder::EMPTY.is_empty());
267        assert_eq!(
268            alloc::format!("{:?}", GlyphSourceOrder::COLOR_BITMAPS),
269            "[Sbix, Cbdt]"
270        );
271    }
272
273    #[test]
274    fn order_keeps_first_position_of_each_source() {
275        let order = GlyphSourceOrder::new(&[
276            GlyphSource::Glyf,
277            GlyphSource::Varc,
278            GlyphSource::Glyf,
279            GlyphSource::Cff,
280        ]);
281        let sources: Vec<_> = order.iter().collect();
282        assert_eq!(
283            sources,
284            [GlyphSource::Glyf, GlyphSource::Varc, GlyphSource::Cff]
285        );
286        assert!(order.contains(GlyphSource::Cff));
287        assert!(!order.contains(GlyphSource::Cff2));
288    }
289
290    #[test]
291    fn default_order_is_the_harfbuzz_extents_chain() {
292        let sources: Vec<_> = GlyphSourceOrder::DEFAULT.iter().collect();
293        assert_eq!(
294            sources,
295            [
296                GlyphSource::Sbix,
297                GlyphSource::Cbdt,
298                GlyphSource::Colr,
299                GlyphSource::Varc,
300                GlyphSource::Glyf,
301                GlyphSource::Cff2,
302                GlyphSource::Cff,
303                GlyphSource::Type1,
304            ]
305        );
306        assert!(!GlyphSourceOrder::DEFAULT.contains(GlyphSource::Ebdt));
307    }
308
309    #[test]
310    fn empty_order_has_no_sources() {
311        assert!(GlyphSourceOrder::EMPTY.is_empty());
312        assert_eq!(GlyphSourceOrder::EMPTY.iter().count(), 0);
313        assert!(!GlyphSourceOrder::new(&[]).contains(GlyphSource::Glyf));
314    }
315
316    #[test]
317    fn every_source_round_trips_through_an_order() {
318        let order = GlyphSourceOrder::new(&GlyphSource::ALL);
319        let sources: Vec<_> = order.iter().collect();
320        assert_eq!(sources, GlyphSource::ALL);
321    }
322}