1pub mod bitmap;
14pub mod color;
15pub mod outline;
16
17#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
19#[repr(u8)]
20pub enum GlyphSource {
21 Sbix = 1,
23 Cbdt,
25 Ebdt,
27 Colr,
29 Varc,
33 Glyf,
35 Cff2,
37 Cff,
39 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 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#[derive(Copy, Clone, PartialEq, Eq, Hash)]
84pub struct GlyphSourceOrder(u64);
85
86impl GlyphSourceOrder {
87 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 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 pub const OUTLINES: Self = Self::new(&[
124 GlyphSource::Varc,
125 GlyphSource::Glyf,
126 GlyphSource::Cff2,
127 GlyphSource::Cff,
128 GlyphSource::Type1,
129 ]);
130
131 pub const BASE_OUTLINES: Self = Self::new(&[
136 GlyphSource::Glyf,
137 GlyphSource::Cff2,
138 GlyphSource::Cff,
139 GlyphSource::Type1,
140 ]);
141
142 pub const BITMAPS: Self = Self::new(&[GlyphSource::Sbix, GlyphSource::Cbdt, GlyphSource::Ebdt]);
144
145 pub const COLOR_BITMAPS: Self = Self::new(&[GlyphSource::Sbix, GlyphSource::Cbdt]);
147
148 pub const MONO_BITMAPS: Self = Self::new(&[GlyphSource::Ebdt]);
150
151 pub const COLOR: Self = Self::new(&[GlyphSource::Colr]);
153
154 pub const EMPTY: Self = Self(0);
156
157 pub(crate) const MAX_LEN: usize = GlyphSource::ALL.len();
159
160 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 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 pub const fn is_empty(self) -> bool {
193 self.0 == 0
194 }
195
196 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 #[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 #[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}