read_fonts/generated/
generated_aat.rs

1// THIS FILE IS AUTOGENERATED.
2// Any changes to this file will be overwritten.
3// For more information about how codegen works, see font-codegen/README.md
4
5#[allow(unused_imports)]
6use crate::codegen_prelude::*;
7
8/// Lookup tables provide a way of looking up information about a glyph index.
9/// The different cmap subtable formats.
10#[derive(Clone)]
11pub enum Lookup<'a> {
12    Format0(Lookup0<'a>),
13    Format2(Lookup2<'a>),
14    Format4(Lookup4<'a>),
15    Format6(Lookup6<'a>),
16    Format8(Lookup8<'a>),
17    Format10(Lookup10<'a>),
18}
19
20impl<'a> Lookup<'a> {
21    ///Return the `FontData` used to resolve offsets for this table.
22    pub fn offset_data(&self) -> FontData<'a> {
23        match self {
24            Self::Format0(item) => item.offset_data(),
25            Self::Format2(item) => item.offset_data(),
26            Self::Format4(item) => item.offset_data(),
27            Self::Format6(item) => item.offset_data(),
28            Self::Format8(item) => item.offset_data(),
29            Self::Format10(item) => item.offset_data(),
30        }
31    }
32
33    /// Format number is set to 0.
34    pub fn format(&self) -> u16 {
35        match self {
36            Self::Format0(item) => item.format(),
37            Self::Format2(item) => item.format(),
38            Self::Format4(item) => item.format(),
39            Self::Format6(item) => item.format(),
40            Self::Format8(item) => item.format(),
41            Self::Format10(item) => item.format(),
42        }
43    }
44}
45
46impl<'a> FontRead<'a> for Lookup<'a> {
47    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
48        let format: u16 = data.read_at(0usize)?;
49        match format {
50            Lookup0Marker::FORMAT => Ok(Self::Format0(FontRead::read(data)?)),
51            Lookup2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
52            Lookup4Marker::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
53            Lookup6Marker::FORMAT => Ok(Self::Format6(FontRead::read(data)?)),
54            Lookup8Marker::FORMAT => Ok(Self::Format8(FontRead::read(data)?)),
55            Lookup10Marker::FORMAT => Ok(Self::Format10(FontRead::read(data)?)),
56            other => Err(ReadError::InvalidFormat(other.into())),
57        }
58    }
59}
60
61impl MinByteRange for Lookup<'_> {
62    fn min_byte_range(&self) -> Range<usize> {
63        match self {
64            Self::Format0(item) => item.min_byte_range(),
65            Self::Format2(item) => item.min_byte_range(),
66            Self::Format4(item) => item.min_byte_range(),
67            Self::Format6(item) => item.min_byte_range(),
68            Self::Format8(item) => item.min_byte_range(),
69            Self::Format10(item) => item.min_byte_range(),
70        }
71    }
72}
73
74#[cfg(feature = "experimental_traverse")]
75impl<'a> Lookup<'a> {
76    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
77        match self {
78            Self::Format0(table) => table,
79            Self::Format2(table) => table,
80            Self::Format4(table) => table,
81            Self::Format6(table) => table,
82            Self::Format8(table) => table,
83            Self::Format10(table) => table,
84        }
85    }
86}
87
88#[cfg(feature = "experimental_traverse")]
89impl std::fmt::Debug for Lookup<'_> {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        self.dyn_inner().fmt(f)
92    }
93}
94
95#[cfg(feature = "experimental_traverse")]
96impl<'a> SomeTable<'a> for Lookup<'a> {
97    fn type_name(&self) -> &str {
98        self.dyn_inner().type_name()
99    }
100    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
101        self.dyn_inner().get_field(idx)
102    }
103}
104
105impl Format<u16> for Lookup0Marker {
106    const FORMAT: u16 = 0;
107}
108
109/// Simple array format. The lookup data is an array of lookup values, indexed
110/// by glyph index.
111#[derive(Debug, Clone, Copy)]
112#[doc(hidden)]
113pub struct Lookup0Marker {
114    values_data_byte_len: usize,
115}
116
117impl Lookup0Marker {
118    pub fn format_byte_range(&self) -> Range<usize> {
119        let start = 0;
120        start..start + u16::RAW_BYTE_LEN
121    }
122
123    pub fn values_data_byte_range(&self) -> Range<usize> {
124        let start = self.format_byte_range().end;
125        start..start + self.values_data_byte_len
126    }
127}
128
129impl MinByteRange for Lookup0Marker {
130    fn min_byte_range(&self) -> Range<usize> {
131        0..self.values_data_byte_range().end
132    }
133}
134
135impl<'a> FontRead<'a> for Lookup0<'a> {
136    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
137        let mut cursor = data.cursor();
138        cursor.advance::<u16>();
139        let values_data_byte_len = cursor.remaining_bytes() / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN;
140        cursor.advance_by(values_data_byte_len);
141        cursor.finish(Lookup0Marker {
142            values_data_byte_len,
143        })
144    }
145}
146
147/// Simple array format. The lookup data is an array of lookup values, indexed
148/// by glyph index.
149pub type Lookup0<'a> = TableRef<'a, Lookup0Marker>;
150
151#[allow(clippy::needless_lifetimes)]
152impl<'a> Lookup0<'a> {
153    /// Format number is set to 0.
154    pub fn format(&self) -> u16 {
155        let range = self.shape.format_byte_range();
156        self.data.read_at(range.start).unwrap()
157    }
158
159    /// Values, indexed by glyph index.
160    pub fn values_data(&self) -> &'a [u8] {
161        let range = self.shape.values_data_byte_range();
162        self.data.read_array(range).unwrap()
163    }
164}
165
166#[cfg(feature = "experimental_traverse")]
167impl<'a> SomeTable<'a> for Lookup0<'a> {
168    fn type_name(&self) -> &str {
169        "Lookup0"
170    }
171    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
172        match idx {
173            0usize => Some(Field::new("format", self.format())),
174            1usize => Some(Field::new("values_data", self.values_data())),
175            _ => None,
176        }
177    }
178}
179
180#[cfg(feature = "experimental_traverse")]
181#[allow(clippy::needless_lifetimes)]
182impl<'a> std::fmt::Debug for Lookup0<'a> {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        (self as &dyn SomeTable<'a>).fmt(f)
185    }
186}
187
188impl Format<u16> for Lookup2Marker {
189    const FORMAT: u16 = 2;
190}
191
192/// Segment single format. Each non-overlapping segment has a single lookup
193/// value that applies to all glyphs in the segment. A segment is defined as
194/// a contiguous range of glyph indexes.
195#[derive(Debug, Clone, Copy)]
196#[doc(hidden)]
197pub struct Lookup2Marker {
198    segments_data_byte_len: usize,
199}
200
201impl Lookup2Marker {
202    pub fn format_byte_range(&self) -> Range<usize> {
203        let start = 0;
204        start..start + u16::RAW_BYTE_LEN
205    }
206
207    pub fn unit_size_byte_range(&self) -> Range<usize> {
208        let start = self.format_byte_range().end;
209        start..start + u16::RAW_BYTE_LEN
210    }
211
212    pub fn n_units_byte_range(&self) -> Range<usize> {
213        let start = self.unit_size_byte_range().end;
214        start..start + u16::RAW_BYTE_LEN
215    }
216
217    pub fn search_range_byte_range(&self) -> Range<usize> {
218        let start = self.n_units_byte_range().end;
219        start..start + u16::RAW_BYTE_LEN
220    }
221
222    pub fn entry_selector_byte_range(&self) -> Range<usize> {
223        let start = self.search_range_byte_range().end;
224        start..start + u16::RAW_BYTE_LEN
225    }
226
227    pub fn range_shift_byte_range(&self) -> Range<usize> {
228        let start = self.entry_selector_byte_range().end;
229        start..start + u16::RAW_BYTE_LEN
230    }
231
232    pub fn segments_data_byte_range(&self) -> Range<usize> {
233        let start = self.range_shift_byte_range().end;
234        start..start + self.segments_data_byte_len
235    }
236}
237
238impl MinByteRange for Lookup2Marker {
239    fn min_byte_range(&self) -> Range<usize> {
240        0..self.segments_data_byte_range().end
241    }
242}
243
244impl<'a> FontRead<'a> for Lookup2<'a> {
245    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
246        let mut cursor = data.cursor();
247        cursor.advance::<u16>();
248        let unit_size: u16 = cursor.read()?;
249        let n_units: u16 = cursor.read()?;
250        cursor.advance::<u16>();
251        cursor.advance::<u16>();
252        cursor.advance::<u16>();
253        let segments_data_byte_len = (transforms::add_multiply(unit_size, 0_usize, n_units))
254            .checked_mul(u8::RAW_BYTE_LEN)
255            .ok_or(ReadError::OutOfBounds)?;
256        cursor.advance_by(segments_data_byte_len);
257        cursor.finish(Lookup2Marker {
258            segments_data_byte_len,
259        })
260    }
261}
262
263/// Segment single format. Each non-overlapping segment has a single lookup
264/// value that applies to all glyphs in the segment. A segment is defined as
265/// a contiguous range of glyph indexes.
266pub type Lookup2<'a> = TableRef<'a, Lookup2Marker>;
267
268#[allow(clippy::needless_lifetimes)]
269impl<'a> Lookup2<'a> {
270    /// Format number is set to 2.
271    pub fn format(&self) -> u16 {
272        let range = self.shape.format_byte_range();
273        self.data.read_at(range.start).unwrap()
274    }
275
276    /// Size of a lookup unit for this search in bytes.
277    pub fn unit_size(&self) -> u16 {
278        let range = self.shape.unit_size_byte_range();
279        self.data.read_at(range.start).unwrap()
280    }
281
282    /// Number of units of the preceding size to be searched.
283    pub fn n_units(&self) -> u16 {
284        let range = self.shape.n_units_byte_range();
285        self.data.read_at(range.start).unwrap()
286    }
287
288    /// The value of unitSize times the largest power of 2 that is less than or equal to the value of nUnits.
289    pub fn search_range(&self) -> u16 {
290        let range = self.shape.search_range_byte_range();
291        self.data.read_at(range.start).unwrap()
292    }
293
294    /// The log base 2 of the largest power of 2 less than or equal to the value of nUnits.
295    pub fn entry_selector(&self) -> u16 {
296        let range = self.shape.entry_selector_byte_range();
297        self.data.read_at(range.start).unwrap()
298    }
299
300    /// The value of unitSize times the difference of the value of nUnits minus the largest power of 2 less than or equal to the value of nUnits.
301    pub fn range_shift(&self) -> u16 {
302        let range = self.shape.range_shift_byte_range();
303        self.data.read_at(range.start).unwrap()
304    }
305
306    /// Segments.
307    pub fn segments_data(&self) -> &'a [u8] {
308        let range = self.shape.segments_data_byte_range();
309        self.data.read_array(range).unwrap()
310    }
311}
312
313#[cfg(feature = "experimental_traverse")]
314impl<'a> SomeTable<'a> for Lookup2<'a> {
315    fn type_name(&self) -> &str {
316        "Lookup2"
317    }
318    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
319        match idx {
320            0usize => Some(Field::new("format", self.format())),
321            1usize => Some(Field::new("unit_size", self.unit_size())),
322            2usize => Some(Field::new("n_units", self.n_units())),
323            3usize => Some(Field::new("search_range", self.search_range())),
324            4usize => Some(Field::new("entry_selector", self.entry_selector())),
325            5usize => Some(Field::new("range_shift", self.range_shift())),
326            6usize => Some(Field::new("segments_data", self.segments_data())),
327            _ => None,
328        }
329    }
330}
331
332#[cfg(feature = "experimental_traverse")]
333#[allow(clippy::needless_lifetimes)]
334impl<'a> std::fmt::Debug for Lookup2<'a> {
335    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336        (self as &dyn SomeTable<'a>).fmt(f)
337    }
338}
339
340impl Format<u16> for Lookup4Marker {
341    const FORMAT: u16 = 4;
342}
343
344/// Segment array format. A segment mapping is performed (as with Format 2),
345/// but instead of a single lookup value for all the glyphs in the segment,
346/// each glyph in the segment gets its own separate lookup value.
347#[derive(Debug, Clone, Copy)]
348#[doc(hidden)]
349pub struct Lookup4Marker {
350    segments_byte_len: usize,
351}
352
353impl Lookup4Marker {
354    pub fn format_byte_range(&self) -> Range<usize> {
355        let start = 0;
356        start..start + u16::RAW_BYTE_LEN
357    }
358
359    pub fn unit_size_byte_range(&self) -> Range<usize> {
360        let start = self.format_byte_range().end;
361        start..start + u16::RAW_BYTE_LEN
362    }
363
364    pub fn n_units_byte_range(&self) -> Range<usize> {
365        let start = self.unit_size_byte_range().end;
366        start..start + u16::RAW_BYTE_LEN
367    }
368
369    pub fn search_range_byte_range(&self) -> Range<usize> {
370        let start = self.n_units_byte_range().end;
371        start..start + u16::RAW_BYTE_LEN
372    }
373
374    pub fn entry_selector_byte_range(&self) -> Range<usize> {
375        let start = self.search_range_byte_range().end;
376        start..start + u16::RAW_BYTE_LEN
377    }
378
379    pub fn range_shift_byte_range(&self) -> Range<usize> {
380        let start = self.entry_selector_byte_range().end;
381        start..start + u16::RAW_BYTE_LEN
382    }
383
384    pub fn segments_byte_range(&self) -> Range<usize> {
385        let start = self.range_shift_byte_range().end;
386        start..start + self.segments_byte_len
387    }
388}
389
390impl MinByteRange for Lookup4Marker {
391    fn min_byte_range(&self) -> Range<usize> {
392        0..self.segments_byte_range().end
393    }
394}
395
396impl<'a> FontRead<'a> for Lookup4<'a> {
397    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
398        let mut cursor = data.cursor();
399        cursor.advance::<u16>();
400        cursor.advance::<u16>();
401        let n_units: u16 = cursor.read()?;
402        cursor.advance::<u16>();
403        cursor.advance::<u16>();
404        cursor.advance::<u16>();
405        let segments_byte_len = (n_units as usize)
406            .checked_mul(LookupSegment4::RAW_BYTE_LEN)
407            .ok_or(ReadError::OutOfBounds)?;
408        cursor.advance_by(segments_byte_len);
409        cursor.finish(Lookup4Marker { segments_byte_len })
410    }
411}
412
413/// Segment array format. A segment mapping is performed (as with Format 2),
414/// but instead of a single lookup value for all the glyphs in the segment,
415/// each glyph in the segment gets its own separate lookup value.
416pub type Lookup4<'a> = TableRef<'a, Lookup4Marker>;
417
418#[allow(clippy::needless_lifetimes)]
419impl<'a> Lookup4<'a> {
420    /// Format number is set to 4.
421    pub fn format(&self) -> u16 {
422        let range = self.shape.format_byte_range();
423        self.data.read_at(range.start).unwrap()
424    }
425
426    /// Size of a lookup unit for this search in bytes.
427    pub fn unit_size(&self) -> u16 {
428        let range = self.shape.unit_size_byte_range();
429        self.data.read_at(range.start).unwrap()
430    }
431
432    /// Number of units of the preceding size to be searched.
433    pub fn n_units(&self) -> u16 {
434        let range = self.shape.n_units_byte_range();
435        self.data.read_at(range.start).unwrap()
436    }
437
438    /// The value of unitSize times the largest power of 2 that is less than or equal to the value of nUnits.
439    pub fn search_range(&self) -> u16 {
440        let range = self.shape.search_range_byte_range();
441        self.data.read_at(range.start).unwrap()
442    }
443
444    /// The log base 2 of the largest power of 2 less than or equal to the value of nUnits.
445    pub fn entry_selector(&self) -> u16 {
446        let range = self.shape.entry_selector_byte_range();
447        self.data.read_at(range.start).unwrap()
448    }
449
450    /// The value of unitSize times the difference of the value of nUnits minus the largest power of 2 less than or equal to the value of nUnits.
451    pub fn range_shift(&self) -> u16 {
452        let range = self.shape.range_shift_byte_range();
453        self.data.read_at(range.start).unwrap()
454    }
455
456    /// Segments.
457    pub fn segments(&self) -> &'a [LookupSegment4] {
458        let range = self.shape.segments_byte_range();
459        self.data.read_array(range).unwrap()
460    }
461}
462
463#[cfg(feature = "experimental_traverse")]
464impl<'a> SomeTable<'a> for Lookup4<'a> {
465    fn type_name(&self) -> &str {
466        "Lookup4"
467    }
468    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
469        match idx {
470            0usize => Some(Field::new("format", self.format())),
471            1usize => Some(Field::new("unit_size", self.unit_size())),
472            2usize => Some(Field::new("n_units", self.n_units())),
473            3usize => Some(Field::new("search_range", self.search_range())),
474            4usize => Some(Field::new("entry_selector", self.entry_selector())),
475            5usize => Some(Field::new("range_shift", self.range_shift())),
476            6usize => Some(Field::new(
477                "segments",
478                traversal::FieldType::array_of_records(
479                    stringify!(LookupSegment4),
480                    self.segments(),
481                    self.offset_data(),
482                ),
483            )),
484            _ => None,
485        }
486    }
487}
488
489#[cfg(feature = "experimental_traverse")]
490#[allow(clippy::needless_lifetimes)]
491impl<'a> std::fmt::Debug for Lookup4<'a> {
492    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493        (self as &dyn SomeTable<'a>).fmt(f)
494    }
495}
496
497/// Lookup segment for format 4.
498#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
499#[repr(C)]
500#[repr(packed)]
501pub struct LookupSegment4 {
502    /// Last glyph index in this segment.
503    pub last_glyph: BigEndian<u16>,
504    /// First glyph index in this segment.
505    pub first_glyph: BigEndian<u16>,
506    /// A 16-bit offset from the start of the table to the data.
507    pub value_offset: BigEndian<u16>,
508}
509
510impl LookupSegment4 {
511    /// Last glyph index in this segment.
512    pub fn last_glyph(&self) -> u16 {
513        self.last_glyph.get()
514    }
515
516    /// First glyph index in this segment.
517    pub fn first_glyph(&self) -> u16 {
518        self.first_glyph.get()
519    }
520
521    /// A 16-bit offset from the start of the table to the data.
522    pub fn value_offset(&self) -> u16 {
523        self.value_offset.get()
524    }
525}
526
527impl FixedSize for LookupSegment4 {
528    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
529}
530
531#[cfg(feature = "experimental_traverse")]
532impl<'a> SomeRecord<'a> for LookupSegment4 {
533    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
534        RecordResolver {
535            name: "LookupSegment4",
536            get_field: Box::new(move |idx, _data| match idx {
537                0usize => Some(Field::new("last_glyph", self.last_glyph())),
538                1usize => Some(Field::new("first_glyph", self.first_glyph())),
539                2usize => Some(Field::new("value_offset", self.value_offset())),
540                _ => None,
541            }),
542            data,
543        }
544    }
545}
546
547impl Format<u16> for Lookup6Marker {
548    const FORMAT: u16 = 6;
549}
550
551/// Single table format. The lookup data is a sorted list of
552/// <glyph index,lookup value> pairs.
553#[derive(Debug, Clone, Copy)]
554#[doc(hidden)]
555pub struct Lookup6Marker {
556    entries_data_byte_len: usize,
557}
558
559impl Lookup6Marker {
560    pub fn format_byte_range(&self) -> Range<usize> {
561        let start = 0;
562        start..start + u16::RAW_BYTE_LEN
563    }
564
565    pub fn unit_size_byte_range(&self) -> Range<usize> {
566        let start = self.format_byte_range().end;
567        start..start + u16::RAW_BYTE_LEN
568    }
569
570    pub fn n_units_byte_range(&self) -> Range<usize> {
571        let start = self.unit_size_byte_range().end;
572        start..start + u16::RAW_BYTE_LEN
573    }
574
575    pub fn search_range_byte_range(&self) -> Range<usize> {
576        let start = self.n_units_byte_range().end;
577        start..start + u16::RAW_BYTE_LEN
578    }
579
580    pub fn entry_selector_byte_range(&self) -> Range<usize> {
581        let start = self.search_range_byte_range().end;
582        start..start + u16::RAW_BYTE_LEN
583    }
584
585    pub fn range_shift_byte_range(&self) -> Range<usize> {
586        let start = self.entry_selector_byte_range().end;
587        start..start + u16::RAW_BYTE_LEN
588    }
589
590    pub fn entries_data_byte_range(&self) -> Range<usize> {
591        let start = self.range_shift_byte_range().end;
592        start..start + self.entries_data_byte_len
593    }
594}
595
596impl MinByteRange for Lookup6Marker {
597    fn min_byte_range(&self) -> Range<usize> {
598        0..self.entries_data_byte_range().end
599    }
600}
601
602impl<'a> FontRead<'a> for Lookup6<'a> {
603    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
604        let mut cursor = data.cursor();
605        cursor.advance::<u16>();
606        let unit_size: u16 = cursor.read()?;
607        let n_units: u16 = cursor.read()?;
608        cursor.advance::<u16>();
609        cursor.advance::<u16>();
610        cursor.advance::<u16>();
611        let entries_data_byte_len = (transforms::add_multiply(unit_size, 0_usize, n_units))
612            .checked_mul(u8::RAW_BYTE_LEN)
613            .ok_or(ReadError::OutOfBounds)?;
614        cursor.advance_by(entries_data_byte_len);
615        cursor.finish(Lookup6Marker {
616            entries_data_byte_len,
617        })
618    }
619}
620
621/// Single table format. The lookup data is a sorted list of
622/// <glyph index,lookup value> pairs.
623pub type Lookup6<'a> = TableRef<'a, Lookup6Marker>;
624
625#[allow(clippy::needless_lifetimes)]
626impl<'a> Lookup6<'a> {
627    /// Format number is set to 6.
628    pub fn format(&self) -> u16 {
629        let range = self.shape.format_byte_range();
630        self.data.read_at(range.start).unwrap()
631    }
632
633    /// Size of a lookup unit for this search in bytes.
634    pub fn unit_size(&self) -> u16 {
635        let range = self.shape.unit_size_byte_range();
636        self.data.read_at(range.start).unwrap()
637    }
638
639    /// Number of units of the preceding size to be searched.
640    pub fn n_units(&self) -> u16 {
641        let range = self.shape.n_units_byte_range();
642        self.data.read_at(range.start).unwrap()
643    }
644
645    /// The value of unitSize times the largest power of 2 that is less than or equal to the value of nUnits.
646    pub fn search_range(&self) -> u16 {
647        let range = self.shape.search_range_byte_range();
648        self.data.read_at(range.start).unwrap()
649    }
650
651    /// The log base 2 of the largest power of 2 less than or equal to the value of nUnits.
652    pub fn entry_selector(&self) -> u16 {
653        let range = self.shape.entry_selector_byte_range();
654        self.data.read_at(range.start).unwrap()
655    }
656
657    /// The value of unitSize times the difference of the value of nUnits minus the largest power of 2 less than or equal to the value of nUnits.
658    pub fn range_shift(&self) -> u16 {
659        let range = self.shape.range_shift_byte_range();
660        self.data.read_at(range.start).unwrap()
661    }
662
663    /// Values, indexed by glyph index.
664    pub fn entries_data(&self) -> &'a [u8] {
665        let range = self.shape.entries_data_byte_range();
666        self.data.read_array(range).unwrap()
667    }
668}
669
670#[cfg(feature = "experimental_traverse")]
671impl<'a> SomeTable<'a> for Lookup6<'a> {
672    fn type_name(&self) -> &str {
673        "Lookup6"
674    }
675    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
676        match idx {
677            0usize => Some(Field::new("format", self.format())),
678            1usize => Some(Field::new("unit_size", self.unit_size())),
679            2usize => Some(Field::new("n_units", self.n_units())),
680            3usize => Some(Field::new("search_range", self.search_range())),
681            4usize => Some(Field::new("entry_selector", self.entry_selector())),
682            5usize => Some(Field::new("range_shift", self.range_shift())),
683            6usize => Some(Field::new("entries_data", self.entries_data())),
684            _ => None,
685        }
686    }
687}
688
689#[cfg(feature = "experimental_traverse")]
690#[allow(clippy::needless_lifetimes)]
691impl<'a> std::fmt::Debug for Lookup6<'a> {
692    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
693        (self as &dyn SomeTable<'a>).fmt(f)
694    }
695}
696
697impl Format<u16> for Lookup8Marker {
698    const FORMAT: u16 = 8;
699}
700
701/// Trimmed array format. The lookup data is a simple trimmed array
702/// indexed by glyph index.
703#[derive(Debug, Clone, Copy)]
704#[doc(hidden)]
705pub struct Lookup8Marker {
706    value_array_byte_len: usize,
707}
708
709impl Lookup8Marker {
710    pub fn format_byte_range(&self) -> Range<usize> {
711        let start = 0;
712        start..start + u16::RAW_BYTE_LEN
713    }
714
715    pub fn first_glyph_byte_range(&self) -> Range<usize> {
716        let start = self.format_byte_range().end;
717        start..start + u16::RAW_BYTE_LEN
718    }
719
720    pub fn glyph_count_byte_range(&self) -> Range<usize> {
721        let start = self.first_glyph_byte_range().end;
722        start..start + u16::RAW_BYTE_LEN
723    }
724
725    pub fn value_array_byte_range(&self) -> Range<usize> {
726        let start = self.glyph_count_byte_range().end;
727        start..start + self.value_array_byte_len
728    }
729}
730
731impl MinByteRange for Lookup8Marker {
732    fn min_byte_range(&self) -> Range<usize> {
733        0..self.value_array_byte_range().end
734    }
735}
736
737impl<'a> FontRead<'a> for Lookup8<'a> {
738    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
739        let mut cursor = data.cursor();
740        cursor.advance::<u16>();
741        cursor.advance::<u16>();
742        cursor.advance::<u16>();
743        let value_array_byte_len = cursor.remaining_bytes() / u16::RAW_BYTE_LEN * u16::RAW_BYTE_LEN;
744        cursor.advance_by(value_array_byte_len);
745        cursor.finish(Lookup8Marker {
746            value_array_byte_len,
747        })
748    }
749}
750
751/// Trimmed array format. The lookup data is a simple trimmed array
752/// indexed by glyph index.
753pub type Lookup8<'a> = TableRef<'a, Lookup8Marker>;
754
755#[allow(clippy::needless_lifetimes)]
756impl<'a> Lookup8<'a> {
757    /// Format number is set to 8.
758    pub fn format(&self) -> u16 {
759        let range = self.shape.format_byte_range();
760        self.data.read_at(range.start).unwrap()
761    }
762
763    /// First glyph index included in the trimmed array.
764    pub fn first_glyph(&self) -> u16 {
765        let range = self.shape.first_glyph_byte_range();
766        self.data.read_at(range.start).unwrap()
767    }
768
769    /// Total number of glyphs (equivalent to the last glyph minus the value
770    /// of firstGlyph plus 1).
771    pub fn glyph_count(&self) -> u16 {
772        let range = self.shape.glyph_count_byte_range();
773        self.data.read_at(range.start).unwrap()
774    }
775
776    /// The lookup values (indexed by the glyph index minus the value of
777    /// firstGlyph). Entries in the value array must be two bytes.
778    pub fn value_array(&self) -> &'a [BigEndian<u16>] {
779        let range = self.shape.value_array_byte_range();
780        self.data.read_array(range).unwrap()
781    }
782}
783
784#[cfg(feature = "experimental_traverse")]
785impl<'a> SomeTable<'a> for Lookup8<'a> {
786    fn type_name(&self) -> &str {
787        "Lookup8"
788    }
789    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
790        match idx {
791            0usize => Some(Field::new("format", self.format())),
792            1usize => Some(Field::new("first_glyph", self.first_glyph())),
793            2usize => Some(Field::new("glyph_count", self.glyph_count())),
794            3usize => Some(Field::new("value_array", self.value_array())),
795            _ => None,
796        }
797    }
798}
799
800#[cfg(feature = "experimental_traverse")]
801#[allow(clippy::needless_lifetimes)]
802impl<'a> std::fmt::Debug for Lookup8<'a> {
803    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
804        (self as &dyn SomeTable<'a>).fmt(f)
805    }
806}
807
808impl Format<u16> for Lookup10Marker {
809    const FORMAT: u16 = 10;
810}
811
812/// Trimmed array format. The lookup data is a simple trimmed array
813/// indexed by glyph index.
814#[derive(Debug, Clone, Copy)]
815#[doc(hidden)]
816pub struct Lookup10Marker {
817    values_data_byte_len: usize,
818}
819
820impl Lookup10Marker {
821    pub fn format_byte_range(&self) -> Range<usize> {
822        let start = 0;
823        start..start + u16::RAW_BYTE_LEN
824    }
825
826    pub fn unit_size_byte_range(&self) -> Range<usize> {
827        let start = self.format_byte_range().end;
828        start..start + u16::RAW_BYTE_LEN
829    }
830
831    pub fn first_glyph_byte_range(&self) -> Range<usize> {
832        let start = self.unit_size_byte_range().end;
833        start..start + u16::RAW_BYTE_LEN
834    }
835
836    pub fn glyph_count_byte_range(&self) -> Range<usize> {
837        let start = self.first_glyph_byte_range().end;
838        start..start + u16::RAW_BYTE_LEN
839    }
840
841    pub fn values_data_byte_range(&self) -> Range<usize> {
842        let start = self.glyph_count_byte_range().end;
843        start..start + self.values_data_byte_len
844    }
845}
846
847impl MinByteRange for Lookup10Marker {
848    fn min_byte_range(&self) -> Range<usize> {
849        0..self.values_data_byte_range().end
850    }
851}
852
853impl<'a> FontRead<'a> for Lookup10<'a> {
854    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
855        let mut cursor = data.cursor();
856        cursor.advance::<u16>();
857        cursor.advance::<u16>();
858        cursor.advance::<u16>();
859        cursor.advance::<u16>();
860        let values_data_byte_len = cursor.remaining_bytes() / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN;
861        cursor.advance_by(values_data_byte_len);
862        cursor.finish(Lookup10Marker {
863            values_data_byte_len,
864        })
865    }
866}
867
868/// Trimmed array format. The lookup data is a simple trimmed array
869/// indexed by glyph index.
870pub type Lookup10<'a> = TableRef<'a, Lookup10Marker>;
871
872#[allow(clippy::needless_lifetimes)]
873impl<'a> Lookup10<'a> {
874    /// Format number is set to 10.
875    pub fn format(&self) -> u16 {
876        let range = self.shape.format_byte_range();
877        self.data.read_at(range.start).unwrap()
878    }
879
880    /// Size of a lookup unit for this lookup table in bytes. Allowed values
881    /// are 1, 2, 4, and 8.
882    pub fn unit_size(&self) -> u16 {
883        let range = self.shape.unit_size_byte_range();
884        self.data.read_at(range.start).unwrap()
885    }
886
887    /// First glyph index included in the trimmed array.
888    pub fn first_glyph(&self) -> u16 {
889        let range = self.shape.first_glyph_byte_range();
890        self.data.read_at(range.start).unwrap()
891    }
892
893    /// Total number of glyphs (equivalent to the last glyph minus the value
894    /// of firstGlyph plus 1).
895    pub fn glyph_count(&self) -> u16 {
896        let range = self.shape.glyph_count_byte_range();
897        self.data.read_at(range.start).unwrap()
898    }
899
900    /// The lookup values (indexed by the glyph index minus the value of
901    /// firstGlyph).
902    pub fn values_data(&self) -> &'a [u8] {
903        let range = self.shape.values_data_byte_range();
904        self.data.read_array(range).unwrap()
905    }
906}
907
908#[cfg(feature = "experimental_traverse")]
909impl<'a> SomeTable<'a> for Lookup10<'a> {
910    fn type_name(&self) -> &str {
911        "Lookup10"
912    }
913    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
914        match idx {
915            0usize => Some(Field::new("format", self.format())),
916            1usize => Some(Field::new("unit_size", self.unit_size())),
917            2usize => Some(Field::new("first_glyph", self.first_glyph())),
918            3usize => Some(Field::new("glyph_count", self.glyph_count())),
919            4usize => Some(Field::new("values_data", self.values_data())),
920            _ => None,
921        }
922    }
923}
924
925#[cfg(feature = "experimental_traverse")]
926#[allow(clippy::needless_lifetimes)]
927impl<'a> std::fmt::Debug for Lookup10<'a> {
928    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
929        (self as &dyn SomeTable<'a>).fmt(f)
930    }
931}
932
933/// Header for a state table.
934#[derive(Debug, Clone, Copy)]
935#[doc(hidden)]
936pub struct StateHeaderMarker {}
937
938impl StateHeaderMarker {
939    pub fn state_size_byte_range(&self) -> Range<usize> {
940        let start = 0;
941        start..start + u16::RAW_BYTE_LEN
942    }
943
944    pub fn class_table_offset_byte_range(&self) -> Range<usize> {
945        let start = self.state_size_byte_range().end;
946        start..start + Offset16::RAW_BYTE_LEN
947    }
948
949    pub fn state_array_offset_byte_range(&self) -> Range<usize> {
950        let start = self.class_table_offset_byte_range().end;
951        start..start + Offset16::RAW_BYTE_LEN
952    }
953
954    pub fn entry_table_offset_byte_range(&self) -> Range<usize> {
955        let start = self.state_array_offset_byte_range().end;
956        start..start + Offset16::RAW_BYTE_LEN
957    }
958}
959
960impl MinByteRange for StateHeaderMarker {
961    fn min_byte_range(&self) -> Range<usize> {
962        0..self.entry_table_offset_byte_range().end
963    }
964}
965
966impl<'a> FontRead<'a> for StateHeader<'a> {
967    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
968        let mut cursor = data.cursor();
969        cursor.advance::<u16>();
970        cursor.advance::<Offset16>();
971        cursor.advance::<Offset16>();
972        cursor.advance::<Offset16>();
973        cursor.finish(StateHeaderMarker {})
974    }
975}
976
977/// Header for a state table.
978pub type StateHeader<'a> = TableRef<'a, StateHeaderMarker>;
979
980#[allow(clippy::needless_lifetimes)]
981impl<'a> StateHeader<'a> {
982    /// Size of a state, in bytes. The size is limited to 8 bits, although the
983    /// field is 16 bits for alignment.
984    pub fn state_size(&self) -> u16 {
985        let range = self.shape.state_size_byte_range();
986        self.data.read_at(range.start).unwrap()
987    }
988
989    /// Byte offset from the beginning of the state table to the class subtable.
990    pub fn class_table_offset(&self) -> Offset16 {
991        let range = self.shape.class_table_offset_byte_range();
992        self.data.read_at(range.start).unwrap()
993    }
994
995    /// Attempt to resolve [`class_table_offset`][Self::class_table_offset].
996    pub fn class_table(&self) -> Result<ClassSubtable<'a>, ReadError> {
997        let data = self.data;
998        self.class_table_offset().resolve(data)
999    }
1000
1001    /// Byte offset from the beginning of the state table to the state array.
1002    pub fn state_array_offset(&self) -> Offset16 {
1003        let range = self.shape.state_array_offset_byte_range();
1004        self.data.read_at(range.start).unwrap()
1005    }
1006
1007    /// Attempt to resolve [`state_array_offset`][Self::state_array_offset].
1008    pub fn state_array(&self) -> Result<RawBytes<'a>, ReadError> {
1009        let data = self.data;
1010        self.state_array_offset().resolve(data)
1011    }
1012
1013    /// Byte offset from the beginning of the state table to the entry subtable.
1014    pub fn entry_table_offset(&self) -> Offset16 {
1015        let range = self.shape.entry_table_offset_byte_range();
1016        self.data.read_at(range.start).unwrap()
1017    }
1018
1019    /// Attempt to resolve [`entry_table_offset`][Self::entry_table_offset].
1020    pub fn entry_table(&self) -> Result<RawBytes<'a>, ReadError> {
1021        let data = self.data;
1022        self.entry_table_offset().resolve(data)
1023    }
1024}
1025
1026#[cfg(feature = "experimental_traverse")]
1027impl<'a> SomeTable<'a> for StateHeader<'a> {
1028    fn type_name(&self) -> &str {
1029        "StateHeader"
1030    }
1031    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1032        match idx {
1033            0usize => Some(Field::new("state_size", self.state_size())),
1034            1usize => Some(Field::new(
1035                "class_table_offset",
1036                FieldType::offset(self.class_table_offset(), self.class_table()),
1037            )),
1038            2usize => Some(Field::new(
1039                "state_array_offset",
1040                FieldType::offset(self.state_array_offset(), self.state_array()),
1041            )),
1042            3usize => Some(Field::new(
1043                "entry_table_offset",
1044                FieldType::offset(self.entry_table_offset(), self.entry_table()),
1045            )),
1046            _ => None,
1047        }
1048    }
1049}
1050
1051#[cfg(feature = "experimental_traverse")]
1052#[allow(clippy::needless_lifetimes)]
1053impl<'a> std::fmt::Debug for StateHeader<'a> {
1054    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1055        (self as &dyn SomeTable<'a>).fmt(f)
1056    }
1057}
1058
1059/// Maps the glyph indexes of your font into classes.
1060#[derive(Debug, Clone, Copy)]
1061#[doc(hidden)]
1062pub struct ClassSubtableMarker {
1063    class_array_byte_len: usize,
1064}
1065
1066impl ClassSubtableMarker {
1067    pub fn first_glyph_byte_range(&self) -> Range<usize> {
1068        let start = 0;
1069        start..start + u16::RAW_BYTE_LEN
1070    }
1071
1072    pub fn n_glyphs_byte_range(&self) -> Range<usize> {
1073        let start = self.first_glyph_byte_range().end;
1074        start..start + u16::RAW_BYTE_LEN
1075    }
1076
1077    pub fn class_array_byte_range(&self) -> Range<usize> {
1078        let start = self.n_glyphs_byte_range().end;
1079        start..start + self.class_array_byte_len
1080    }
1081}
1082
1083impl MinByteRange for ClassSubtableMarker {
1084    fn min_byte_range(&self) -> Range<usize> {
1085        0..self.class_array_byte_range().end
1086    }
1087}
1088
1089impl<'a> FontRead<'a> for ClassSubtable<'a> {
1090    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1091        let mut cursor = data.cursor();
1092        cursor.advance::<u16>();
1093        let n_glyphs: u16 = cursor.read()?;
1094        let class_array_byte_len = (n_glyphs as usize)
1095            .checked_mul(u8::RAW_BYTE_LEN)
1096            .ok_or(ReadError::OutOfBounds)?;
1097        cursor.advance_by(class_array_byte_len);
1098        cursor.finish(ClassSubtableMarker {
1099            class_array_byte_len,
1100        })
1101    }
1102}
1103
1104/// Maps the glyph indexes of your font into classes.
1105pub type ClassSubtable<'a> = TableRef<'a, ClassSubtableMarker>;
1106
1107#[allow(clippy::needless_lifetimes)]
1108impl<'a> ClassSubtable<'a> {
1109    /// Glyph index of the first glyph in the class table.
1110    pub fn first_glyph(&self) -> u16 {
1111        let range = self.shape.first_glyph_byte_range();
1112        self.data.read_at(range.start).unwrap()
1113    }
1114
1115    /// Number of glyphs in class table.
1116    pub fn n_glyphs(&self) -> u16 {
1117        let range = self.shape.n_glyphs_byte_range();
1118        self.data.read_at(range.start).unwrap()
1119    }
1120
1121    /// The class codes (indexed by glyph index minus firstGlyph). Class codes
1122    /// range from 0 to the value of stateSize minus 1.
1123    pub fn class_array(&self) -> &'a [u8] {
1124        let range = self.shape.class_array_byte_range();
1125        self.data.read_array(range).unwrap()
1126    }
1127}
1128
1129#[cfg(feature = "experimental_traverse")]
1130impl<'a> SomeTable<'a> for ClassSubtable<'a> {
1131    fn type_name(&self) -> &str {
1132        "ClassSubtable"
1133    }
1134    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1135        match idx {
1136            0usize => Some(Field::new("first_glyph", self.first_glyph())),
1137            1usize => Some(Field::new("n_glyphs", self.n_glyphs())),
1138            2usize => Some(Field::new("class_array", self.class_array())),
1139            _ => None,
1140        }
1141    }
1142}
1143
1144#[cfg(feature = "experimental_traverse")]
1145#[allow(clippy::needless_lifetimes)]
1146impl<'a> std::fmt::Debug for ClassSubtable<'a> {
1147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1148        (self as &dyn SomeTable<'a>).fmt(f)
1149    }
1150}
1151
1152/// Used for the `state_array` and `entry_table` fields in [`StateHeader`].
1153#[derive(Debug, Clone, Copy)]
1154#[doc(hidden)]
1155pub struct RawBytesMarker {
1156    data_byte_len: usize,
1157}
1158
1159impl RawBytesMarker {
1160    pub fn data_byte_range(&self) -> Range<usize> {
1161        let start = 0;
1162        start..start + self.data_byte_len
1163    }
1164}
1165
1166impl MinByteRange for RawBytesMarker {
1167    fn min_byte_range(&self) -> Range<usize> {
1168        0..self.data_byte_range().end
1169    }
1170}
1171
1172impl<'a> FontRead<'a> for RawBytes<'a> {
1173    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1174        let mut cursor = data.cursor();
1175        let data_byte_len = cursor.remaining_bytes() / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN;
1176        cursor.advance_by(data_byte_len);
1177        cursor.finish(RawBytesMarker { data_byte_len })
1178    }
1179}
1180
1181/// Used for the `state_array` and `entry_table` fields in [`StateHeader`].
1182pub type RawBytes<'a> = TableRef<'a, RawBytesMarker>;
1183
1184#[allow(clippy::needless_lifetimes)]
1185impl<'a> RawBytes<'a> {
1186    pub fn data(&self) -> &'a [u8] {
1187        let range = self.shape.data_byte_range();
1188        self.data.read_array(range).unwrap()
1189    }
1190}
1191
1192#[cfg(feature = "experimental_traverse")]
1193impl<'a> SomeTable<'a> for RawBytes<'a> {
1194    fn type_name(&self) -> &str {
1195        "RawBytes"
1196    }
1197    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1198        match idx {
1199            0usize => Some(Field::new("data", self.data())),
1200            _ => None,
1201        }
1202    }
1203}
1204
1205#[cfg(feature = "experimental_traverse")]
1206#[allow(clippy::needless_lifetimes)]
1207impl<'a> std::fmt::Debug for RawBytes<'a> {
1208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1209        (self as &dyn SomeTable<'a>).fmt(f)
1210    }
1211}
1212
1213/// Header for an extended state table.
1214#[derive(Debug, Clone, Copy)]
1215#[doc(hidden)]
1216pub struct StxHeaderMarker {}
1217
1218impl StxHeaderMarker {
1219    pub fn n_classes_byte_range(&self) -> Range<usize> {
1220        let start = 0;
1221        start..start + u32::RAW_BYTE_LEN
1222    }
1223
1224    pub fn class_table_offset_byte_range(&self) -> Range<usize> {
1225        let start = self.n_classes_byte_range().end;
1226        start..start + Offset32::RAW_BYTE_LEN
1227    }
1228
1229    pub fn state_array_offset_byte_range(&self) -> Range<usize> {
1230        let start = self.class_table_offset_byte_range().end;
1231        start..start + Offset32::RAW_BYTE_LEN
1232    }
1233
1234    pub fn entry_table_offset_byte_range(&self) -> Range<usize> {
1235        let start = self.state_array_offset_byte_range().end;
1236        start..start + Offset32::RAW_BYTE_LEN
1237    }
1238}
1239
1240impl MinByteRange for StxHeaderMarker {
1241    fn min_byte_range(&self) -> Range<usize> {
1242        0..self.entry_table_offset_byte_range().end
1243    }
1244}
1245
1246impl<'a> FontRead<'a> for StxHeader<'a> {
1247    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1248        let mut cursor = data.cursor();
1249        cursor.advance::<u32>();
1250        cursor.advance::<Offset32>();
1251        cursor.advance::<Offset32>();
1252        cursor.advance::<Offset32>();
1253        cursor.finish(StxHeaderMarker {})
1254    }
1255}
1256
1257/// Header for an extended state table.
1258pub type StxHeader<'a> = TableRef<'a, StxHeaderMarker>;
1259
1260#[allow(clippy::needless_lifetimes)]
1261impl<'a> StxHeader<'a> {
1262    /// Number of classes, which is the number of 16-bit entry indices in a single line in the state array.
1263    pub fn n_classes(&self) -> u32 {
1264        let range = self.shape.n_classes_byte_range();
1265        self.data.read_at(range.start).unwrap()
1266    }
1267
1268    /// Byte offset from the beginning of the state table to the class subtable.
1269    pub fn class_table_offset(&self) -> Offset32 {
1270        let range = self.shape.class_table_offset_byte_range();
1271        self.data.read_at(range.start).unwrap()
1272    }
1273
1274    /// Attempt to resolve [`class_table_offset`][Self::class_table_offset].
1275    pub fn class_table(&self) -> Result<LookupU16<'a>, ReadError> {
1276        let data = self.data;
1277        self.class_table_offset().resolve(data)
1278    }
1279
1280    /// Byte offset from the beginning of the state table to the state array.
1281    pub fn state_array_offset(&self) -> Offset32 {
1282        let range = self.shape.state_array_offset_byte_range();
1283        self.data.read_at(range.start).unwrap()
1284    }
1285
1286    /// Attempt to resolve [`state_array_offset`][Self::state_array_offset].
1287    pub fn state_array(&self) -> Result<RawWords<'a>, ReadError> {
1288        let data = self.data;
1289        self.state_array_offset().resolve(data)
1290    }
1291
1292    /// Byte offset from the beginning of the state table to the entry subtable.
1293    pub fn entry_table_offset(&self) -> Offset32 {
1294        let range = self.shape.entry_table_offset_byte_range();
1295        self.data.read_at(range.start).unwrap()
1296    }
1297
1298    /// Attempt to resolve [`entry_table_offset`][Self::entry_table_offset].
1299    pub fn entry_table(&self) -> Result<RawBytes<'a>, ReadError> {
1300        let data = self.data;
1301        self.entry_table_offset().resolve(data)
1302    }
1303}
1304
1305#[cfg(feature = "experimental_traverse")]
1306impl<'a> SomeTable<'a> for StxHeader<'a> {
1307    fn type_name(&self) -> &str {
1308        "StxHeader"
1309    }
1310    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1311        match idx {
1312            0usize => Some(Field::new("n_classes", self.n_classes())),
1313            1usize => Some(Field::new(
1314                "class_table_offset",
1315                FieldType::offset(self.class_table_offset(), self.class_table()),
1316            )),
1317            2usize => Some(Field::new(
1318                "state_array_offset",
1319                FieldType::offset(self.state_array_offset(), self.state_array()),
1320            )),
1321            3usize => Some(Field::new(
1322                "entry_table_offset",
1323                FieldType::offset(self.entry_table_offset(), self.entry_table()),
1324            )),
1325            _ => None,
1326        }
1327    }
1328}
1329
1330#[cfg(feature = "experimental_traverse")]
1331#[allow(clippy::needless_lifetimes)]
1332impl<'a> std::fmt::Debug for StxHeader<'a> {
1333    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1334        (self as &dyn SomeTable<'a>).fmt(f)
1335    }
1336}
1337
1338/// Used for the `state_array` in [`StxHeader`].
1339#[derive(Debug, Clone, Copy)]
1340#[doc(hidden)]
1341pub struct RawWordsMarker {
1342    data_byte_len: usize,
1343}
1344
1345impl RawWordsMarker {
1346    pub fn data_byte_range(&self) -> Range<usize> {
1347        let start = 0;
1348        start..start + self.data_byte_len
1349    }
1350}
1351
1352impl MinByteRange for RawWordsMarker {
1353    fn min_byte_range(&self) -> Range<usize> {
1354        0..self.data_byte_range().end
1355    }
1356}
1357
1358impl<'a> FontRead<'a> for RawWords<'a> {
1359    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1360        let mut cursor = data.cursor();
1361        let data_byte_len = cursor.remaining_bytes() / u16::RAW_BYTE_LEN * u16::RAW_BYTE_LEN;
1362        cursor.advance_by(data_byte_len);
1363        cursor.finish(RawWordsMarker { data_byte_len })
1364    }
1365}
1366
1367/// Used for the `state_array` in [`StxHeader`].
1368pub type RawWords<'a> = TableRef<'a, RawWordsMarker>;
1369
1370#[allow(clippy::needless_lifetimes)]
1371impl<'a> RawWords<'a> {
1372    pub fn data(&self) -> &'a [BigEndian<u16>] {
1373        let range = self.shape.data_byte_range();
1374        self.data.read_array(range).unwrap()
1375    }
1376}
1377
1378#[cfg(feature = "experimental_traverse")]
1379impl<'a> SomeTable<'a> for RawWords<'a> {
1380    fn type_name(&self) -> &str {
1381        "RawWords"
1382    }
1383    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1384        match idx {
1385            0usize => Some(Field::new("data", self.data())),
1386            _ => None,
1387        }
1388    }
1389}
1390
1391#[cfg(feature = "experimental_traverse")]
1392#[allow(clippy::needless_lifetimes)]
1393impl<'a> std::fmt::Debug for RawWords<'a> {
1394    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1395        (self as &dyn SomeTable<'a>).fmt(f)
1396    }
1397}