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