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