/rust/registry/src/index.crates.io-1949cf8c6b5b557f/read-fonts-0.46.0/src/model/glyph.rs
Line | Count | Source |
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 | | |
13 | | pub mod bitmap; |
14 | | pub mod color; |
15 | | pub mod outline; |
16 | | |
17 | | /// A table that can provide a glyph. |
18 | | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
19 | | #[repr(u8)] |
20 | | pub 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 | | |
43 | | impl 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 | 870k | const fn from_nibble(nibble: u8) -> Option<Self> { |
58 | 870k | let index = nibble as usize; |
59 | 870k | if index == 0 || index > Self::ALL.len() { |
60 | 120k | None |
61 | | } else { |
62 | 749k | Some(Self::ALL[index - 1]) |
63 | | } |
64 | 870k | } |
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)] |
84 | | pub struct GlyphSourceOrder(u64); |
85 | | |
86 | | impl 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 | 45.0k | pub const fn new(sources: &[GlyphSource]) -> Self { |
164 | 45.0k | let mut packed = 0u64; |
165 | 45.0k | let mut len = 0; |
166 | 45.0k | let mut i = 0; |
167 | 90.1k | while i < sources.len() && len < Self::MAX_LEN { |
168 | 45.0k | let source = sources[i]; |
169 | 45.0k | i += 1; |
170 | 45.0k | if Self(packed).contains(source) { |
171 | 0 | continue; |
172 | 45.0k | } |
173 | 45.0k | packed |= (source as u64) << (len * 4); |
174 | 45.0k | len += 1; |
175 | | } |
176 | 45.0k | Self(packed) |
177 | 45.0k | } |
178 | | |
179 | | /// Returns `true` if the order contains `source`. |
180 | 45.0k | pub const fn contains(self, source: GlyphSource) -> bool { |
181 | 45.0k | let mut packed = self.0; |
182 | 45.0k | while packed != 0 { |
183 | 0 | if (packed & 0xF) as u8 == source as u8 { |
184 | 0 | return true; |
185 | 0 | } |
186 | 0 | packed >>= 4; |
187 | | } |
188 | 45.0k | false |
189 | 45.0k | } |
190 | | |
191 | | /// Returns `true` if the order contains no sources. |
192 | 0 | pub const fn is_empty(self) -> bool { |
193 | 0 | self.0 == 0 |
194 | 0 | } |
195 | | |
196 | | /// Returns an iterator over the sources, most preferred first. |
197 | 137k | pub fn iter(self) -> impl Iterator<Item = GlyphSource> { |
198 | 137k | let mut packed = self.0; |
199 | 870k | core::iter::from_fn(move || { |
200 | 870k | let source = GlyphSource::from_nibble((packed & 0xF) as u8)?; |
201 | 749k | packed >>= 4; |
202 | 749k | Some(source) |
203 | 870k | }) |
204 | 137k | } |
205 | | } |
206 | | |
207 | | impl Default for GlyphSourceOrder { |
208 | 0 | fn default() -> Self { |
209 | 0 | Self::DEFAULT |
210 | 0 | } |
211 | | } |
212 | | |
213 | | impl core::fmt::Debug for GlyphSourceOrder { |
214 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
215 | 0 | f.debug_list().entries(self.iter()).finish() |
216 | 0 | } |
217 | | } |
218 | | |
219 | | #[cfg(test)] |
220 | | mod 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 | | } |