Skip to main content

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