Skip to main content

read_fonts/generated/
generated_aat.rs

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