Skip to main content

read_fonts/generated/
generated_layout.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
8impl<'a> MinByteRange<'a> for ScriptList<'a> {
9    fn min_byte_range(&self) -> Range<usize> {
10        0..self.script_records_byte_range().end
11    }
12    fn min_table_bytes(&self) -> &'a [u8] {
13        let range = self.min_byte_range();
14        self.offset_data().as_bytes().get(range).unwrap_or_default()
15    }
16}
17
18impl ReadArgs for ScriptList<'_> {
19    type Args = ();
20}
21
22impl<'a> FontRead<'a> for ScriptList<'a> {
23    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
24        #[allow(clippy::absurd_extreme_comparisons)]
25        if data.len() < Self::MIN_SIZE {
26            return Err(ReadError::OutOfBounds);
27        }
28        let Some(data) = TableDataRef::new(data.as_bytes()) else {
29            return Err(ReadError::OutOfBounds);
30        };
31        Ok(Self { data })
32    }
33}
34
35/// The fields at the start of the table, which are read directly.
36#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
37#[repr(C, packed)]
38#[doc(hidden)]
39pub struct ScriptListFixedHeader {
40    pub script_count: BigEndian<u16>,
41}
42
43#[allow(clippy::absurd_extreme_comparisons)]
44const _: () = assert!(std::mem::size_of::<ScriptListFixedHeader>() <= ScriptList::MIN_SIZE);
45
46/// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
47#[derive(Clone)]
48pub struct ScriptList<'a> {
49    data: TableDataRef<'a, ScriptListFixedHeader>,
50}
51
52#[allow(clippy::needless_lifetimes)]
53impl<'a> ScriptList<'a> {
54    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
55    basic_table_impls!(impl_the_methods);
56
57    /// Number of ScriptRecords
58    pub fn script_count(&self) -> u16 {
59        self.data.header().script_count.get()
60    }
61
62    /// Array of ScriptRecords, listed alphabetically by script tag
63    pub fn script_records(&self) -> &'a [ScriptRecord] {
64        let range = self.script_records_byte_range();
65        self.offset_data()
66            .read_array(range)
67            .ok()
68            .unwrap_or_default()
69    }
70
71    pub fn script_count_byte_range(&self) -> Range<usize> {
72        let start = 0;
73        let end = start + u16::RAW_BYTE_LEN;
74        start..end
75    }
76
77    pub fn script_records_byte_range(&self) -> Range<usize> {
78        let script_count = self.script_count();
79        let start = self.script_count_byte_range().end;
80        let end =
81            start + (transforms::to_usize(script_count)).saturating_mul(ScriptRecord::RAW_BYTE_LEN);
82        start..end
83    }
84}
85
86const _: () = assert!(FontData::default_data_long_enough(ScriptList::MIN_SIZE));
87
88impl Default for ScriptList<'_> {
89    fn default() -> Self {
90        Self {
91            data: TableDataRef::new(FontData::default_table_data().as_bytes())
92                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
93        }
94    }
95}
96
97/// [Script Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
98#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
99#[repr(C)]
100#[repr(packed)]
101pub struct ScriptRecord {
102    /// 4-byte script tag identifier
103    pub script_tag: BigEndian<Tag>,
104    /// Offset to Script table, from beginning of ScriptList
105    pub script_offset: BigEndian<Offset16>,
106}
107
108impl ScriptRecord {
109    /// 4-byte script tag identifier
110    pub fn script_tag(&self) -> Tag {
111        self.script_tag.get()
112    }
113
114    /// Offset to Script table, from beginning of ScriptList
115    pub fn script_offset(&self) -> Offset16 {
116        self.script_offset.get()
117    }
118
119    /// Offset to Script table, from beginning of ScriptList
120    ///
121    /// The `data` argument should be retrieved from the parent table
122    /// By calling its `offset_data` method.
123    pub fn script<'a>(&self, data: FontData<'a>) -> Result<Script<'a>, ReadError> {
124        self.script_offset().resolve(data)
125    }
126}
127
128impl FixedSize for ScriptRecord {
129    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
130}
131
132impl<'a> MinByteRange<'a> for Script<'a> {
133    fn min_byte_range(&self) -> Range<usize> {
134        0..self.lang_sys_records_byte_range().end
135    }
136    fn min_table_bytes(&self) -> &'a [u8] {
137        let range = self.min_byte_range();
138        self.offset_data().as_bytes().get(range).unwrap_or_default()
139    }
140}
141
142impl ReadArgs for Script<'_> {
143    type Args = ();
144}
145
146impl<'a> FontRead<'a> for Script<'a> {
147    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
148        #[allow(clippy::absurd_extreme_comparisons)]
149        if data.len() < Self::MIN_SIZE {
150            return Err(ReadError::OutOfBounds);
151        }
152        let Some(data) = TableDataRef::new(data.as_bytes()) else {
153            return Err(ReadError::OutOfBounds);
154        };
155        Ok(Self { data })
156    }
157}
158
159/// The fields at the start of the table, which are read directly.
160#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
161#[repr(C, packed)]
162#[doc(hidden)]
163pub struct ScriptFixedHeader {
164    pub default_lang_sys_offset: BigEndian<Nullable<Offset16>>,
165    pub lang_sys_count: BigEndian<u16>,
166}
167
168#[allow(clippy::absurd_extreme_comparisons)]
169const _: () = assert!(std::mem::size_of::<ScriptFixedHeader>() <= Script::MIN_SIZE);
170
171/// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record)
172#[derive(Clone)]
173pub struct Script<'a> {
174    data: TableDataRef<'a, ScriptFixedHeader>,
175}
176
177#[allow(clippy::needless_lifetimes)]
178impl<'a> Script<'a> {
179    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
180    basic_table_impls!(impl_the_methods);
181
182    /// Offset to default LangSys table, from beginning of Script table
183    /// — may be NULL
184    pub fn default_lang_sys_offset(&self) -> Nullable<Offset16> {
185        self.data.header().default_lang_sys_offset.get()
186    }
187
188    /// Attempt to resolve [`default_lang_sys_offset`][Self::default_lang_sys_offset].
189    pub fn default_lang_sys(&self) -> Option<Result<LangSys<'a>, ReadError>> {
190        let data = self.offset_data();
191        self.default_lang_sys_offset().resolve(data)
192    }
193
194    /// Number of LangSysRecords for this script — excluding the
195    /// default LangSys
196    pub fn lang_sys_count(&self) -> u16 {
197        self.data.header().lang_sys_count.get()
198    }
199
200    /// Array of LangSysRecords, listed alphabetically by LangSys tag
201    pub fn lang_sys_records(&self) -> &'a [LangSysRecord] {
202        let range = self.lang_sys_records_byte_range();
203        self.offset_data()
204            .read_array(range)
205            .ok()
206            .unwrap_or_default()
207    }
208
209    pub fn default_lang_sys_offset_byte_range(&self) -> Range<usize> {
210        let start = 0;
211        let end = start + Offset16::RAW_BYTE_LEN;
212        start..end
213    }
214
215    pub fn lang_sys_count_byte_range(&self) -> Range<usize> {
216        let start = self.default_lang_sys_offset_byte_range().end;
217        let end = start + u16::RAW_BYTE_LEN;
218        start..end
219    }
220
221    pub fn lang_sys_records_byte_range(&self) -> Range<usize> {
222        let lang_sys_count = self.lang_sys_count();
223        let start = self.lang_sys_count_byte_range().end;
224        let end = start
225            + (transforms::to_usize(lang_sys_count)).saturating_mul(LangSysRecord::RAW_BYTE_LEN);
226        start..end
227    }
228}
229
230const _: () = assert!(FontData::default_data_long_enough(Script::MIN_SIZE));
231
232impl Default for Script<'_> {
233    fn default() -> Self {
234        Self {
235            data: TableDataRef::new(FontData::default_table_data().as_bytes())
236                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
237        }
238    }
239}
240
241#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
242#[repr(C)]
243#[repr(packed)]
244pub struct LangSysRecord {
245    /// 4-byte LangSysTag identifier
246    pub lang_sys_tag: BigEndian<Tag>,
247    /// Offset to LangSys table, from beginning of Script table
248    pub lang_sys_offset: BigEndian<Offset16>,
249}
250
251impl LangSysRecord {
252    /// 4-byte LangSysTag identifier
253    pub fn lang_sys_tag(&self) -> Tag {
254        self.lang_sys_tag.get()
255    }
256
257    /// Offset to LangSys table, from beginning of Script table
258    pub fn lang_sys_offset(&self) -> Offset16 {
259        self.lang_sys_offset.get()
260    }
261
262    /// Offset to LangSys table, from beginning of Script table
263    ///
264    /// The `data` argument should be retrieved from the parent table
265    /// By calling its `offset_data` method.
266    pub fn lang_sys<'a>(&self, data: FontData<'a>) -> Result<LangSys<'a>, ReadError> {
267        self.lang_sys_offset().resolve(data)
268    }
269}
270
271impl FixedSize for LangSysRecord {
272    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
273}
274
275impl<'a> MinByteRange<'a> for LangSys<'a> {
276    fn min_byte_range(&self) -> Range<usize> {
277        0..self.feature_indices_byte_range().end
278    }
279    fn min_table_bytes(&self) -> &'a [u8] {
280        let range = self.min_byte_range();
281        self.offset_data().as_bytes().get(range).unwrap_or_default()
282    }
283}
284
285impl ReadArgs for LangSys<'_> {
286    type Args = ();
287}
288
289impl<'a> FontRead<'a> for LangSys<'a> {
290    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
291        #[allow(clippy::absurd_extreme_comparisons)]
292        if data.len() < Self::MIN_SIZE {
293            return Err(ReadError::OutOfBounds);
294        }
295        let Some(data) = TableDataRef::new(data.as_bytes()) else {
296            return Err(ReadError::OutOfBounds);
297        };
298        Ok(Self { data })
299    }
300}
301
302/// The fields at the start of the table, which are read directly.
303#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
304#[repr(C, packed)]
305#[doc(hidden)]
306pub struct LangSysFixedHeader {
307    pub lookup_order_offset: BigEndian<u16>,
308    pub required_feature_index: BigEndian<u16>,
309    pub feature_index_count: BigEndian<u16>,
310}
311
312#[allow(clippy::absurd_extreme_comparisons)]
313const _: () = assert!(std::mem::size_of::<LangSysFixedHeader>() <= LangSys::MIN_SIZE);
314
315/// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table)
316#[derive(Clone)]
317pub struct LangSys<'a> {
318    data: TableDataRef<'a, LangSysFixedHeader>,
319}
320
321#[allow(clippy::needless_lifetimes)]
322impl<'a> LangSys<'a> {
323    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
324    basic_table_impls!(impl_the_methods);
325
326    /// Index of a feature required for this language system; if no
327    /// required features = 0xFFFF
328    pub fn required_feature_index(&self) -> u16 {
329        self.data.header().required_feature_index.get()
330    }
331
332    /// Number of feature index values for this language system —
333    /// excludes the required feature
334    pub fn feature_index_count(&self) -> u16 {
335        self.data.header().feature_index_count.get()
336    }
337
338    /// Array of indices into the FeatureList, in arbitrary order
339    pub fn feature_indices(&self) -> &'a [BigEndian<u16>] {
340        let range = self.feature_indices_byte_range();
341        self.offset_data()
342            .read_array(range)
343            .ok()
344            .unwrap_or_default()
345    }
346
347    pub fn lookup_order_offset_byte_range(&self) -> Range<usize> {
348        let start = 0;
349        let end = start + u16::RAW_BYTE_LEN;
350        start..end
351    }
352
353    pub fn required_feature_index_byte_range(&self) -> Range<usize> {
354        let start = self.lookup_order_offset_byte_range().end;
355        let end = start + u16::RAW_BYTE_LEN;
356        start..end
357    }
358
359    pub fn feature_index_count_byte_range(&self) -> Range<usize> {
360        let start = self.required_feature_index_byte_range().end;
361        let end = start + u16::RAW_BYTE_LEN;
362        start..end
363    }
364
365    pub fn feature_indices_byte_range(&self) -> Range<usize> {
366        let feature_index_count = self.feature_index_count();
367        let start = self.feature_index_count_byte_range().end;
368        let end =
369            start + (transforms::to_usize(feature_index_count)).saturating_mul(u16::RAW_BYTE_LEN);
370        start..end
371    }
372}
373
374const _: () = assert!(FontData::default_data_long_enough(LangSys::MIN_SIZE));
375
376impl Default for LangSys<'_> {
377    fn default() -> Self {
378        Self {
379            data: TableDataRef::new(FontData::default_table_data().as_bytes())
380                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
381        }
382    }
383}
384
385impl<'a> MinByteRange<'a> for FeatureList<'a> {
386    fn min_byte_range(&self) -> Range<usize> {
387        0..self.feature_records_byte_range().end
388    }
389    fn min_table_bytes(&self) -> &'a [u8] {
390        let range = self.min_byte_range();
391        self.offset_data().as_bytes().get(range).unwrap_or_default()
392    }
393}
394
395impl ReadArgs for FeatureList<'_> {
396    type Args = ();
397}
398
399impl<'a> FontRead<'a> for FeatureList<'a> {
400    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
401        #[allow(clippy::absurd_extreme_comparisons)]
402        if data.len() < Self::MIN_SIZE {
403            return Err(ReadError::OutOfBounds);
404        }
405        let Some(data) = TableDataRef::new(data.as_bytes()) else {
406            return Err(ReadError::OutOfBounds);
407        };
408        Ok(Self { data })
409    }
410}
411
412/// The fields at the start of the table, which are read directly.
413#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
414#[repr(C, packed)]
415#[doc(hidden)]
416pub struct FeatureListFixedHeader {
417    pub feature_count: BigEndian<u16>,
418}
419
420#[allow(clippy::absurd_extreme_comparisons)]
421const _: () = assert!(std::mem::size_of::<FeatureListFixedHeader>() <= FeatureList::MIN_SIZE);
422
423/// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table)
424#[derive(Clone)]
425pub struct FeatureList<'a> {
426    data: TableDataRef<'a, FeatureListFixedHeader>,
427}
428
429#[allow(clippy::needless_lifetimes)]
430impl<'a> FeatureList<'a> {
431    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
432    basic_table_impls!(impl_the_methods);
433
434    /// Number of FeatureRecords in this table
435    pub fn feature_count(&self) -> u16 {
436        self.data.header().feature_count.get()
437    }
438
439    /// Array of FeatureRecords — zero-based (first feature has
440    /// FeatureIndex = 0), listed alphabetically by feature tag
441    pub fn feature_records(&self) -> &'a [FeatureRecord] {
442        let range = self.feature_records_byte_range();
443        self.offset_data()
444            .read_array(range)
445            .ok()
446            .unwrap_or_default()
447    }
448
449    pub fn feature_count_byte_range(&self) -> Range<usize> {
450        let start = 0;
451        let end = start + u16::RAW_BYTE_LEN;
452        start..end
453    }
454
455    pub fn feature_records_byte_range(&self) -> Range<usize> {
456        let feature_count = self.feature_count();
457        let start = self.feature_count_byte_range().end;
458        let end = start
459            + (transforms::to_usize(feature_count)).saturating_mul(FeatureRecord::RAW_BYTE_LEN);
460        start..end
461    }
462}
463
464const _: () = assert!(FontData::default_data_long_enough(FeatureList::MIN_SIZE));
465
466impl Default for FeatureList<'_> {
467    fn default() -> Self {
468        Self {
469            data: TableDataRef::new(FontData::default_table_data().as_bytes())
470                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
471        }
472    }
473}
474
475/// Part of [FeatureList]
476#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
477#[repr(C)]
478#[repr(packed)]
479pub struct FeatureRecord {
480    /// 4-byte feature identification tag
481    pub feature_tag: BigEndian<Tag>,
482    /// Offset to Feature table, from beginning of FeatureList
483    pub feature_offset: BigEndian<Offset16>,
484}
485
486impl FeatureRecord {
487    /// 4-byte feature identification tag
488    pub fn feature_tag(&self) -> Tag {
489        self.feature_tag.get()
490    }
491
492    /// Offset to Feature table, from beginning of FeatureList
493    pub fn feature_offset(&self) -> Offset16 {
494        self.feature_offset.get()
495    }
496
497    /// Offset to Feature table, from beginning of FeatureList
498    ///
499    /// The `data` argument should be retrieved from the parent table
500    /// By calling its `offset_data` method.
501    pub fn feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
502        let args = self.feature_tag();
503        self.feature_offset().resolve_with_args(data, args)
504    }
505}
506
507impl FixedSize for FeatureRecord {
508    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
509}
510
511impl<'a> MinByteRange<'a> for Feature<'a> {
512    fn min_byte_range(&self) -> Range<usize> {
513        0..self.lookup_list_indices_byte_range().end
514    }
515    fn min_table_bytes(&self) -> &'a [u8] {
516        let range = self.min_byte_range();
517        self.offset_data().as_bytes().get(range).unwrap_or_default()
518    }
519}
520
521impl ReadArgs for Feature<'_> {
522    type Args = Tag;
523}
524
525impl<'a> FontRead<'a> for Feature<'a> {
526    fn read_with_args(data: FontData<'a>, args: Tag) -> Result<Self, ReadError> {
527        let feature_tag = args;
528
529        #[allow(clippy::absurd_extreme_comparisons)]
530        if data.len() < Self::MIN_SIZE {
531            return Err(ReadError::OutOfBounds);
532        }
533        let Some(data) = TableDataRef::new(data.as_bytes()) else {
534            return Err(ReadError::OutOfBounds);
535        };
536        Ok(Self { data, feature_tag })
537    }
538}
539
540impl<'a> Feature<'a> {
541    /// A constructor that requires additional arguments.
542    ///
543    /// This type requires some external state in order to be
544    /// parsed.
545    pub fn read(data: FontData<'a>, feature_tag: Tag) -> Result<Self, ReadError> {
546        let args = feature_tag;
547        Self::read_with_args(data, args)
548    }
549}
550
551/// The fields at the start of the table, which are read directly.
552#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
553#[repr(C, packed)]
554#[doc(hidden)]
555pub struct FeatureFixedHeader {
556    pub feature_params_offset: BigEndian<Nullable<Offset16>>,
557    pub lookup_index_count: BigEndian<u16>,
558}
559
560#[allow(clippy::absurd_extreme_comparisons)]
561const _: () = assert!(std::mem::size_of::<FeatureFixedHeader>() <= Feature::MIN_SIZE);
562
563/// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table)
564#[derive(Clone)]
565pub struct Feature<'a> {
566    data: TableDataRef<'a, FeatureFixedHeader>,
567    feature_tag: Tag,
568}
569
570#[allow(clippy::needless_lifetimes)]
571impl<'a> Feature<'a> {
572    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
573    basic_table_impls!(impl_the_methods);
574
575    /// Offset from start of Feature table to FeatureParams table, if defined for the feature and present, else NULL
576    pub fn feature_params_offset(&self) -> Nullable<Offset16> {
577        self.data.header().feature_params_offset.get()
578    }
579
580    /// Attempt to resolve [`feature_params_offset`][Self::feature_params_offset].
581    pub fn feature_params(&self) -> Option<Result<FeatureParams<'a>, ReadError>> {
582        let data = self.offset_data();
583        let args = self.feature_tag();
584        self.feature_params_offset().resolve_with_args(data, args)
585    }
586
587    /// Number of LookupList indices for this feature
588    pub fn lookup_index_count(&self) -> u16 {
589        self.data.header().lookup_index_count.get()
590    }
591
592    /// Array of indices into the LookupList — zero-based (first
593    /// lookup is LookupListIndex = 0)
594    pub fn lookup_list_indices(&self) -> &'a [BigEndian<u16>] {
595        let range = self.lookup_list_indices_byte_range();
596        self.offset_data()
597            .read_array(range)
598            .ok()
599            .unwrap_or_default()
600    }
601
602    pub(crate) fn feature_tag(&self) -> Tag {
603        self.feature_tag
604    }
605
606    pub fn feature_params_offset_byte_range(&self) -> Range<usize> {
607        let start = 0;
608        let end = start + Offset16::RAW_BYTE_LEN;
609        start..end
610    }
611
612    pub fn lookup_index_count_byte_range(&self) -> Range<usize> {
613        let start = self.feature_params_offset_byte_range().end;
614        let end = start + u16::RAW_BYTE_LEN;
615        start..end
616    }
617
618    pub fn lookup_list_indices_byte_range(&self) -> Range<usize> {
619        let lookup_index_count = self.lookup_index_count();
620        let start = self.lookup_index_count_byte_range().end;
621        let end =
622            start + (transforms::to_usize(lookup_index_count)).saturating_mul(u16::RAW_BYTE_LEN);
623        start..end
624    }
625}
626
627const _: () = assert!(FontData::default_data_long_enough(Feature::MIN_SIZE));
628
629impl Default for Feature<'_> {
630    fn default() -> Self {
631        Self {
632            data: TableDataRef::new(FontData::default_table_data().as_bytes())
633                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
634            feature_tag: Default::default(),
635        }
636    }
637}
638
639impl<'a, T> MinByteRange<'a> for LookupList<'a, T> {
640    fn min_byte_range(&self) -> Range<usize> {
641        0..self.lookup_offsets_byte_range().end
642    }
643    fn min_table_bytes(&self) -> &'a [u8] {
644        let range = self.min_byte_range();
645        self.offset_data().as_bytes().get(range).unwrap_or_default()
646    }
647}
648
649impl<T> ReadArgs for LookupList<'_, T> {
650    type Args = ();
651}
652
653impl<'a, T> FontRead<'a> for LookupList<'a, T> {
654    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
655        #[allow(clippy::absurd_extreme_comparisons)]
656        if data.len() < Self::MIN_SIZE {
657            return Err(ReadError::OutOfBounds);
658        }
659        let Some(data) = TableDataRef::new(data.as_bytes()) else {
660            return Err(ReadError::OutOfBounds);
661        };
662        Ok(Self {
663            data,
664            offset_type: std::marker::PhantomData,
665        })
666    }
667}
668
669impl<'a, T> LookupList<'a, T> {
670    #[allow(dead_code)]
671    /// Replace the specific generic type on this implementation with `()`
672    pub(crate) fn of_unit_type(&self) -> LookupList<'a, ()> {
673        LookupList {
674            data: self.data,
675            offset_type: std::marker::PhantomData,
676        }
677    }
678}
679
680/// The fields at the start of the table, which are read directly.
681#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
682#[repr(C, packed)]
683#[doc(hidden)]
684pub struct LookupListFixedHeader {
685    pub lookup_count: BigEndian<u16>,
686}
687
688#[allow(clippy::absurd_extreme_comparisons)]
689const _: () = assert!(std::mem::size_of::<LookupListFixedHeader>() <= LookupList::<()>::MIN_SIZE);
690
691/// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table)
692#[derive(Clone)]
693pub struct LookupList<'a, T = ()> {
694    data: TableDataRef<'a, LookupListFixedHeader>,
695    offset_type: std::marker::PhantomData<*const T>,
696}
697
698#[allow(clippy::needless_lifetimes)]
699impl<'a, T> LookupList<'a, T> {
700    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
701    basic_table_impls!(impl_the_methods);
702
703    /// Number of lookups in this table
704    pub fn lookup_count(&self) -> u16 {
705        self.data.header().lookup_count.get()
706    }
707
708    /// Array of offsets to Lookup tables, from beginning of LookupList
709    /// — zero based (first lookup is Lookup index = 0)
710    pub fn lookup_offsets(&self) -> &'a [BigEndian<Offset16>] {
711        let range = self.lookup_offsets_byte_range();
712        self.offset_data()
713            .read_array(range)
714            .ok()
715            .unwrap_or_default()
716    }
717
718    /// A dynamically resolving wrapper for [`lookup_offsets`][Self::lookup_offsets].
719    pub fn lookups(&self) -> ArrayOfOffsets<'a, T, Offset16>
720    where
721        T: FontRead<'a, Args = ()>,
722    {
723        let data = self.offset_data();
724        let offsets = self.lookup_offsets();
725        ArrayOfOffsets::new(offsets, data, ())
726    }
727
728    pub fn lookup_count_byte_range(&self) -> Range<usize> {
729        let start = 0;
730        let end = start + u16::RAW_BYTE_LEN;
731        start..end
732    }
733
734    pub fn lookup_offsets_byte_range(&self) -> Range<usize> {
735        let lookup_count = self.lookup_count();
736        let start = self.lookup_count_byte_range().end;
737        let end =
738            start + (transforms::to_usize(lookup_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
739        start..end
740    }
741}
742
743const _: () = assert!(FontData::default_data_long_enough(
744    LookupList::<()>::MIN_SIZE
745));
746
747impl<T> Default for LookupList<'_, T> {
748    fn default() -> Self {
749        Self {
750            data: TableDataRef::new(FontData::default_table_data().as_bytes())
751                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
752            offset_type: std::marker::PhantomData,
753        }
754    }
755}
756
757impl Discriminant for Lookup<'_, ()> {
758    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
759        data.read_at(0)
760    }
761}
762
763impl<'a, T> MinByteRange<'a> for Lookup<'a, T> {
764    fn min_byte_range(&self) -> Range<usize> {
765        0..self.subtable_offsets_byte_range().end
766    }
767    fn min_table_bytes(&self) -> &'a [u8] {
768        let range = self.min_byte_range();
769        self.offset_data().as_bytes().get(range).unwrap_or_default()
770    }
771}
772
773impl<T> ReadArgs for Lookup<'_, T> {
774    type Args = ();
775}
776
777impl<'a, T> FontRead<'a> for Lookup<'a, T> {
778    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
779        #[allow(clippy::absurd_extreme_comparisons)]
780        if data.len() < Self::MIN_SIZE {
781            return Err(ReadError::OutOfBounds);
782        }
783        let Some(data) = TableDataRef::new(data.as_bytes()) else {
784            return Err(ReadError::OutOfBounds);
785        };
786        Ok(Self {
787            data,
788            offset_type: std::marker::PhantomData,
789        })
790    }
791}
792
793impl<'a, T> Lookup<'a, T> {
794    #[allow(dead_code)]
795    /// Replace the specific generic type on this implementation with `()`
796    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
797        Lookup {
798            data: self.data,
799            offset_type: std::marker::PhantomData,
800        }
801    }
802}
803
804/// The fields at the start of the table, which are read directly.
805#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
806#[repr(C, packed)]
807#[doc(hidden)]
808pub struct LookupFixedHeader {
809    pub lookup_type: BigEndian<u16>,
810    pub lookup_flag: BigEndian<LookupFlag>,
811    pub sub_table_count: BigEndian<u16>,
812}
813
814#[allow(clippy::absurd_extreme_comparisons)]
815const _: () = assert!(std::mem::size_of::<LookupFixedHeader>() <= Lookup::<()>::MIN_SIZE);
816
817/// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table)
818#[derive(Clone)]
819pub struct Lookup<'a, T = ()> {
820    data: TableDataRef<'a, LookupFixedHeader>,
821    offset_type: std::marker::PhantomData<*const T>,
822}
823
824#[allow(clippy::needless_lifetimes)]
825impl<'a, T> Lookup<'a, T> {
826    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + LookupFlag::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
827    basic_table_impls!(impl_the_methods);
828
829    /// Different enumerations for GSUB and GPOS
830    pub fn lookup_type(&self) -> u16 {
831        self.data.header().lookup_type.get()
832    }
833
834    /// Lookup qualifiers
835    pub fn lookup_flag(&self) -> LookupFlag {
836        self.data.header().lookup_flag.get()
837    }
838
839    /// Number of subtables for this lookup
840    pub fn sub_table_count(&self) -> u16 {
841        self.data.header().sub_table_count.get()
842    }
843
844    /// Array of offsets to lookup subtables, from beginning of Lookup
845    /// table
846    pub fn subtable_offsets(&self) -> &'a [BigEndian<Offset16>] {
847        let range = self.subtable_offsets_byte_range();
848        self.offset_data()
849            .read_array(range)
850            .ok()
851            .unwrap_or_default()
852    }
853
854    /// A dynamically resolving wrapper for [`subtable_offsets`][Self::subtable_offsets].
855    pub fn subtables(&self) -> ArrayOfOffsets<'a, T, Offset16>
856    where
857        T: FontRead<'a, Args = ()>,
858    {
859        let data = self.offset_data();
860        let offsets = self.subtable_offsets();
861        ArrayOfOffsets::new(offsets, data, ())
862    }
863
864    /// Index (base 0) into GDEF mark glyph sets structure. This field
865    /// is only present if the USE_MARK_FILTERING_SET lookup flag is
866    /// set.
867    pub fn mark_filtering_set(&self) -> Option<u16> {
868        let range = self.mark_filtering_set_byte_range();
869        (!range.is_empty())
870            .then(|| self.offset_data().read_at(range.start).ok())
871            .flatten()
872    }
873
874    pub fn lookup_type_byte_range(&self) -> Range<usize> {
875        let start = 0;
876        let end = start + u16::RAW_BYTE_LEN;
877        start..end
878    }
879
880    pub fn lookup_flag_byte_range(&self) -> Range<usize> {
881        let start = self.lookup_type_byte_range().end;
882        let end = start + LookupFlag::RAW_BYTE_LEN;
883        start..end
884    }
885
886    pub fn sub_table_count_byte_range(&self) -> Range<usize> {
887        let start = self.lookup_flag_byte_range().end;
888        let end = start + u16::RAW_BYTE_LEN;
889        start..end
890    }
891
892    pub fn subtable_offsets_byte_range(&self) -> Range<usize> {
893        let sub_table_count = self.sub_table_count();
894        let start = self.sub_table_count_byte_range().end;
895        let end =
896            start + (transforms::to_usize(sub_table_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
897        start..end
898    }
899
900    pub fn mark_filtering_set_byte_range(&self) -> Range<usize> {
901        let start = self.subtable_offsets_byte_range().end;
902        let end = if self
903            .lookup_flag()
904            .contains(LookupFlag::USE_MARK_FILTERING_SET)
905        {
906            start + u16::RAW_BYTE_LEN
907        } else {
908            start
909        };
910        start..end
911    }
912}
913
914const _: () = assert!(FontData::default_data_long_enough(Lookup::<()>::MIN_SIZE));
915
916impl<T> Default for Lookup<'_, T> {
917    fn default() -> Self {
918        Self {
919            data: TableDataRef::new(FontData::default_table_data().as_bytes())
920                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
921            offset_type: std::marker::PhantomData,
922        }
923    }
924}
925
926impl Format<u16> for CoverageFormat1<'_> {
927    const FORMAT: u16 = 1;
928}
929
930impl<'a> MinByteRange<'a> for CoverageFormat1<'a> {
931    fn min_byte_range(&self) -> Range<usize> {
932        0..self.glyph_array_byte_range().end
933    }
934    fn min_table_bytes(&self) -> &'a [u8] {
935        let range = self.min_byte_range();
936        self.offset_data().as_bytes().get(range).unwrap_or_default()
937    }
938}
939
940impl ReadArgs for CoverageFormat1<'_> {
941    type Args = ();
942}
943
944impl<'a> FontRead<'a> for CoverageFormat1<'a> {
945    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
946        #[allow(clippy::absurd_extreme_comparisons)]
947        if data.len() < Self::MIN_SIZE {
948            return Err(ReadError::OutOfBounds);
949        }
950        let Some(data) = TableDataRef::new(data.as_bytes()) else {
951            return Err(ReadError::OutOfBounds);
952        };
953        Ok(Self { data })
954    }
955}
956
957/// The fields at the start of the table, which are read directly.
958#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
959#[repr(C, packed)]
960#[doc(hidden)]
961pub struct CoverageFormat1FixedHeader {
962    pub coverage_format: BigEndian<u16>,
963    pub glyph_count: BigEndian<u16>,
964}
965
966#[allow(clippy::absurd_extreme_comparisons)]
967const _: () =
968    assert!(std::mem::size_of::<CoverageFormat1FixedHeader>() <= CoverageFormat1::MIN_SIZE);
969
970/// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1)
971#[derive(Clone)]
972pub struct CoverageFormat1<'a> {
973    data: TableDataRef<'a, CoverageFormat1FixedHeader>,
974}
975
976#[allow(clippy::needless_lifetimes)]
977impl<'a> CoverageFormat1<'a> {
978    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
979    basic_table_impls!(impl_the_methods);
980
981    /// Format identifier — format = 1
982    pub fn coverage_format(&self) -> u16 {
983        self.data.header().coverage_format.get()
984    }
985
986    /// Number of glyphs in the glyph array
987    pub fn glyph_count(&self) -> u16 {
988        self.data.header().glyph_count.get()
989    }
990
991    /// Array of glyph IDs — in numerical order
992    pub fn glyph_array(&self) -> &'a [BigEndian<GlyphId16>] {
993        let range = self.glyph_array_byte_range();
994        self.offset_data()
995            .read_array(range)
996            .ok()
997            .unwrap_or_default()
998    }
999
1000    pub fn coverage_format_byte_range(&self) -> Range<usize> {
1001        let start = 0;
1002        let end = start + u16::RAW_BYTE_LEN;
1003        start..end
1004    }
1005
1006    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1007        let start = self.coverage_format_byte_range().end;
1008        let end = start + u16::RAW_BYTE_LEN;
1009        start..end
1010    }
1011
1012    pub fn glyph_array_byte_range(&self) -> Range<usize> {
1013        let glyph_count = self.glyph_count();
1014        let start = self.glyph_count_byte_range().end;
1015        let end =
1016            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1017        start..end
1018    }
1019}
1020
1021const _: () = assert!(FontData::default_data_long_enough(
1022    CoverageFormat1::MIN_SIZE
1023));
1024
1025impl Default for CoverageFormat1<'_> {
1026    fn default() -> Self {
1027        Self {
1028            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1029                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1030        }
1031    }
1032}
1033
1034impl Format<u16> for CoverageFormat2<'_> {
1035    const FORMAT: u16 = 2;
1036}
1037
1038impl<'a> MinByteRange<'a> for CoverageFormat2<'a> {
1039    fn min_byte_range(&self) -> Range<usize> {
1040        0..self.range_records_byte_range().end
1041    }
1042    fn min_table_bytes(&self) -> &'a [u8] {
1043        let range = self.min_byte_range();
1044        self.offset_data().as_bytes().get(range).unwrap_or_default()
1045    }
1046}
1047
1048impl ReadArgs for CoverageFormat2<'_> {
1049    type Args = ();
1050}
1051
1052impl<'a> FontRead<'a> for CoverageFormat2<'a> {
1053    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1054        #[allow(clippy::absurd_extreme_comparisons)]
1055        if data.len() < Self::MIN_SIZE {
1056            return Err(ReadError::OutOfBounds);
1057        }
1058        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1059            return Err(ReadError::OutOfBounds);
1060        };
1061        Ok(Self { data })
1062    }
1063}
1064
1065/// The fields at the start of the table, which are read directly.
1066#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1067#[repr(C, packed)]
1068#[doc(hidden)]
1069pub struct CoverageFormat2FixedHeader {
1070    pub coverage_format: BigEndian<u16>,
1071    pub range_count: BigEndian<u16>,
1072}
1073
1074#[allow(clippy::absurd_extreme_comparisons)]
1075const _: () =
1076    assert!(std::mem::size_of::<CoverageFormat2FixedHeader>() <= CoverageFormat2::MIN_SIZE);
1077
1078/// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2)
1079#[derive(Clone)]
1080pub struct CoverageFormat2<'a> {
1081    data: TableDataRef<'a, CoverageFormat2FixedHeader>,
1082}
1083
1084#[allow(clippy::needless_lifetimes)]
1085impl<'a> CoverageFormat2<'a> {
1086    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1087    basic_table_impls!(impl_the_methods);
1088
1089    /// Format identifier — format = 2
1090    pub fn coverage_format(&self) -> u16 {
1091        self.data.header().coverage_format.get()
1092    }
1093
1094    /// Number of RangeRecords
1095    pub fn range_count(&self) -> u16 {
1096        self.data.header().range_count.get()
1097    }
1098
1099    /// Array of glyph ranges — ordered by startGlyphID.
1100    pub fn range_records(&self) -> &'a [RangeRecord] {
1101        let range = self.range_records_byte_range();
1102        self.offset_data()
1103            .read_array(range)
1104            .ok()
1105            .unwrap_or_default()
1106    }
1107
1108    pub fn coverage_format_byte_range(&self) -> Range<usize> {
1109        let start = 0;
1110        let end = start + u16::RAW_BYTE_LEN;
1111        start..end
1112    }
1113
1114    pub fn range_count_byte_range(&self) -> Range<usize> {
1115        let start = self.coverage_format_byte_range().end;
1116        let end = start + u16::RAW_BYTE_LEN;
1117        start..end
1118    }
1119
1120    pub fn range_records_byte_range(&self) -> Range<usize> {
1121        let range_count = self.range_count();
1122        let start = self.range_count_byte_range().end;
1123        let end =
1124            start + (transforms::to_usize(range_count)).saturating_mul(RangeRecord::RAW_BYTE_LEN);
1125        start..end
1126    }
1127}
1128
1129/// Used in [CoverageFormat2]
1130#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1131#[repr(C)]
1132#[repr(packed)]
1133pub struct RangeRecord {
1134    /// First glyph ID in the range
1135    pub start_glyph_id: BigEndian<GlyphId16>,
1136    /// Last glyph ID in the range
1137    pub end_glyph_id: BigEndian<GlyphId16>,
1138    /// Coverage Index of first glyph ID in range
1139    pub start_coverage_index: BigEndian<u16>,
1140}
1141
1142impl RangeRecord {
1143    /// First glyph ID in the range
1144    pub fn start_glyph_id(&self) -> GlyphId16 {
1145        self.start_glyph_id.get()
1146    }
1147
1148    /// Last glyph ID in the range
1149    pub fn end_glyph_id(&self) -> GlyphId16 {
1150        self.end_glyph_id.get()
1151    }
1152
1153    /// Coverage Index of first glyph ID in range
1154    pub fn start_coverage_index(&self) -> u16 {
1155        self.start_coverage_index.get()
1156    }
1157}
1158
1159impl FixedSize for RangeRecord {
1160    const RAW_BYTE_LEN: usize =
1161        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1162}
1163
1164/// [Coverage Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-table)
1165#[derive(Clone)]
1166pub enum CoverageTable<'a> {
1167    Format1(CoverageFormat1<'a>),
1168    Format2(CoverageFormat2<'a>),
1169    Format3(CoverageFormat3<'a>),
1170    Format4(CoverageFormat4<'a>),
1171}
1172
1173impl Default for CoverageTable<'_> {
1174    fn default() -> Self {
1175        Self::Format1(Default::default())
1176    }
1177}
1178
1179impl<'a> CoverageTable<'a> {
1180    ///Return the `FontData` used to resolve offsets for this table.
1181    pub fn offset_data(&self) -> FontData<'a> {
1182        match self {
1183            Self::Format1(item) => item.offset_data(),
1184            Self::Format2(item) => item.offset_data(),
1185            Self::Format3(item) => item.offset_data(),
1186            Self::Format4(item) => item.offset_data(),
1187        }
1188    }
1189
1190    /// Format identifier — format = 1
1191    pub fn coverage_format(&self) -> u16 {
1192        match self {
1193            Self::Format1(item) => item.coverage_format(),
1194            Self::Format2(item) => item.coverage_format(),
1195            Self::Format3(item) => item.coverage_format(),
1196            Self::Format4(item) => item.coverage_format(),
1197        }
1198    }
1199}
1200
1201impl ReadArgs for CoverageTable<'_> {
1202    type Args = ();
1203}
1204
1205impl<'a> FontRead<'a> for CoverageTable<'a> {
1206    #[inline]
1207    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1208        let format: u16 = data.read_at(0usize)?;
1209        match format {
1210            CoverageFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1211            CoverageFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1212            CoverageFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
1213            CoverageFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
1214            other => Err(ReadError::InvalidFormat(other.into())),
1215        }
1216    }
1217}
1218
1219impl<'a> MinByteRange<'a> for CoverageTable<'a> {
1220    fn min_byte_range(&self) -> Range<usize> {
1221        match self {
1222            Self::Format1(item) => item.min_byte_range(),
1223            Self::Format2(item) => item.min_byte_range(),
1224            Self::Format3(item) => item.min_byte_range(),
1225            Self::Format4(item) => item.min_byte_range(),
1226        }
1227    }
1228    fn min_table_bytes(&self) -> &'a [u8] {
1229        match self {
1230            Self::Format1(item) => item.min_table_bytes(),
1231            Self::Format2(item) => item.min_table_bytes(),
1232            Self::Format3(item) => item.min_table_bytes(),
1233            Self::Format4(item) => item.min_table_bytes(),
1234        }
1235    }
1236}
1237
1238impl Format<u16> for ClassDefFormat1<'_> {
1239    const FORMAT: u16 = 1;
1240}
1241
1242impl<'a> MinByteRange<'a> for ClassDefFormat1<'a> {
1243    fn min_byte_range(&self) -> Range<usize> {
1244        0..self.class_value_array_byte_range().end
1245    }
1246    fn min_table_bytes(&self) -> &'a [u8] {
1247        let range = self.min_byte_range();
1248        self.offset_data().as_bytes().get(range).unwrap_or_default()
1249    }
1250}
1251
1252impl ReadArgs for ClassDefFormat1<'_> {
1253    type Args = ();
1254}
1255
1256impl<'a> FontRead<'a> for ClassDefFormat1<'a> {
1257    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1258        #[allow(clippy::absurd_extreme_comparisons)]
1259        if data.len() < Self::MIN_SIZE {
1260            return Err(ReadError::OutOfBounds);
1261        }
1262        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1263            return Err(ReadError::OutOfBounds);
1264        };
1265        Ok(Self { data })
1266    }
1267}
1268
1269/// The fields at the start of the table, which are read directly.
1270#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1271#[repr(C, packed)]
1272#[doc(hidden)]
1273pub struct ClassDefFormat1FixedHeader {
1274    pub class_format: BigEndian<u16>,
1275    pub start_glyph_id: BigEndian<GlyphId16>,
1276    pub glyph_count: BigEndian<u16>,
1277}
1278
1279#[allow(clippy::absurd_extreme_comparisons)]
1280const _: () =
1281    assert!(std::mem::size_of::<ClassDefFormat1FixedHeader>() <= ClassDefFormat1::MIN_SIZE);
1282
1283/// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1)
1284#[derive(Clone)]
1285pub struct ClassDefFormat1<'a> {
1286    data: TableDataRef<'a, ClassDefFormat1FixedHeader>,
1287}
1288
1289#[allow(clippy::needless_lifetimes)]
1290impl<'a> ClassDefFormat1<'a> {
1291    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1292    basic_table_impls!(impl_the_methods);
1293
1294    /// Format identifier — format = 1
1295    pub fn class_format(&self) -> u16 {
1296        self.data.header().class_format.get()
1297    }
1298
1299    /// First glyph ID of the classValueArray
1300    pub fn start_glyph_id(&self) -> GlyphId16 {
1301        self.data.header().start_glyph_id.get()
1302    }
1303
1304    /// Size of the classValueArray
1305    pub fn glyph_count(&self) -> u16 {
1306        self.data.header().glyph_count.get()
1307    }
1308
1309    /// Array of Class Values — one per glyph ID
1310    pub fn class_value_array(&self) -> &'a [BigEndian<u16>] {
1311        let range = self.class_value_array_byte_range();
1312        self.offset_data()
1313            .read_array(range)
1314            .ok()
1315            .unwrap_or_default()
1316    }
1317
1318    pub fn class_format_byte_range(&self) -> Range<usize> {
1319        let start = 0;
1320        let end = start + u16::RAW_BYTE_LEN;
1321        start..end
1322    }
1323
1324    pub fn start_glyph_id_byte_range(&self) -> Range<usize> {
1325        let start = self.class_format_byte_range().end;
1326        let end = start + GlyphId16::RAW_BYTE_LEN;
1327        start..end
1328    }
1329
1330    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1331        let start = self.start_glyph_id_byte_range().end;
1332        let end = start + u16::RAW_BYTE_LEN;
1333        start..end
1334    }
1335
1336    pub fn class_value_array_byte_range(&self) -> Range<usize> {
1337        let glyph_count = self.glyph_count();
1338        let start = self.glyph_count_byte_range().end;
1339        let end = start + (transforms::to_usize(glyph_count)).saturating_mul(u16::RAW_BYTE_LEN);
1340        start..end
1341    }
1342}
1343
1344const _: () = assert!(FontData::default_data_long_enough(
1345    ClassDefFormat1::MIN_SIZE
1346));
1347
1348impl Default for ClassDefFormat1<'_> {
1349    fn default() -> Self {
1350        Self {
1351            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1352                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1353        }
1354    }
1355}
1356
1357impl Format<u16> for ClassDefFormat2<'_> {
1358    const FORMAT: u16 = 2;
1359}
1360
1361impl<'a> MinByteRange<'a> for ClassDefFormat2<'a> {
1362    fn min_byte_range(&self) -> Range<usize> {
1363        0..self.class_range_records_byte_range().end
1364    }
1365    fn min_table_bytes(&self) -> &'a [u8] {
1366        let range = self.min_byte_range();
1367        self.offset_data().as_bytes().get(range).unwrap_or_default()
1368    }
1369}
1370
1371impl ReadArgs for ClassDefFormat2<'_> {
1372    type Args = ();
1373}
1374
1375impl<'a> FontRead<'a> for ClassDefFormat2<'a> {
1376    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1377        #[allow(clippy::absurd_extreme_comparisons)]
1378        if data.len() < Self::MIN_SIZE {
1379            return Err(ReadError::OutOfBounds);
1380        }
1381        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1382            return Err(ReadError::OutOfBounds);
1383        };
1384        Ok(Self { data })
1385    }
1386}
1387
1388/// The fields at the start of the table, which are read directly.
1389#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1390#[repr(C, packed)]
1391#[doc(hidden)]
1392pub struct ClassDefFormat2FixedHeader {
1393    pub class_format: BigEndian<u16>,
1394    pub class_range_count: BigEndian<u16>,
1395}
1396
1397#[allow(clippy::absurd_extreme_comparisons)]
1398const _: () =
1399    assert!(std::mem::size_of::<ClassDefFormat2FixedHeader>() <= ClassDefFormat2::MIN_SIZE);
1400
1401/// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2)
1402#[derive(Clone)]
1403pub struct ClassDefFormat2<'a> {
1404    data: TableDataRef<'a, ClassDefFormat2FixedHeader>,
1405}
1406
1407#[allow(clippy::needless_lifetimes)]
1408impl<'a> ClassDefFormat2<'a> {
1409    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1410    basic_table_impls!(impl_the_methods);
1411
1412    /// Format identifier — format = 2
1413    pub fn class_format(&self) -> u16 {
1414        self.data.header().class_format.get()
1415    }
1416
1417    /// Number of ClassRangeRecords
1418    pub fn class_range_count(&self) -> u16 {
1419        self.data.header().class_range_count.get()
1420    }
1421
1422    /// Array of ClassRangeRecords — ordered by startGlyphID
1423    pub fn class_range_records(&self) -> &'a [ClassRangeRecord] {
1424        let range = self.class_range_records_byte_range();
1425        self.offset_data()
1426            .read_array(range)
1427            .ok()
1428            .unwrap_or_default()
1429    }
1430
1431    pub fn class_format_byte_range(&self) -> Range<usize> {
1432        let start = 0;
1433        let end = start + u16::RAW_BYTE_LEN;
1434        start..end
1435    }
1436
1437    pub fn class_range_count_byte_range(&self) -> Range<usize> {
1438        let start = self.class_format_byte_range().end;
1439        let end = start + u16::RAW_BYTE_LEN;
1440        start..end
1441    }
1442
1443    pub fn class_range_records_byte_range(&self) -> Range<usize> {
1444        let class_range_count = self.class_range_count();
1445        let start = self.class_range_count_byte_range().end;
1446        let end = start
1447            + (transforms::to_usize(class_range_count))
1448                .saturating_mul(ClassRangeRecord::RAW_BYTE_LEN);
1449        start..end
1450    }
1451}
1452
1453/// Used in [ClassDefFormat2]
1454#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1455#[repr(C)]
1456#[repr(packed)]
1457pub struct ClassRangeRecord {
1458    /// First glyph ID in the range
1459    pub start_glyph_id: BigEndian<GlyphId16>,
1460    /// Last glyph ID in the range
1461    pub end_glyph_id: BigEndian<GlyphId16>,
1462    /// Applied to all glyphs in the range
1463    pub class: BigEndian<u16>,
1464}
1465
1466impl ClassRangeRecord {
1467    /// First glyph ID in the range
1468    pub fn start_glyph_id(&self) -> GlyphId16 {
1469        self.start_glyph_id.get()
1470    }
1471
1472    /// Last glyph ID in the range
1473    pub fn end_glyph_id(&self) -> GlyphId16 {
1474        self.end_glyph_id.get()
1475    }
1476
1477    /// Applied to all glyphs in the range
1478    pub fn class(&self) -> u16 {
1479        self.class.get()
1480    }
1481}
1482
1483impl FixedSize for ClassRangeRecord {
1484    const RAW_BYTE_LEN: usize =
1485        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1486}
1487
1488/// A [Class Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table)
1489#[derive(Clone)]
1490pub enum ClassDef<'a> {
1491    Format1(ClassDefFormat1<'a>),
1492    Format2(ClassDefFormat2<'a>),
1493    Format3(ClassDefFormat3<'a>),
1494    Format4(ClassDefFormat4<'a>),
1495}
1496
1497impl Default for ClassDef<'_> {
1498    fn default() -> Self {
1499        Self::Format1(Default::default())
1500    }
1501}
1502
1503impl<'a> ClassDef<'a> {
1504    ///Return the `FontData` used to resolve offsets for this table.
1505    pub fn offset_data(&self) -> FontData<'a> {
1506        match self {
1507            Self::Format1(item) => item.offset_data(),
1508            Self::Format2(item) => item.offset_data(),
1509            Self::Format3(item) => item.offset_data(),
1510            Self::Format4(item) => item.offset_data(),
1511        }
1512    }
1513
1514    /// Format identifier — format = 1
1515    pub fn class_format(&self) -> u16 {
1516        match self {
1517            Self::Format1(item) => item.class_format(),
1518            Self::Format2(item) => item.class_format(),
1519            Self::Format3(item) => item.class_format(),
1520            Self::Format4(item) => item.class_format(),
1521        }
1522    }
1523}
1524
1525impl ReadArgs for ClassDef<'_> {
1526    type Args = ();
1527}
1528
1529impl<'a> FontRead<'a> for ClassDef<'a> {
1530    #[inline]
1531    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1532        let format: u16 = data.read_at(0usize)?;
1533        match format {
1534            ClassDefFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1535            ClassDefFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1536            ClassDefFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
1537            ClassDefFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
1538            other => Err(ReadError::InvalidFormat(other.into())),
1539        }
1540    }
1541}
1542
1543impl<'a> MinByteRange<'a> for ClassDef<'a> {
1544    fn min_byte_range(&self) -> Range<usize> {
1545        match self {
1546            Self::Format1(item) => item.min_byte_range(),
1547            Self::Format2(item) => item.min_byte_range(),
1548            Self::Format3(item) => item.min_byte_range(),
1549            Self::Format4(item) => item.min_byte_range(),
1550        }
1551    }
1552    fn min_table_bytes(&self) -> &'a [u8] {
1553        match self {
1554            Self::Format1(item) => item.min_table_bytes(),
1555            Self::Format2(item) => item.min_table_bytes(),
1556            Self::Format3(item) => item.min_table_bytes(),
1557            Self::Format4(item) => item.min_table_bytes(),
1558        }
1559    }
1560}
1561
1562/// [Sequence Lookup Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-lookup-record)
1563#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1564#[repr(C)]
1565#[repr(packed)]
1566pub struct SequenceLookupRecord {
1567    /// Index (zero-based) into the input glyph sequence
1568    pub sequence_index: BigEndian<u16>,
1569    /// Index (zero-based) into the LookupList
1570    pub lookup_list_index: BigEndian<u16>,
1571}
1572
1573impl SequenceLookupRecord {
1574    /// Index (zero-based) into the input glyph sequence
1575    pub fn sequence_index(&self) -> u16 {
1576        self.sequence_index.get()
1577    }
1578
1579    /// Index (zero-based) into the LookupList
1580    pub fn lookup_list_index(&self) -> u16 {
1581        self.lookup_list_index.get()
1582    }
1583}
1584
1585impl FixedSize for SequenceLookupRecord {
1586    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1587}
1588
1589impl Format<u16> for SequenceContextFormat1<'_> {
1590    const FORMAT: u16 = 1;
1591}
1592
1593impl<'a> MinByteRange<'a> for SequenceContextFormat1<'a> {
1594    fn min_byte_range(&self) -> Range<usize> {
1595        0..self.seq_rule_set_offsets_byte_range().end
1596    }
1597    fn min_table_bytes(&self) -> &'a [u8] {
1598        let range = self.min_byte_range();
1599        self.offset_data().as_bytes().get(range).unwrap_or_default()
1600    }
1601}
1602
1603impl ReadArgs for SequenceContextFormat1<'_> {
1604    type Args = ();
1605}
1606
1607impl<'a> FontRead<'a> for SequenceContextFormat1<'a> {
1608    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1609        #[allow(clippy::absurd_extreme_comparisons)]
1610        if data.len() < Self::MIN_SIZE {
1611            return Err(ReadError::OutOfBounds);
1612        }
1613        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1614            return Err(ReadError::OutOfBounds);
1615        };
1616        Ok(Self { data })
1617    }
1618}
1619
1620/// The fields at the start of the table, which are read directly.
1621#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1622#[repr(C, packed)]
1623#[doc(hidden)]
1624pub struct SequenceContextFormat1FixedHeader {
1625    pub format: BigEndian<u16>,
1626    pub coverage_offset: BigEndian<Offset16>,
1627    pub seq_rule_set_count: BigEndian<u16>,
1628}
1629
1630#[allow(clippy::absurd_extreme_comparisons)]
1631const _: () = assert!(
1632    std::mem::size_of::<SequenceContextFormat1FixedHeader>() <= SequenceContextFormat1::MIN_SIZE
1633);
1634
1635/// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts)
1636#[derive(Clone)]
1637pub struct SequenceContextFormat1<'a> {
1638    data: TableDataRef<'a, SequenceContextFormat1FixedHeader>,
1639}
1640
1641#[allow(clippy::needless_lifetimes)]
1642impl<'a> SequenceContextFormat1<'a> {
1643    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1644    basic_table_impls!(impl_the_methods);
1645
1646    /// Format identifier: format = 1
1647    pub fn format(&self) -> u16 {
1648        self.data.header().format.get()
1649    }
1650
1651    /// Offset to Coverage table, from beginning of
1652    /// SequenceContextFormat1 table
1653    pub fn coverage_offset(&self) -> Offset16 {
1654        self.data.header().coverage_offset.get()
1655    }
1656
1657    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1658    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1659        let data = self.offset_data();
1660        self.coverage_offset().resolve(data)
1661    }
1662
1663    /// Number of SequenceRuleSet tables
1664    pub fn seq_rule_set_count(&self) -> u16 {
1665        self.data.header().seq_rule_set_count.get()
1666    }
1667
1668    /// Array of offsets to SequenceRuleSet tables, from beginning of
1669    /// SequenceContextFormat1 table (offsets may be NULL)
1670    pub fn seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
1671        let range = self.seq_rule_set_offsets_byte_range();
1672        self.offset_data()
1673            .read_array(range)
1674            .ok()
1675            .unwrap_or_default()
1676    }
1677
1678    /// A dynamically resolving wrapper for [`seq_rule_set_offsets`][Self::seq_rule_set_offsets].
1679    pub fn seq_rule_sets(&self) -> ArrayOfNullableOffsets<'a, SequenceRuleSet<'a>, Offset16> {
1680        let data = self.offset_data();
1681        let offsets = self.seq_rule_set_offsets();
1682        ArrayOfNullableOffsets::new(offsets, data, ())
1683    }
1684
1685    pub fn format_byte_range(&self) -> Range<usize> {
1686        let start = 0;
1687        let end = start + u16::RAW_BYTE_LEN;
1688        start..end
1689    }
1690
1691    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1692        let start = self.format_byte_range().end;
1693        let end = start + Offset16::RAW_BYTE_LEN;
1694        start..end
1695    }
1696
1697    pub fn seq_rule_set_count_byte_range(&self) -> Range<usize> {
1698        let start = self.coverage_offset_byte_range().end;
1699        let end = start + u16::RAW_BYTE_LEN;
1700        start..end
1701    }
1702
1703    pub fn seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
1704        let seq_rule_set_count = self.seq_rule_set_count();
1705        let start = self.seq_rule_set_count_byte_range().end;
1706        let end = start
1707            + (transforms::to_usize(seq_rule_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1708        start..end
1709    }
1710}
1711
1712const _: () = assert!(FontData::default_data_long_enough(
1713    SequenceContextFormat1::MIN_SIZE
1714));
1715
1716impl Default for SequenceContextFormat1<'_> {
1717    fn default() -> Self {
1718        Self {
1719            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1720                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1721        }
1722    }
1723}
1724
1725impl<'a> MinByteRange<'a> for SequenceRuleSet<'a> {
1726    fn min_byte_range(&self) -> Range<usize> {
1727        0..self.seq_rule_offsets_byte_range().end
1728    }
1729    fn min_table_bytes(&self) -> &'a [u8] {
1730        let range = self.min_byte_range();
1731        self.offset_data().as_bytes().get(range).unwrap_or_default()
1732    }
1733}
1734
1735impl ReadArgs for SequenceRuleSet<'_> {
1736    type Args = ();
1737}
1738
1739impl<'a> FontRead<'a> for SequenceRuleSet<'a> {
1740    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1741        #[allow(clippy::absurd_extreme_comparisons)]
1742        if data.len() < Self::MIN_SIZE {
1743            return Err(ReadError::OutOfBounds);
1744        }
1745        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1746            return Err(ReadError::OutOfBounds);
1747        };
1748        Ok(Self { data })
1749    }
1750}
1751
1752/// The fields at the start of the table, which are read directly.
1753#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1754#[repr(C, packed)]
1755#[doc(hidden)]
1756pub struct SequenceRuleSetFixedHeader {
1757    pub seq_rule_count: BigEndian<u16>,
1758}
1759
1760#[allow(clippy::absurd_extreme_comparisons)]
1761const _: () =
1762    assert!(std::mem::size_of::<SequenceRuleSetFixedHeader>() <= SequenceRuleSet::MIN_SIZE);
1763
1764/// Part of [SequenceContextFormat1]
1765#[derive(Clone)]
1766pub struct SequenceRuleSet<'a> {
1767    data: TableDataRef<'a, SequenceRuleSetFixedHeader>,
1768}
1769
1770#[allow(clippy::needless_lifetimes)]
1771impl<'a> SequenceRuleSet<'a> {
1772    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1773    basic_table_impls!(impl_the_methods);
1774
1775    /// Number of SequenceRule tables
1776    pub fn seq_rule_count(&self) -> u16 {
1777        self.data.header().seq_rule_count.get()
1778    }
1779
1780    /// Array of offsets to SequenceRule tables, from beginning of the
1781    /// SequenceRuleSet table
1782    pub fn seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
1783        let range = self.seq_rule_offsets_byte_range();
1784        self.offset_data()
1785            .read_array(range)
1786            .ok()
1787            .unwrap_or_default()
1788    }
1789
1790    /// A dynamically resolving wrapper for [`seq_rule_offsets`][Self::seq_rule_offsets].
1791    pub fn seq_rules(&self) -> ArrayOfOffsets<'a, SequenceRule<'a>, Offset16> {
1792        let data = self.offset_data();
1793        let offsets = self.seq_rule_offsets();
1794        ArrayOfOffsets::new(offsets, data, ())
1795    }
1796
1797    pub fn seq_rule_count_byte_range(&self) -> Range<usize> {
1798        let start = 0;
1799        let end = start + u16::RAW_BYTE_LEN;
1800        start..end
1801    }
1802
1803    pub fn seq_rule_offsets_byte_range(&self) -> Range<usize> {
1804        let seq_rule_count = self.seq_rule_count();
1805        let start = self.seq_rule_count_byte_range().end;
1806        let end =
1807            start + (transforms::to_usize(seq_rule_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1808        start..end
1809    }
1810}
1811
1812const _: () = assert!(FontData::default_data_long_enough(
1813    SequenceRuleSet::MIN_SIZE
1814));
1815
1816impl Default for SequenceRuleSet<'_> {
1817    fn default() -> Self {
1818        Self {
1819            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1820                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1821        }
1822    }
1823}
1824
1825impl<'a> MinByteRange<'a> for SequenceRule<'a> {
1826    fn min_byte_range(&self) -> Range<usize> {
1827        0..self.seq_lookup_records_byte_range().end
1828    }
1829    fn min_table_bytes(&self) -> &'a [u8] {
1830        let range = self.min_byte_range();
1831        self.offset_data().as_bytes().get(range).unwrap_or_default()
1832    }
1833}
1834
1835impl ReadArgs for SequenceRule<'_> {
1836    type Args = ();
1837}
1838
1839impl<'a> FontRead<'a> for SequenceRule<'a> {
1840    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1841        #[allow(clippy::absurd_extreme_comparisons)]
1842        if data.len() < Self::MIN_SIZE {
1843            return Err(ReadError::OutOfBounds);
1844        }
1845        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1846            return Err(ReadError::OutOfBounds);
1847        };
1848        Ok(Self { data })
1849    }
1850}
1851
1852/// The fields at the start of the table, which are read directly.
1853#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1854#[repr(C, packed)]
1855#[doc(hidden)]
1856pub struct SequenceRuleFixedHeader {
1857    pub glyph_count: BigEndian<u16>,
1858    pub seq_lookup_count: BigEndian<u16>,
1859}
1860
1861#[allow(clippy::absurd_extreme_comparisons)]
1862const _: () = assert!(std::mem::size_of::<SequenceRuleFixedHeader>() <= SequenceRule::MIN_SIZE);
1863
1864/// Part of [SequenceContextFormat1]
1865#[derive(Clone)]
1866pub struct SequenceRule<'a> {
1867    data: TableDataRef<'a, SequenceRuleFixedHeader>,
1868}
1869
1870#[allow(clippy::needless_lifetimes)]
1871impl<'a> SequenceRule<'a> {
1872    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1873    basic_table_impls!(impl_the_methods);
1874
1875    /// Number of glyphs in the input glyph sequence
1876    pub fn glyph_count(&self) -> u16 {
1877        self.data.header().glyph_count.get()
1878    }
1879
1880    /// Number of SequenceLookupRecords
1881    pub fn seq_lookup_count(&self) -> u16 {
1882        self.data.header().seq_lookup_count.get()
1883    }
1884
1885    /// Array of input glyph IDs—starting with the second glyph
1886    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
1887        let range = self.input_sequence_byte_range();
1888        self.offset_data()
1889            .read_array(range)
1890            .ok()
1891            .unwrap_or_default()
1892    }
1893
1894    /// Array of Sequence lookup records
1895    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
1896        let range = self.seq_lookup_records_byte_range();
1897        self.offset_data()
1898            .read_array(range)
1899            .ok()
1900            .unwrap_or_default()
1901    }
1902
1903    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1904        let start = 0;
1905        let end = start + u16::RAW_BYTE_LEN;
1906        start..end
1907    }
1908
1909    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
1910        let start = self.glyph_count_byte_range().end;
1911        let end = start + u16::RAW_BYTE_LEN;
1912        start..end
1913    }
1914
1915    pub fn input_sequence_byte_range(&self) -> Range<usize> {
1916        let glyph_count = self.glyph_count();
1917        let start = self.seq_lookup_count_byte_range().end;
1918        let end = start
1919            + (transforms::subtract(glyph_count, 1_usize)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1920        start..end
1921    }
1922
1923    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
1924        let seq_lookup_count = self.seq_lookup_count();
1925        let start = self.input_sequence_byte_range().end;
1926        let end = start
1927            + (transforms::to_usize(seq_lookup_count))
1928                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
1929        start..end
1930    }
1931}
1932
1933const _: () = assert!(FontData::default_data_long_enough(SequenceRule::MIN_SIZE));
1934
1935impl Default for SequenceRule<'_> {
1936    fn default() -> Self {
1937        Self {
1938            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1939                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1940        }
1941    }
1942}
1943
1944impl Format<u16> for SequenceContextFormat2<'_> {
1945    const FORMAT: u16 = 2;
1946}
1947
1948impl<'a> MinByteRange<'a> for SequenceContextFormat2<'a> {
1949    fn min_byte_range(&self) -> Range<usize> {
1950        0..self.class_seq_rule_set_offsets_byte_range().end
1951    }
1952    fn min_table_bytes(&self) -> &'a [u8] {
1953        let range = self.min_byte_range();
1954        self.offset_data().as_bytes().get(range).unwrap_or_default()
1955    }
1956}
1957
1958impl ReadArgs for SequenceContextFormat2<'_> {
1959    type Args = ();
1960}
1961
1962impl<'a> FontRead<'a> for SequenceContextFormat2<'a> {
1963    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1964        #[allow(clippy::absurd_extreme_comparisons)]
1965        if data.len() < Self::MIN_SIZE {
1966            return Err(ReadError::OutOfBounds);
1967        }
1968        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1969            return Err(ReadError::OutOfBounds);
1970        };
1971        Ok(Self { data })
1972    }
1973}
1974
1975/// The fields at the start of the table, which are read directly.
1976#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1977#[repr(C, packed)]
1978#[doc(hidden)]
1979pub struct SequenceContextFormat2FixedHeader {
1980    pub format: BigEndian<u16>,
1981    pub coverage_offset: BigEndian<Offset16>,
1982    pub class_def_offset: BigEndian<Offset16>,
1983    pub class_seq_rule_set_count: BigEndian<u16>,
1984}
1985
1986#[allow(clippy::absurd_extreme_comparisons)]
1987const _: () = assert!(
1988    std::mem::size_of::<SequenceContextFormat2FixedHeader>() <= SequenceContextFormat2::MIN_SIZE
1989);
1990
1991/// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts)
1992#[derive(Clone)]
1993pub struct SequenceContextFormat2<'a> {
1994    data: TableDataRef<'a, SequenceContextFormat2FixedHeader>,
1995}
1996
1997#[allow(clippy::needless_lifetimes)]
1998impl<'a> SequenceContextFormat2<'a> {
1999    pub const MIN_SIZE: usize =
2000        (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2001    basic_table_impls!(impl_the_methods);
2002
2003    /// Format identifier: format = 2
2004    pub fn format(&self) -> u16 {
2005        self.data.header().format.get()
2006    }
2007
2008    /// Offset to Coverage table, from beginning of
2009    /// SequenceContextFormat2 table
2010    pub fn coverage_offset(&self) -> Offset16 {
2011        self.data.header().coverage_offset.get()
2012    }
2013
2014    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2015    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2016        let data = self.offset_data();
2017        self.coverage_offset().resolve(data)
2018    }
2019
2020    /// Offset to ClassDef table, from beginning of
2021    /// SequenceContextFormat2 table
2022    pub fn class_def_offset(&self) -> Offset16 {
2023        self.data.header().class_def_offset.get()
2024    }
2025
2026    /// Attempt to resolve [`class_def_offset`][Self::class_def_offset].
2027    pub fn class_def(&self) -> Result<ClassDef<'a>, ReadError> {
2028        let data = self.offset_data();
2029        self.class_def_offset().resolve(data)
2030    }
2031
2032    /// Number of ClassSequenceRuleSet tables
2033    pub fn class_seq_rule_set_count(&self) -> u16 {
2034        self.data.header().class_seq_rule_set_count.get()
2035    }
2036
2037    /// Array of offsets to ClassSequenceRuleSet tables, from beginning
2038    /// of SequenceContextFormat2 table (may be NULL)
2039    pub fn class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2040        let range = self.class_seq_rule_set_offsets_byte_range();
2041        self.offset_data()
2042            .read_array(range)
2043            .ok()
2044            .unwrap_or_default()
2045    }
2046
2047    /// A dynamically resolving wrapper for [`class_seq_rule_set_offsets`][Self::class_seq_rule_set_offsets].
2048    pub fn class_seq_rule_sets(
2049        &self,
2050    ) -> ArrayOfNullableOffsets<'a, ClassSequenceRuleSet<'a>, Offset16> {
2051        let data = self.offset_data();
2052        let offsets = self.class_seq_rule_set_offsets();
2053        ArrayOfNullableOffsets::new(offsets, data, ())
2054    }
2055
2056    pub fn format_byte_range(&self) -> Range<usize> {
2057        let start = 0;
2058        let end = start + u16::RAW_BYTE_LEN;
2059        start..end
2060    }
2061
2062    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2063        let start = self.format_byte_range().end;
2064        let end = start + Offset16::RAW_BYTE_LEN;
2065        start..end
2066    }
2067
2068    pub fn class_def_offset_byte_range(&self) -> Range<usize> {
2069        let start = self.coverage_offset_byte_range().end;
2070        let end = start + Offset16::RAW_BYTE_LEN;
2071        start..end
2072    }
2073
2074    pub fn class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
2075        let start = self.class_def_offset_byte_range().end;
2076        let end = start + u16::RAW_BYTE_LEN;
2077        start..end
2078    }
2079
2080    pub fn class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
2081        let class_seq_rule_set_count = self.class_seq_rule_set_count();
2082        let start = self.class_seq_rule_set_count_byte_range().end;
2083        let end = start
2084            + (transforms::to_usize(class_seq_rule_set_count))
2085                .saturating_mul(Offset16::RAW_BYTE_LEN);
2086        start..end
2087    }
2088}
2089
2090impl<'a> MinByteRange<'a> for ClassSequenceRuleSet<'a> {
2091    fn min_byte_range(&self) -> Range<usize> {
2092        0..self.class_seq_rule_offsets_byte_range().end
2093    }
2094    fn min_table_bytes(&self) -> &'a [u8] {
2095        let range = self.min_byte_range();
2096        self.offset_data().as_bytes().get(range).unwrap_or_default()
2097    }
2098}
2099
2100impl ReadArgs for ClassSequenceRuleSet<'_> {
2101    type Args = ();
2102}
2103
2104impl<'a> FontRead<'a> for ClassSequenceRuleSet<'a> {
2105    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2106        #[allow(clippy::absurd_extreme_comparisons)]
2107        if data.len() < Self::MIN_SIZE {
2108            return Err(ReadError::OutOfBounds);
2109        }
2110        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2111            return Err(ReadError::OutOfBounds);
2112        };
2113        Ok(Self { data })
2114    }
2115}
2116
2117/// The fields at the start of the table, which are read directly.
2118#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2119#[repr(C, packed)]
2120#[doc(hidden)]
2121pub struct ClassSequenceRuleSetFixedHeader {
2122    pub class_seq_rule_count: BigEndian<u16>,
2123}
2124
2125#[allow(clippy::absurd_extreme_comparisons)]
2126const _: () = assert!(
2127    std::mem::size_of::<ClassSequenceRuleSetFixedHeader>() <= ClassSequenceRuleSet::MIN_SIZE
2128);
2129
2130/// Part of [SequenceContextFormat2]
2131#[derive(Clone)]
2132pub struct ClassSequenceRuleSet<'a> {
2133    data: TableDataRef<'a, ClassSequenceRuleSetFixedHeader>,
2134}
2135
2136#[allow(clippy::needless_lifetimes)]
2137impl<'a> ClassSequenceRuleSet<'a> {
2138    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2139    basic_table_impls!(impl_the_methods);
2140
2141    /// Number of ClassSequenceRule tables
2142    pub fn class_seq_rule_count(&self) -> u16 {
2143        self.data.header().class_seq_rule_count.get()
2144    }
2145
2146    /// Array of offsets to ClassSequenceRule tables, from beginning of
2147    /// ClassSequenceRuleSet table
2148    pub fn class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
2149        let range = self.class_seq_rule_offsets_byte_range();
2150        self.offset_data()
2151            .read_array(range)
2152            .ok()
2153            .unwrap_or_default()
2154    }
2155
2156    /// A dynamically resolving wrapper for [`class_seq_rule_offsets`][Self::class_seq_rule_offsets].
2157    pub fn class_seq_rules(&self) -> ArrayOfOffsets<'a, ClassSequenceRule<'a>, Offset16> {
2158        let data = self.offset_data();
2159        let offsets = self.class_seq_rule_offsets();
2160        ArrayOfOffsets::new(offsets, data, ())
2161    }
2162
2163    pub fn class_seq_rule_count_byte_range(&self) -> Range<usize> {
2164        let start = 0;
2165        let end = start + u16::RAW_BYTE_LEN;
2166        start..end
2167    }
2168
2169    pub fn class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
2170        let class_seq_rule_count = self.class_seq_rule_count();
2171        let start = self.class_seq_rule_count_byte_range().end;
2172        let end = start
2173            + (transforms::to_usize(class_seq_rule_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
2174        start..end
2175    }
2176}
2177
2178const _: () = assert!(FontData::default_data_long_enough(
2179    ClassSequenceRuleSet::MIN_SIZE
2180));
2181
2182impl Default for ClassSequenceRuleSet<'_> {
2183    fn default() -> Self {
2184        Self {
2185            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2186                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2187        }
2188    }
2189}
2190
2191impl<'a> MinByteRange<'a> for ClassSequenceRule<'a> {
2192    fn min_byte_range(&self) -> Range<usize> {
2193        0..self.seq_lookup_records_byte_range().end
2194    }
2195    fn min_table_bytes(&self) -> &'a [u8] {
2196        let range = self.min_byte_range();
2197        self.offset_data().as_bytes().get(range).unwrap_or_default()
2198    }
2199}
2200
2201impl ReadArgs for ClassSequenceRule<'_> {
2202    type Args = ();
2203}
2204
2205impl<'a> FontRead<'a> for ClassSequenceRule<'a> {
2206    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2207        #[allow(clippy::absurd_extreme_comparisons)]
2208        if data.len() < Self::MIN_SIZE {
2209            return Err(ReadError::OutOfBounds);
2210        }
2211        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2212            return Err(ReadError::OutOfBounds);
2213        };
2214        Ok(Self { data })
2215    }
2216}
2217
2218/// The fields at the start of the table, which are read directly.
2219#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2220#[repr(C, packed)]
2221#[doc(hidden)]
2222pub struct ClassSequenceRuleFixedHeader {
2223    pub glyph_count: BigEndian<u16>,
2224    pub seq_lookup_count: BigEndian<u16>,
2225}
2226
2227#[allow(clippy::absurd_extreme_comparisons)]
2228const _: () =
2229    assert!(std::mem::size_of::<ClassSequenceRuleFixedHeader>() <= ClassSequenceRule::MIN_SIZE);
2230
2231/// Part of [SequenceContextFormat2]
2232#[derive(Clone)]
2233pub struct ClassSequenceRule<'a> {
2234    data: TableDataRef<'a, ClassSequenceRuleFixedHeader>,
2235}
2236
2237#[allow(clippy::needless_lifetimes)]
2238impl<'a> ClassSequenceRule<'a> {
2239    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2240    basic_table_impls!(impl_the_methods);
2241
2242    /// Number of glyphs to be matched
2243    pub fn glyph_count(&self) -> u16 {
2244        self.data.header().glyph_count.get()
2245    }
2246
2247    /// Number of SequenceLookupRecords
2248    pub fn seq_lookup_count(&self) -> u16 {
2249        self.data.header().seq_lookup_count.get()
2250    }
2251
2252    /// Sequence of classes to be matched to the input glyph sequence,
2253    /// beginning with the second glyph position
2254    pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
2255        let range = self.input_sequence_byte_range();
2256        self.offset_data()
2257            .read_array(range)
2258            .ok()
2259            .unwrap_or_default()
2260    }
2261
2262    /// Array of SequenceLookupRecords
2263    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2264        let range = self.seq_lookup_records_byte_range();
2265        self.offset_data()
2266            .read_array(range)
2267            .ok()
2268            .unwrap_or_default()
2269    }
2270
2271    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2272        let start = 0;
2273        let end = start + u16::RAW_BYTE_LEN;
2274        start..end
2275    }
2276
2277    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2278        let start = self.glyph_count_byte_range().end;
2279        let end = start + u16::RAW_BYTE_LEN;
2280        start..end
2281    }
2282
2283    pub fn input_sequence_byte_range(&self) -> Range<usize> {
2284        let glyph_count = self.glyph_count();
2285        let start = self.seq_lookup_count_byte_range().end;
2286        let end =
2287            start + (transforms::subtract(glyph_count, 1_usize)).saturating_mul(u16::RAW_BYTE_LEN);
2288        start..end
2289    }
2290
2291    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2292        let seq_lookup_count = self.seq_lookup_count();
2293        let start = self.input_sequence_byte_range().end;
2294        let end = start
2295            + (transforms::to_usize(seq_lookup_count))
2296                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
2297        start..end
2298    }
2299}
2300
2301const _: () = assert!(FontData::default_data_long_enough(
2302    ClassSequenceRule::MIN_SIZE
2303));
2304
2305impl Default for ClassSequenceRule<'_> {
2306    fn default() -> Self {
2307        Self {
2308            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2309                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2310        }
2311    }
2312}
2313
2314impl Format<u16> for SequenceContextFormat3<'_> {
2315    const FORMAT: u16 = 3;
2316}
2317
2318impl<'a> MinByteRange<'a> for SequenceContextFormat3<'a> {
2319    fn min_byte_range(&self) -> Range<usize> {
2320        0..self.seq_lookup_records_byte_range().end
2321    }
2322    fn min_table_bytes(&self) -> &'a [u8] {
2323        let range = self.min_byte_range();
2324        self.offset_data().as_bytes().get(range).unwrap_or_default()
2325    }
2326}
2327
2328impl ReadArgs for SequenceContextFormat3<'_> {
2329    type Args = ();
2330}
2331
2332impl<'a> FontRead<'a> for SequenceContextFormat3<'a> {
2333    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2334        #[allow(clippy::absurd_extreme_comparisons)]
2335        if data.len() < Self::MIN_SIZE {
2336            return Err(ReadError::OutOfBounds);
2337        }
2338        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2339            return Err(ReadError::OutOfBounds);
2340        };
2341        Ok(Self { data })
2342    }
2343}
2344
2345/// The fields at the start of the table, which are read directly.
2346#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2347#[repr(C, packed)]
2348#[doc(hidden)]
2349pub struct SequenceContextFormat3FixedHeader {
2350    pub format: BigEndian<u16>,
2351    pub glyph_count: BigEndian<u16>,
2352    pub seq_lookup_count: BigEndian<u16>,
2353}
2354
2355#[allow(clippy::absurd_extreme_comparisons)]
2356const _: () = assert!(
2357    std::mem::size_of::<SequenceContextFormat3FixedHeader>() <= SequenceContextFormat3::MIN_SIZE
2358);
2359
2360/// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts)
2361#[derive(Clone)]
2362pub struct SequenceContextFormat3<'a> {
2363    data: TableDataRef<'a, SequenceContextFormat3FixedHeader>,
2364}
2365
2366#[allow(clippy::needless_lifetimes)]
2367impl<'a> SequenceContextFormat3<'a> {
2368    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2369    basic_table_impls!(impl_the_methods);
2370
2371    /// Format identifier: format = 3
2372    pub fn format(&self) -> u16 {
2373        self.data.header().format.get()
2374    }
2375
2376    /// Number of glyphs in the input sequence
2377    pub fn glyph_count(&self) -> u16 {
2378        self.data.header().glyph_count.get()
2379    }
2380
2381    /// Number of SequenceLookupRecords
2382    pub fn seq_lookup_count(&self) -> u16 {
2383        self.data.header().seq_lookup_count.get()
2384    }
2385
2386    /// Array of offsets to Coverage tables, from beginning of
2387    /// SequenceContextFormat3 subtable
2388    pub fn coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
2389        let range = self.coverage_offsets_byte_range();
2390        self.offset_data()
2391            .read_array(range)
2392            .ok()
2393            .unwrap_or_default()
2394    }
2395
2396    /// A dynamically resolving wrapper for [`coverage_offsets`][Self::coverage_offsets].
2397    pub fn coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
2398        let data = self.offset_data();
2399        let offsets = self.coverage_offsets();
2400        ArrayOfOffsets::new(offsets, data, ())
2401    }
2402
2403    /// Array of SequenceLookupRecords
2404    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2405        let range = self.seq_lookup_records_byte_range();
2406        self.offset_data()
2407            .read_array(range)
2408            .ok()
2409            .unwrap_or_default()
2410    }
2411
2412    pub fn format_byte_range(&self) -> Range<usize> {
2413        let start = 0;
2414        let end = start + u16::RAW_BYTE_LEN;
2415        start..end
2416    }
2417
2418    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2419        let start = self.format_byte_range().end;
2420        let end = start + u16::RAW_BYTE_LEN;
2421        start..end
2422    }
2423
2424    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2425        let start = self.glyph_count_byte_range().end;
2426        let end = start + u16::RAW_BYTE_LEN;
2427        start..end
2428    }
2429
2430    pub fn coverage_offsets_byte_range(&self) -> Range<usize> {
2431        let glyph_count = self.glyph_count();
2432        let start = self.seq_lookup_count_byte_range().end;
2433        let end =
2434            start + (transforms::to_usize(glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
2435        start..end
2436    }
2437
2438    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2439        let seq_lookup_count = self.seq_lookup_count();
2440        let start = self.coverage_offsets_byte_range().end;
2441        let end = start
2442            + (transforms::to_usize(seq_lookup_count))
2443                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
2444        start..end
2445    }
2446}
2447
2448#[derive(Clone)]
2449pub enum SequenceContext<'a> {
2450    Format1(SequenceContextFormat1<'a>),
2451    Format2(SequenceContextFormat2<'a>),
2452    Format3(SequenceContextFormat3<'a>),
2453    Format4(SequenceContextFormat4<'a>),
2454    Format5(SequenceContextFormat5<'a>),
2455    Format6(SequenceContextFormat6<'a>),
2456}
2457
2458impl Default for SequenceContext<'_> {
2459    fn default() -> Self {
2460        Self::Format1(Default::default())
2461    }
2462}
2463
2464impl<'a> SequenceContext<'a> {
2465    ///Return the `FontData` used to resolve offsets for this table.
2466    pub fn offset_data(&self) -> FontData<'a> {
2467        match self {
2468            Self::Format1(item) => item.offset_data(),
2469            Self::Format2(item) => item.offset_data(),
2470            Self::Format3(item) => item.offset_data(),
2471            Self::Format4(item) => item.offset_data(),
2472            Self::Format5(item) => item.offset_data(),
2473            Self::Format6(item) => item.offset_data(),
2474        }
2475    }
2476
2477    /// Format identifier: format = 1
2478    pub fn format(&self) -> u16 {
2479        match self {
2480            Self::Format1(item) => item.format(),
2481            Self::Format2(item) => item.format(),
2482            Self::Format3(item) => item.format(),
2483            Self::Format4(item) => item.format(),
2484            Self::Format5(item) => item.format(),
2485            Self::Format6(item) => item.format(),
2486        }
2487    }
2488}
2489
2490impl ReadArgs for SequenceContext<'_> {
2491    type Args = ();
2492}
2493
2494impl<'a> FontRead<'a> for SequenceContext<'a> {
2495    #[inline]
2496    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2497        let format: u16 = data.read_at(0usize)?;
2498        match format {
2499            SequenceContextFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
2500            SequenceContextFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
2501            SequenceContextFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
2502            SequenceContextFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
2503            SequenceContextFormat5::FORMAT => Ok(Self::Format5(FontRead::read(data)?)),
2504            SequenceContextFormat6::FORMAT => Ok(Self::Format6(FontRead::read(data)?)),
2505            other => Err(ReadError::InvalidFormat(other.into())),
2506        }
2507    }
2508}
2509
2510impl<'a> MinByteRange<'a> for SequenceContext<'a> {
2511    fn min_byte_range(&self) -> Range<usize> {
2512        match self {
2513            Self::Format1(item) => item.min_byte_range(),
2514            Self::Format2(item) => item.min_byte_range(),
2515            Self::Format3(item) => item.min_byte_range(),
2516            Self::Format4(item) => item.min_byte_range(),
2517            Self::Format5(item) => item.min_byte_range(),
2518            Self::Format6(item) => item.min_byte_range(),
2519        }
2520    }
2521    fn min_table_bytes(&self) -> &'a [u8] {
2522        match self {
2523            Self::Format1(item) => item.min_table_bytes(),
2524            Self::Format2(item) => item.min_table_bytes(),
2525            Self::Format3(item) => item.min_table_bytes(),
2526            Self::Format4(item) => item.min_table_bytes(),
2527            Self::Format5(item) => item.min_table_bytes(),
2528            Self::Format6(item) => item.min_table_bytes(),
2529        }
2530    }
2531}
2532
2533impl Format<u16> for ChainedSequenceContextFormat1<'_> {
2534    const FORMAT: u16 = 1;
2535}
2536
2537impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat1<'a> {
2538    fn min_byte_range(&self) -> Range<usize> {
2539        0..self.chained_seq_rule_set_offsets_byte_range().end
2540    }
2541    fn min_table_bytes(&self) -> &'a [u8] {
2542        let range = self.min_byte_range();
2543        self.offset_data().as_bytes().get(range).unwrap_or_default()
2544    }
2545}
2546
2547impl ReadArgs for ChainedSequenceContextFormat1<'_> {
2548    type Args = ();
2549}
2550
2551impl<'a> FontRead<'a> for ChainedSequenceContextFormat1<'a> {
2552    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2553        #[allow(clippy::absurd_extreme_comparisons)]
2554        if data.len() < Self::MIN_SIZE {
2555            return Err(ReadError::OutOfBounds);
2556        }
2557        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2558            return Err(ReadError::OutOfBounds);
2559        };
2560        Ok(Self { data })
2561    }
2562}
2563
2564/// The fields at the start of the table, which are read directly.
2565#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2566#[repr(C, packed)]
2567#[doc(hidden)]
2568pub struct ChainedSequenceContextFormat1FixedHeader {
2569    pub format: BigEndian<u16>,
2570    pub coverage_offset: BigEndian<Offset16>,
2571    pub chained_seq_rule_set_count: BigEndian<u16>,
2572}
2573
2574#[allow(clippy::absurd_extreme_comparisons)]
2575const _: () = assert!(
2576    std::mem::size_of::<ChainedSequenceContextFormat1FixedHeader>()
2577        <= ChainedSequenceContextFormat1::MIN_SIZE
2578);
2579
2580/// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts)
2581#[derive(Clone)]
2582pub struct ChainedSequenceContextFormat1<'a> {
2583    data: TableDataRef<'a, ChainedSequenceContextFormat1FixedHeader>,
2584}
2585
2586#[allow(clippy::needless_lifetimes)]
2587impl<'a> ChainedSequenceContextFormat1<'a> {
2588    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2589    basic_table_impls!(impl_the_methods);
2590
2591    /// Format identifier: format = 1
2592    pub fn format(&self) -> u16 {
2593        self.data.header().format.get()
2594    }
2595
2596    /// Offset to Coverage table, from beginning of
2597    /// ChainSequenceContextFormat1 table
2598    pub fn coverage_offset(&self) -> Offset16 {
2599        self.data.header().coverage_offset.get()
2600    }
2601
2602    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2603    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2604        let data = self.offset_data();
2605        self.coverage_offset().resolve(data)
2606    }
2607
2608    /// Number of ChainedSequenceRuleSet tables
2609    pub fn chained_seq_rule_set_count(&self) -> u16 {
2610        self.data.header().chained_seq_rule_set_count.get()
2611    }
2612
2613    /// Array of offsets to ChainedSeqRuleSet tables, from beginning of
2614    /// ChainedSequenceContextFormat1 table (may be NULL)
2615    pub fn chained_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2616        let range = self.chained_seq_rule_set_offsets_byte_range();
2617        self.offset_data()
2618            .read_array(range)
2619            .ok()
2620            .unwrap_or_default()
2621    }
2622
2623    /// A dynamically resolving wrapper for [`chained_seq_rule_set_offsets`][Self::chained_seq_rule_set_offsets].
2624    pub fn chained_seq_rule_sets(
2625        &self,
2626    ) -> ArrayOfNullableOffsets<'a, ChainedSequenceRuleSet<'a>, Offset16> {
2627        let data = self.offset_data();
2628        let offsets = self.chained_seq_rule_set_offsets();
2629        ArrayOfNullableOffsets::new(offsets, data, ())
2630    }
2631
2632    pub fn format_byte_range(&self) -> Range<usize> {
2633        let start = 0;
2634        let end = start + u16::RAW_BYTE_LEN;
2635        start..end
2636    }
2637
2638    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2639        let start = self.format_byte_range().end;
2640        let end = start + Offset16::RAW_BYTE_LEN;
2641        start..end
2642    }
2643
2644    pub fn chained_seq_rule_set_count_byte_range(&self) -> Range<usize> {
2645        let start = self.coverage_offset_byte_range().end;
2646        let end = start + u16::RAW_BYTE_LEN;
2647        start..end
2648    }
2649
2650    pub fn chained_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
2651        let chained_seq_rule_set_count = self.chained_seq_rule_set_count();
2652        let start = self.chained_seq_rule_set_count_byte_range().end;
2653        let end = start
2654            + (transforms::to_usize(chained_seq_rule_set_count))
2655                .saturating_mul(Offset16::RAW_BYTE_LEN);
2656        start..end
2657    }
2658}
2659
2660const _: () = assert!(FontData::default_data_long_enough(
2661    ChainedSequenceContextFormat1::MIN_SIZE
2662));
2663
2664impl Default for ChainedSequenceContextFormat1<'_> {
2665    fn default() -> Self {
2666        Self {
2667            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
2668                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2669        }
2670    }
2671}
2672
2673impl<'a> MinByteRange<'a> for ChainedSequenceRuleSet<'a> {
2674    fn min_byte_range(&self) -> Range<usize> {
2675        0..self.chained_seq_rule_offsets_byte_range().end
2676    }
2677    fn min_table_bytes(&self) -> &'a [u8] {
2678        let range = self.min_byte_range();
2679        self.offset_data().as_bytes().get(range).unwrap_or_default()
2680    }
2681}
2682
2683impl ReadArgs for ChainedSequenceRuleSet<'_> {
2684    type Args = ();
2685}
2686
2687impl<'a> FontRead<'a> for ChainedSequenceRuleSet<'a> {
2688    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2689        #[allow(clippy::absurd_extreme_comparisons)]
2690        if data.len() < Self::MIN_SIZE {
2691            return Err(ReadError::OutOfBounds);
2692        }
2693        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2694            return Err(ReadError::OutOfBounds);
2695        };
2696        Ok(Self { data })
2697    }
2698}
2699
2700/// The fields at the start of the table, which are read directly.
2701#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2702#[repr(C, packed)]
2703#[doc(hidden)]
2704pub struct ChainedSequenceRuleSetFixedHeader {
2705    pub chained_seq_rule_count: BigEndian<u16>,
2706}
2707
2708#[allow(clippy::absurd_extreme_comparisons)]
2709const _: () = assert!(
2710    std::mem::size_of::<ChainedSequenceRuleSetFixedHeader>() <= ChainedSequenceRuleSet::MIN_SIZE
2711);
2712
2713/// Part of [ChainedSequenceContextFormat1]
2714#[derive(Clone)]
2715pub struct ChainedSequenceRuleSet<'a> {
2716    data: TableDataRef<'a, ChainedSequenceRuleSetFixedHeader>,
2717}
2718
2719#[allow(clippy::needless_lifetimes)]
2720impl<'a> ChainedSequenceRuleSet<'a> {
2721    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2722    basic_table_impls!(impl_the_methods);
2723
2724    /// Number of ChainedSequenceRule tables
2725    pub fn chained_seq_rule_count(&self) -> u16 {
2726        self.data.header().chained_seq_rule_count.get()
2727    }
2728
2729    /// Array of offsets to ChainedSequenceRule tables, from beginning
2730    /// of ChainedSequenceRuleSet table
2731    pub fn chained_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
2732        let range = self.chained_seq_rule_offsets_byte_range();
2733        self.offset_data()
2734            .read_array(range)
2735            .ok()
2736            .unwrap_or_default()
2737    }
2738
2739    /// A dynamically resolving wrapper for [`chained_seq_rule_offsets`][Self::chained_seq_rule_offsets].
2740    pub fn chained_seq_rules(&self) -> ArrayOfOffsets<'a, ChainedSequenceRule<'a>, Offset16> {
2741        let data = self.offset_data();
2742        let offsets = self.chained_seq_rule_offsets();
2743        ArrayOfOffsets::new(offsets, data, ())
2744    }
2745
2746    pub fn chained_seq_rule_count_byte_range(&self) -> Range<usize> {
2747        let start = 0;
2748        let end = start + u16::RAW_BYTE_LEN;
2749        start..end
2750    }
2751
2752    pub fn chained_seq_rule_offsets_byte_range(&self) -> Range<usize> {
2753        let chained_seq_rule_count = self.chained_seq_rule_count();
2754        let start = self.chained_seq_rule_count_byte_range().end;
2755        let end = start
2756            + (transforms::to_usize(chained_seq_rule_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
2757        start..end
2758    }
2759}
2760
2761const _: () = assert!(FontData::default_data_long_enough(
2762    ChainedSequenceRuleSet::MIN_SIZE
2763));
2764
2765impl Default for ChainedSequenceRuleSet<'_> {
2766    fn default() -> Self {
2767        Self {
2768            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2769                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2770        }
2771    }
2772}
2773
2774impl<'a> MinByteRange<'a> for ChainedSequenceRule<'a> {
2775    fn min_byte_range(&self) -> Range<usize> {
2776        0..self.seq_lookup_records_byte_range().end
2777    }
2778    fn min_table_bytes(&self) -> &'a [u8] {
2779        let range = self.min_byte_range();
2780        self.offset_data().as_bytes().get(range).unwrap_or_default()
2781    }
2782}
2783
2784impl ReadArgs for ChainedSequenceRule<'_> {
2785    type Args = ();
2786}
2787
2788impl<'a> FontRead<'a> for ChainedSequenceRule<'a> {
2789    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2790        #[allow(clippy::absurd_extreme_comparisons)]
2791        if data.len() < Self::MIN_SIZE {
2792            return Err(ReadError::OutOfBounds);
2793        }
2794        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2795            return Err(ReadError::OutOfBounds);
2796        };
2797        Ok(Self { data })
2798    }
2799}
2800
2801/// The fields at the start of the table, which are read directly.
2802#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2803#[repr(C, packed)]
2804#[doc(hidden)]
2805pub struct ChainedSequenceRuleFixedHeader {
2806    pub backtrack_glyph_count: BigEndian<u16>,
2807}
2808
2809#[allow(clippy::absurd_extreme_comparisons)]
2810const _: () =
2811    assert!(std::mem::size_of::<ChainedSequenceRuleFixedHeader>() <= ChainedSequenceRule::MIN_SIZE);
2812
2813/// Part of [ChainedSequenceContextFormat1]
2814#[derive(Clone)]
2815pub struct ChainedSequenceRule<'a> {
2816    data: TableDataRef<'a, ChainedSequenceRuleFixedHeader>,
2817}
2818
2819#[allow(clippy::needless_lifetimes)]
2820impl<'a> ChainedSequenceRule<'a> {
2821    pub const MIN_SIZE: usize =
2822        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2823    basic_table_impls!(impl_the_methods);
2824
2825    /// Number of glyphs in the backtrack sequence
2826    pub fn backtrack_glyph_count(&self) -> u16 {
2827        self.data.header().backtrack_glyph_count.get()
2828    }
2829
2830    /// Array of backtrack glyph IDs
2831    pub fn backtrack_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2832        let range = self.backtrack_sequence_byte_range();
2833        self.offset_data()
2834            .read_array(range)
2835            .ok()
2836            .unwrap_or_default()
2837    }
2838
2839    /// Number of glyphs in the input sequence
2840    pub fn input_glyph_count(&self) -> u16 {
2841        let range = self.input_glyph_count_byte_range();
2842        self.offset_data()
2843            .read_at(range.start)
2844            .ok()
2845            .unwrap_or_default()
2846    }
2847
2848    /// Array of input glyph IDs—start with second glyph
2849    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2850        let range = self.input_sequence_byte_range();
2851        self.offset_data()
2852            .read_array(range)
2853            .ok()
2854            .unwrap_or_default()
2855    }
2856
2857    /// Number of glyphs in the lookahead sequence
2858    pub fn lookahead_glyph_count(&self) -> u16 {
2859        let range = self.lookahead_glyph_count_byte_range();
2860        self.offset_data()
2861            .read_at(range.start)
2862            .ok()
2863            .unwrap_or_default()
2864    }
2865
2866    /// Array of lookahead glyph IDs
2867    pub fn lookahead_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2868        let range = self.lookahead_sequence_byte_range();
2869        self.offset_data()
2870            .read_array(range)
2871            .ok()
2872            .unwrap_or_default()
2873    }
2874
2875    /// Number of SequenceLookupRecords
2876    pub fn seq_lookup_count(&self) -> u16 {
2877        let range = self.seq_lookup_count_byte_range();
2878        self.offset_data()
2879            .read_at(range.start)
2880            .ok()
2881            .unwrap_or_default()
2882    }
2883
2884    /// Array of SequenceLookupRecords
2885    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2886        let range = self.seq_lookup_records_byte_range();
2887        self.offset_data()
2888            .read_array(range)
2889            .ok()
2890            .unwrap_or_default()
2891    }
2892
2893    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
2894        let start = 0;
2895        let end = start + u16::RAW_BYTE_LEN;
2896        start..end
2897    }
2898
2899    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
2900        let backtrack_glyph_count = self.backtrack_glyph_count();
2901        let start = self.backtrack_glyph_count_byte_range().end;
2902        let end = start
2903            + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
2904        start..end
2905    }
2906
2907    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
2908        let start = self.backtrack_sequence_byte_range().end;
2909        let end = start + u16::RAW_BYTE_LEN;
2910        start..end
2911    }
2912
2913    pub fn input_sequence_byte_range(&self) -> Range<usize> {
2914        let input_glyph_count = self.input_glyph_count();
2915        let start = self.input_glyph_count_byte_range().end;
2916        let end = start
2917            + (transforms::subtract(input_glyph_count, 1_usize))
2918                .saturating_mul(GlyphId16::RAW_BYTE_LEN);
2919        start..end
2920    }
2921
2922    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
2923        let start = self.input_sequence_byte_range().end;
2924        let end = start + u16::RAW_BYTE_LEN;
2925        start..end
2926    }
2927
2928    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
2929        let lookahead_glyph_count = self.lookahead_glyph_count();
2930        let start = self.lookahead_glyph_count_byte_range().end;
2931        let end = start
2932            + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
2933        start..end
2934    }
2935
2936    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2937        let start = self.lookahead_sequence_byte_range().end;
2938        let end = start + u16::RAW_BYTE_LEN;
2939        start..end
2940    }
2941
2942    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2943        let seq_lookup_count = self.seq_lookup_count();
2944        let start = self.seq_lookup_count_byte_range().end;
2945        let end = start
2946            + (transforms::to_usize(seq_lookup_count))
2947                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
2948        start..end
2949    }
2950}
2951
2952const _: () = assert!(FontData::default_data_long_enough(
2953    ChainedSequenceRule::MIN_SIZE
2954));
2955
2956impl Default for ChainedSequenceRule<'_> {
2957    fn default() -> Self {
2958        Self {
2959            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2960                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2961        }
2962    }
2963}
2964
2965impl Format<u16> for ChainedSequenceContextFormat2<'_> {
2966    const FORMAT: u16 = 2;
2967}
2968
2969impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat2<'a> {
2970    fn min_byte_range(&self) -> Range<usize> {
2971        0..self.chained_class_seq_rule_set_offsets_byte_range().end
2972    }
2973    fn min_table_bytes(&self) -> &'a [u8] {
2974        let range = self.min_byte_range();
2975        self.offset_data().as_bytes().get(range).unwrap_or_default()
2976    }
2977}
2978
2979impl ReadArgs for ChainedSequenceContextFormat2<'_> {
2980    type Args = ();
2981}
2982
2983impl<'a> FontRead<'a> for ChainedSequenceContextFormat2<'a> {
2984    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2985        #[allow(clippy::absurd_extreme_comparisons)]
2986        if data.len() < Self::MIN_SIZE {
2987            return Err(ReadError::OutOfBounds);
2988        }
2989        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2990            return Err(ReadError::OutOfBounds);
2991        };
2992        Ok(Self { data })
2993    }
2994}
2995
2996/// The fields at the start of the table, which are read directly.
2997#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2998#[repr(C, packed)]
2999#[doc(hidden)]
3000pub struct ChainedSequenceContextFormat2FixedHeader {
3001    pub format: BigEndian<u16>,
3002    pub coverage_offset: BigEndian<Offset16>,
3003    pub backtrack_class_def_offset: BigEndian<Offset16>,
3004    pub input_class_def_offset: BigEndian<Offset16>,
3005    pub lookahead_class_def_offset: BigEndian<Offset16>,
3006    pub chained_class_seq_rule_set_count: BigEndian<u16>,
3007}
3008
3009#[allow(clippy::absurd_extreme_comparisons)]
3010const _: () = assert!(
3011    std::mem::size_of::<ChainedSequenceContextFormat2FixedHeader>()
3012        <= ChainedSequenceContextFormat2::MIN_SIZE
3013);
3014
3015/// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts)
3016#[derive(Clone)]
3017pub struct ChainedSequenceContextFormat2<'a> {
3018    data: TableDataRef<'a, ChainedSequenceContextFormat2FixedHeader>,
3019}
3020
3021#[allow(clippy::needless_lifetimes)]
3022impl<'a> ChainedSequenceContextFormat2<'a> {
3023    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3024        + Offset16::RAW_BYTE_LEN
3025        + Offset16::RAW_BYTE_LEN
3026        + Offset16::RAW_BYTE_LEN
3027        + Offset16::RAW_BYTE_LEN
3028        + u16::RAW_BYTE_LEN);
3029    basic_table_impls!(impl_the_methods);
3030
3031    /// Format identifier: format = 2
3032    pub fn format(&self) -> u16 {
3033        self.data.header().format.get()
3034    }
3035
3036    /// Offset to Coverage table, from beginning of
3037    /// ChainedSequenceContextFormat2 table
3038    pub fn coverage_offset(&self) -> Offset16 {
3039        self.data.header().coverage_offset.get()
3040    }
3041
3042    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
3043    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3044        let data = self.offset_data();
3045        self.coverage_offset().resolve(data)
3046    }
3047
3048    /// Offset to ClassDef table containing backtrack sequence context,
3049    /// from beginning of ChainedSequenceContextFormat2 table
3050    pub fn backtrack_class_def_offset(&self) -> Offset16 {
3051        self.data.header().backtrack_class_def_offset.get()
3052    }
3053
3054    /// Attempt to resolve [`backtrack_class_def_offset`][Self::backtrack_class_def_offset].
3055    pub fn backtrack_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3056        let data = self.offset_data();
3057        self.backtrack_class_def_offset().resolve(data)
3058    }
3059
3060    /// Offset to ClassDef table containing input sequence context,
3061    /// from beginning of ChainedSequenceContextFormat2 table
3062    pub fn input_class_def_offset(&self) -> Offset16 {
3063        self.data.header().input_class_def_offset.get()
3064    }
3065
3066    /// Attempt to resolve [`input_class_def_offset`][Self::input_class_def_offset].
3067    pub fn input_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3068        let data = self.offset_data();
3069        self.input_class_def_offset().resolve(data)
3070    }
3071
3072    /// Offset to ClassDef table containing lookahead sequence context,
3073    /// from beginning of ChainedSequenceContextFormat2 table
3074    pub fn lookahead_class_def_offset(&self) -> Offset16 {
3075        self.data.header().lookahead_class_def_offset.get()
3076    }
3077
3078    /// Attempt to resolve [`lookahead_class_def_offset`][Self::lookahead_class_def_offset].
3079    pub fn lookahead_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3080        let data = self.offset_data();
3081        self.lookahead_class_def_offset().resolve(data)
3082    }
3083
3084    /// Number of ChainedClassSequenceRuleSet tables
3085    pub fn chained_class_seq_rule_set_count(&self) -> u16 {
3086        self.data.header().chained_class_seq_rule_set_count.get()
3087    }
3088
3089    /// Array of offsets to ChainedClassSequenceRuleSet tables, from
3090    /// beginning of ChainedSequenceContextFormat2 table (may be NULL)
3091    pub fn chained_class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3092        let range = self.chained_class_seq_rule_set_offsets_byte_range();
3093        self.offset_data()
3094            .read_array(range)
3095            .ok()
3096            .unwrap_or_default()
3097    }
3098
3099    /// A dynamically resolving wrapper for [`chained_class_seq_rule_set_offsets`][Self::chained_class_seq_rule_set_offsets].
3100    pub fn chained_class_seq_rule_sets(
3101        &self,
3102    ) -> ArrayOfNullableOffsets<'a, ChainedClassSequenceRuleSet<'a>, Offset16> {
3103        let data = self.offset_data();
3104        let offsets = self.chained_class_seq_rule_set_offsets();
3105        ArrayOfNullableOffsets::new(offsets, data, ())
3106    }
3107
3108    pub fn format_byte_range(&self) -> Range<usize> {
3109        let start = 0;
3110        let end = start + u16::RAW_BYTE_LEN;
3111        start..end
3112    }
3113
3114    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
3115        let start = self.format_byte_range().end;
3116        let end = start + Offset16::RAW_BYTE_LEN;
3117        start..end
3118    }
3119
3120    pub fn backtrack_class_def_offset_byte_range(&self) -> Range<usize> {
3121        let start = self.coverage_offset_byte_range().end;
3122        let end = start + Offset16::RAW_BYTE_LEN;
3123        start..end
3124    }
3125
3126    pub fn input_class_def_offset_byte_range(&self) -> Range<usize> {
3127        let start = self.backtrack_class_def_offset_byte_range().end;
3128        let end = start + Offset16::RAW_BYTE_LEN;
3129        start..end
3130    }
3131
3132    pub fn lookahead_class_def_offset_byte_range(&self) -> Range<usize> {
3133        let start = self.input_class_def_offset_byte_range().end;
3134        let end = start + Offset16::RAW_BYTE_LEN;
3135        start..end
3136    }
3137
3138    pub fn chained_class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
3139        let start = self.lookahead_class_def_offset_byte_range().end;
3140        let end = start + u16::RAW_BYTE_LEN;
3141        start..end
3142    }
3143
3144    pub fn chained_class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
3145        let chained_class_seq_rule_set_count = self.chained_class_seq_rule_set_count();
3146        let start = self.chained_class_seq_rule_set_count_byte_range().end;
3147        let end = start
3148            + (transforms::to_usize(chained_class_seq_rule_set_count))
3149                .saturating_mul(Offset16::RAW_BYTE_LEN);
3150        start..end
3151    }
3152}
3153
3154impl<'a> MinByteRange<'a> for ChainedClassSequenceRuleSet<'a> {
3155    fn min_byte_range(&self) -> Range<usize> {
3156        0..self.chained_class_seq_rule_offsets_byte_range().end
3157    }
3158    fn min_table_bytes(&self) -> &'a [u8] {
3159        let range = self.min_byte_range();
3160        self.offset_data().as_bytes().get(range).unwrap_or_default()
3161    }
3162}
3163
3164impl ReadArgs for ChainedClassSequenceRuleSet<'_> {
3165    type Args = ();
3166}
3167
3168impl<'a> FontRead<'a> for ChainedClassSequenceRuleSet<'a> {
3169    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3170        #[allow(clippy::absurd_extreme_comparisons)]
3171        if data.len() < Self::MIN_SIZE {
3172            return Err(ReadError::OutOfBounds);
3173        }
3174        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3175            return Err(ReadError::OutOfBounds);
3176        };
3177        Ok(Self { data })
3178    }
3179}
3180
3181/// The fields at the start of the table, which are read directly.
3182#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3183#[repr(C, packed)]
3184#[doc(hidden)]
3185pub struct ChainedClassSequenceRuleSetFixedHeader {
3186    pub chained_class_seq_rule_count: BigEndian<u16>,
3187}
3188
3189#[allow(clippy::absurd_extreme_comparisons)]
3190const _: () = assert!(
3191    std::mem::size_of::<ChainedClassSequenceRuleSetFixedHeader>()
3192        <= ChainedClassSequenceRuleSet::MIN_SIZE
3193);
3194
3195/// Part of [ChainedSequenceContextFormat2]
3196#[derive(Clone)]
3197pub struct ChainedClassSequenceRuleSet<'a> {
3198    data: TableDataRef<'a, ChainedClassSequenceRuleSetFixedHeader>,
3199}
3200
3201#[allow(clippy::needless_lifetimes)]
3202impl<'a> ChainedClassSequenceRuleSet<'a> {
3203    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3204    basic_table_impls!(impl_the_methods);
3205
3206    /// Number of ChainedClassSequenceRule tables
3207    pub fn chained_class_seq_rule_count(&self) -> u16 {
3208        self.data.header().chained_class_seq_rule_count.get()
3209    }
3210
3211    /// Array of offsets to ChainedClassSequenceRule tables, from
3212    /// beginning of ChainedClassSequenceRuleSet
3213    pub fn chained_class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
3214        let range = self.chained_class_seq_rule_offsets_byte_range();
3215        self.offset_data()
3216            .read_array(range)
3217            .ok()
3218            .unwrap_or_default()
3219    }
3220
3221    /// A dynamically resolving wrapper for [`chained_class_seq_rule_offsets`][Self::chained_class_seq_rule_offsets].
3222    pub fn chained_class_seq_rules(
3223        &self,
3224    ) -> ArrayOfOffsets<'a, ChainedClassSequenceRule<'a>, Offset16> {
3225        let data = self.offset_data();
3226        let offsets = self.chained_class_seq_rule_offsets();
3227        ArrayOfOffsets::new(offsets, data, ())
3228    }
3229
3230    pub fn chained_class_seq_rule_count_byte_range(&self) -> Range<usize> {
3231        let start = 0;
3232        let end = start + u16::RAW_BYTE_LEN;
3233        start..end
3234    }
3235
3236    pub fn chained_class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
3237        let chained_class_seq_rule_count = self.chained_class_seq_rule_count();
3238        let start = self.chained_class_seq_rule_count_byte_range().end;
3239        let end = start
3240            + (transforms::to_usize(chained_class_seq_rule_count))
3241                .saturating_mul(Offset16::RAW_BYTE_LEN);
3242        start..end
3243    }
3244}
3245
3246const _: () = assert!(FontData::default_data_long_enough(
3247    ChainedClassSequenceRuleSet::MIN_SIZE
3248));
3249
3250impl Default for ChainedClassSequenceRuleSet<'_> {
3251    fn default() -> Self {
3252        Self {
3253            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3254                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3255        }
3256    }
3257}
3258
3259impl<'a> MinByteRange<'a> for ChainedClassSequenceRule<'a> {
3260    fn min_byte_range(&self) -> Range<usize> {
3261        0..self.seq_lookup_records_byte_range().end
3262    }
3263    fn min_table_bytes(&self) -> &'a [u8] {
3264        let range = self.min_byte_range();
3265        self.offset_data().as_bytes().get(range).unwrap_or_default()
3266    }
3267}
3268
3269impl ReadArgs for ChainedClassSequenceRule<'_> {
3270    type Args = ();
3271}
3272
3273impl<'a> FontRead<'a> for ChainedClassSequenceRule<'a> {
3274    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3275        #[allow(clippy::absurd_extreme_comparisons)]
3276        if data.len() < Self::MIN_SIZE {
3277            return Err(ReadError::OutOfBounds);
3278        }
3279        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3280            return Err(ReadError::OutOfBounds);
3281        };
3282        Ok(Self { data })
3283    }
3284}
3285
3286/// The fields at the start of the table, which are read directly.
3287#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3288#[repr(C, packed)]
3289#[doc(hidden)]
3290pub struct ChainedClassSequenceRuleFixedHeader {
3291    pub backtrack_glyph_count: BigEndian<u16>,
3292}
3293
3294#[allow(clippy::absurd_extreme_comparisons)]
3295const _: () = assert!(
3296    std::mem::size_of::<ChainedClassSequenceRuleFixedHeader>()
3297        <= ChainedClassSequenceRule::MIN_SIZE
3298);
3299
3300/// Part of [ChainedSequenceContextFormat2]
3301#[derive(Clone)]
3302pub struct ChainedClassSequenceRule<'a> {
3303    data: TableDataRef<'a, ChainedClassSequenceRuleFixedHeader>,
3304}
3305
3306#[allow(clippy::needless_lifetimes)]
3307impl<'a> ChainedClassSequenceRule<'a> {
3308    pub const MIN_SIZE: usize =
3309        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
3310    basic_table_impls!(impl_the_methods);
3311
3312    /// Number of glyphs in the backtrack sequence
3313    pub fn backtrack_glyph_count(&self) -> u16 {
3314        self.data.header().backtrack_glyph_count.get()
3315    }
3316
3317    /// Array of backtrack-sequence classes
3318    pub fn backtrack_sequence(&self) -> &'a [BigEndian<u16>] {
3319        let range = self.backtrack_sequence_byte_range();
3320        self.offset_data()
3321            .read_array(range)
3322            .ok()
3323            .unwrap_or_default()
3324    }
3325
3326    /// Total number of glyphs in the input sequence
3327    pub fn input_glyph_count(&self) -> u16 {
3328        let range = self.input_glyph_count_byte_range();
3329        self.offset_data()
3330            .read_at(range.start)
3331            .ok()
3332            .unwrap_or_default()
3333    }
3334
3335    /// Array of input sequence classes, beginning with the second
3336    /// glyph position
3337    pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
3338        let range = self.input_sequence_byte_range();
3339        self.offset_data()
3340            .read_array(range)
3341            .ok()
3342            .unwrap_or_default()
3343    }
3344
3345    /// Number of glyphs in the lookahead sequence
3346    pub fn lookahead_glyph_count(&self) -> u16 {
3347        let range = self.lookahead_glyph_count_byte_range();
3348        self.offset_data()
3349            .read_at(range.start)
3350            .ok()
3351            .unwrap_or_default()
3352    }
3353
3354    /// Array of lookahead-sequence classes
3355    pub fn lookahead_sequence(&self) -> &'a [BigEndian<u16>] {
3356        let range = self.lookahead_sequence_byte_range();
3357        self.offset_data()
3358            .read_array(range)
3359            .ok()
3360            .unwrap_or_default()
3361    }
3362
3363    /// Number of SequenceLookupRecords
3364    pub fn seq_lookup_count(&self) -> u16 {
3365        let range = self.seq_lookup_count_byte_range();
3366        self.offset_data()
3367            .read_at(range.start)
3368            .ok()
3369            .unwrap_or_default()
3370    }
3371
3372    /// Array of SequenceLookupRecords
3373    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3374        let range = self.seq_lookup_records_byte_range();
3375        self.offset_data()
3376            .read_array(range)
3377            .ok()
3378            .unwrap_or_default()
3379    }
3380
3381    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3382        let start = 0;
3383        let end = start + u16::RAW_BYTE_LEN;
3384        start..end
3385    }
3386
3387    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
3388        let backtrack_glyph_count = self.backtrack_glyph_count();
3389        let start = self.backtrack_glyph_count_byte_range().end;
3390        let end =
3391            start + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(u16::RAW_BYTE_LEN);
3392        start..end
3393    }
3394
3395    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3396        let start = self.backtrack_sequence_byte_range().end;
3397        let end = start + u16::RAW_BYTE_LEN;
3398        start..end
3399    }
3400
3401    pub fn input_sequence_byte_range(&self) -> Range<usize> {
3402        let input_glyph_count = self.input_glyph_count();
3403        let start = self.input_glyph_count_byte_range().end;
3404        let end = start
3405            + (transforms::subtract(input_glyph_count, 1_usize)).saturating_mul(u16::RAW_BYTE_LEN);
3406        start..end
3407    }
3408
3409    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3410        let start = self.input_sequence_byte_range().end;
3411        let end = start + u16::RAW_BYTE_LEN;
3412        start..end
3413    }
3414
3415    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
3416        let lookahead_glyph_count = self.lookahead_glyph_count();
3417        let start = self.lookahead_glyph_count_byte_range().end;
3418        let end =
3419            start + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(u16::RAW_BYTE_LEN);
3420        start..end
3421    }
3422
3423    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3424        let start = self.lookahead_sequence_byte_range().end;
3425        let end = start + u16::RAW_BYTE_LEN;
3426        start..end
3427    }
3428
3429    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3430        let seq_lookup_count = self.seq_lookup_count();
3431        let start = self.seq_lookup_count_byte_range().end;
3432        let end = start
3433            + (transforms::to_usize(seq_lookup_count))
3434                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
3435        start..end
3436    }
3437}
3438
3439const _: () = assert!(FontData::default_data_long_enough(
3440    ChainedClassSequenceRule::MIN_SIZE
3441));
3442
3443impl Default for ChainedClassSequenceRule<'_> {
3444    fn default() -> Self {
3445        Self {
3446            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3447                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3448        }
3449    }
3450}
3451
3452impl Format<u16> for ChainedSequenceContextFormat3<'_> {
3453    const FORMAT: u16 = 3;
3454}
3455
3456impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat3<'a> {
3457    fn min_byte_range(&self) -> Range<usize> {
3458        0..self.seq_lookup_records_byte_range().end
3459    }
3460    fn min_table_bytes(&self) -> &'a [u8] {
3461        let range = self.min_byte_range();
3462        self.offset_data().as_bytes().get(range).unwrap_or_default()
3463    }
3464}
3465
3466impl ReadArgs for ChainedSequenceContextFormat3<'_> {
3467    type Args = ();
3468}
3469
3470impl<'a> FontRead<'a> for ChainedSequenceContextFormat3<'a> {
3471    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3472        #[allow(clippy::absurd_extreme_comparisons)]
3473        if data.len() < Self::MIN_SIZE {
3474            return Err(ReadError::OutOfBounds);
3475        }
3476        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3477            return Err(ReadError::OutOfBounds);
3478        };
3479        Ok(Self { data })
3480    }
3481}
3482
3483/// The fields at the start of the table, which are read directly.
3484#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3485#[repr(C, packed)]
3486#[doc(hidden)]
3487pub struct ChainedSequenceContextFormat3FixedHeader {
3488    pub format: BigEndian<u16>,
3489    pub backtrack_glyph_count: BigEndian<u16>,
3490}
3491
3492#[allow(clippy::absurd_extreme_comparisons)]
3493const _: () = assert!(
3494    std::mem::size_of::<ChainedSequenceContextFormat3FixedHeader>()
3495        <= ChainedSequenceContextFormat3::MIN_SIZE
3496);
3497
3498/// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts)
3499#[derive(Clone)]
3500pub struct ChainedSequenceContextFormat3<'a> {
3501    data: TableDataRef<'a, ChainedSequenceContextFormat3FixedHeader>,
3502}
3503
3504#[allow(clippy::needless_lifetimes)]
3505impl<'a> ChainedSequenceContextFormat3<'a> {
3506    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3507        + u16::RAW_BYTE_LEN
3508        + u16::RAW_BYTE_LEN
3509        + u16::RAW_BYTE_LEN
3510        + u16::RAW_BYTE_LEN);
3511    basic_table_impls!(impl_the_methods);
3512
3513    /// Format identifier: format = 3
3514    pub fn format(&self) -> u16 {
3515        self.data.header().format.get()
3516    }
3517
3518    /// Number of glyphs in the backtrack sequence
3519    pub fn backtrack_glyph_count(&self) -> u16 {
3520        self.data.header().backtrack_glyph_count.get()
3521    }
3522
3523    /// Array of offsets to coverage tables for the backtrack sequence
3524    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3525        let range = self.backtrack_coverage_offsets_byte_range();
3526        self.offset_data()
3527            .read_array(range)
3528            .ok()
3529            .unwrap_or_default()
3530    }
3531
3532    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
3533    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3534        let data = self.offset_data();
3535        let offsets = self.backtrack_coverage_offsets();
3536        ArrayOfOffsets::new(offsets, data, ())
3537    }
3538
3539    /// Number of glyphs in the input sequence
3540    pub fn input_glyph_count(&self) -> u16 {
3541        let range = self.input_glyph_count_byte_range();
3542        self.offset_data()
3543            .read_at(range.start)
3544            .ok()
3545            .unwrap_or_default()
3546    }
3547
3548    /// Array of offsets to coverage tables for the input sequence
3549    pub fn input_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3550        let range = self.input_coverage_offsets_byte_range();
3551        self.offset_data()
3552            .read_array(range)
3553            .ok()
3554            .unwrap_or_default()
3555    }
3556
3557    /// A dynamically resolving wrapper for [`input_coverage_offsets`][Self::input_coverage_offsets].
3558    pub fn input_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3559        let data = self.offset_data();
3560        let offsets = self.input_coverage_offsets();
3561        ArrayOfOffsets::new(offsets, data, ())
3562    }
3563
3564    /// Number of glyphs in the lookahead sequence
3565    pub fn lookahead_glyph_count(&self) -> u16 {
3566        let range = self.lookahead_glyph_count_byte_range();
3567        self.offset_data()
3568            .read_at(range.start)
3569            .ok()
3570            .unwrap_or_default()
3571    }
3572
3573    /// Array of offsets to coverage tables for the lookahead sequence
3574    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3575        let range = self.lookahead_coverage_offsets_byte_range();
3576        self.offset_data()
3577            .read_array(range)
3578            .ok()
3579            .unwrap_or_default()
3580    }
3581
3582    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
3583    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3584        let data = self.offset_data();
3585        let offsets = self.lookahead_coverage_offsets();
3586        ArrayOfOffsets::new(offsets, data, ())
3587    }
3588
3589    /// Number of SequenceLookupRecords
3590    pub fn seq_lookup_count(&self) -> u16 {
3591        let range = self.seq_lookup_count_byte_range();
3592        self.offset_data()
3593            .read_at(range.start)
3594            .ok()
3595            .unwrap_or_default()
3596    }
3597
3598    /// Array of SequenceLookupRecords
3599    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3600        let range = self.seq_lookup_records_byte_range();
3601        self.offset_data()
3602            .read_array(range)
3603            .ok()
3604            .unwrap_or_default()
3605    }
3606
3607    pub fn format_byte_range(&self) -> Range<usize> {
3608        let start = 0;
3609        let end = start + u16::RAW_BYTE_LEN;
3610        start..end
3611    }
3612
3613    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3614        let start = self.format_byte_range().end;
3615        let end = start + u16::RAW_BYTE_LEN;
3616        start..end
3617    }
3618
3619    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
3620        let backtrack_glyph_count = self.backtrack_glyph_count();
3621        let start = self.backtrack_glyph_count_byte_range().end;
3622        let end = start
3623            + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
3624        start..end
3625    }
3626
3627    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3628        let start = self.backtrack_coverage_offsets_byte_range().end;
3629        let end = start + u16::RAW_BYTE_LEN;
3630        start..end
3631    }
3632
3633    pub fn input_coverage_offsets_byte_range(&self) -> Range<usize> {
3634        let input_glyph_count = self.input_glyph_count();
3635        let start = self.input_glyph_count_byte_range().end;
3636        let end = start
3637            + (transforms::to_usize(input_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
3638        start..end
3639    }
3640
3641    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3642        let start = self.input_coverage_offsets_byte_range().end;
3643        let end = start + u16::RAW_BYTE_LEN;
3644        start..end
3645    }
3646
3647    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
3648        let lookahead_glyph_count = self.lookahead_glyph_count();
3649        let start = self.lookahead_glyph_count_byte_range().end;
3650        let end = start
3651            + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
3652        start..end
3653    }
3654
3655    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3656        let start = self.lookahead_coverage_offsets_byte_range().end;
3657        let end = start + u16::RAW_BYTE_LEN;
3658        start..end
3659    }
3660
3661    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3662        let seq_lookup_count = self.seq_lookup_count();
3663        let start = self.seq_lookup_count_byte_range().end;
3664        let end = start
3665            + (transforms::to_usize(seq_lookup_count))
3666                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
3667        start..end
3668    }
3669}
3670
3671#[derive(Clone)]
3672pub enum ChainedSequenceContext<'a> {
3673    Format1(ChainedSequenceContextFormat1<'a>),
3674    Format2(ChainedSequenceContextFormat2<'a>),
3675    Format3(ChainedSequenceContextFormat3<'a>),
3676    Format4(ChainedSequenceContextFormat4<'a>),
3677    Format5(ChainedSequenceContextFormat5<'a>),
3678}
3679
3680impl Default for ChainedSequenceContext<'_> {
3681    fn default() -> Self {
3682        Self::Format1(Default::default())
3683    }
3684}
3685
3686impl<'a> ChainedSequenceContext<'a> {
3687    ///Return the `FontData` used to resolve offsets for this table.
3688    pub fn offset_data(&self) -> FontData<'a> {
3689        match self {
3690            Self::Format1(item) => item.offset_data(),
3691            Self::Format2(item) => item.offset_data(),
3692            Self::Format3(item) => item.offset_data(),
3693            Self::Format4(item) => item.offset_data(),
3694            Self::Format5(item) => item.offset_data(),
3695        }
3696    }
3697
3698    /// Format identifier: format = 1
3699    pub fn format(&self) -> u16 {
3700        match self {
3701            Self::Format1(item) => item.format(),
3702            Self::Format2(item) => item.format(),
3703            Self::Format3(item) => item.format(),
3704            Self::Format4(item) => item.format(),
3705            Self::Format5(item) => item.format(),
3706        }
3707    }
3708}
3709
3710impl ReadArgs for ChainedSequenceContext<'_> {
3711    type Args = ();
3712}
3713
3714impl<'a> FontRead<'a> for ChainedSequenceContext<'a> {
3715    #[inline]
3716    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3717        let format: u16 = data.read_at(0usize)?;
3718        match format {
3719            ChainedSequenceContextFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3720            ChainedSequenceContextFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3721            ChainedSequenceContextFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
3722            ChainedSequenceContextFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
3723            ChainedSequenceContextFormat5::FORMAT => Ok(Self::Format5(FontRead::read(data)?)),
3724            other => Err(ReadError::InvalidFormat(other.into())),
3725        }
3726    }
3727}
3728
3729impl<'a> MinByteRange<'a> for ChainedSequenceContext<'a> {
3730    fn min_byte_range(&self) -> Range<usize> {
3731        match self {
3732            Self::Format1(item) => item.min_byte_range(),
3733            Self::Format2(item) => item.min_byte_range(),
3734            Self::Format3(item) => item.min_byte_range(),
3735            Self::Format4(item) => item.min_byte_range(),
3736            Self::Format5(item) => item.min_byte_range(),
3737        }
3738    }
3739    fn min_table_bytes(&self) -> &'a [u8] {
3740        match self {
3741            Self::Format1(item) => item.min_table_bytes(),
3742            Self::Format2(item) => item.min_table_bytes(),
3743            Self::Format3(item) => item.min_table_bytes(),
3744            Self::Format4(item) => item.min_table_bytes(),
3745            Self::Format5(item) => item.min_table_bytes(),
3746        }
3747    }
3748}
3749
3750/// [Device](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
3751/// delta formats
3752#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
3753#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3754#[repr(u16)]
3755#[allow(clippy::manual_non_exhaustive)]
3756pub enum DeltaFormat {
3757    /// Signed 2-bit value, 8 values per uint16
3758    #[default]
3759    Local2BitDeltas = 0x0001,
3760    /// Signed 4-bit value, 4 values per uint16
3761    Local4BitDeltas = 0x0002,
3762    /// Signed 8-bit value, 2 values per uint16
3763    Local8BitDeltas = 0x0003,
3764    /// VariationIndex table, contains a delta-set index pair.
3765    VariationIndex = 0x8000,
3766    #[doc(hidden)]
3767    /// If font data is malformed we will map unknown values to this variant
3768    Unknown,
3769}
3770
3771impl DeltaFormat {
3772    /// Create from a raw scalar.
3773    ///
3774    /// This will never fail; unknown values will be mapped to the `Unknown` variant
3775    pub fn new(raw: u16) -> Self {
3776        match raw {
3777            0x0001 => Self::Local2BitDeltas,
3778            0x0002 => Self::Local4BitDeltas,
3779            0x0003 => Self::Local8BitDeltas,
3780            0x8000 => Self::VariationIndex,
3781            _ => Self::Unknown,
3782        }
3783    }
3784}
3785
3786impl font_types::Scalar for DeltaFormat {
3787    type Raw = <u16 as font_types::Scalar>::Raw;
3788    fn to_raw(self) -> Self::Raw {
3789        (self as u16).to_raw()
3790    }
3791    fn from_raw(raw: Self::Raw) -> Self {
3792        let t = <u16>::from_raw(raw);
3793        Self::new(t)
3794    }
3795}
3796
3797impl<'a> MinByteRange<'a> for Device<'a> {
3798    fn min_byte_range(&self) -> Range<usize> {
3799        0..self.delta_value_byte_range().end
3800    }
3801    fn min_table_bytes(&self) -> &'a [u8] {
3802        let range = self.min_byte_range();
3803        self.offset_data().as_bytes().get(range).unwrap_or_default()
3804    }
3805}
3806
3807impl ReadArgs for Device<'_> {
3808    type Args = ();
3809}
3810
3811impl<'a> FontRead<'a> for Device<'a> {
3812    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3813        #[allow(clippy::absurd_extreme_comparisons)]
3814        if data.len() < Self::MIN_SIZE {
3815            return Err(ReadError::OutOfBounds);
3816        }
3817        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3818            return Err(ReadError::OutOfBounds);
3819        };
3820        Ok(Self { data })
3821    }
3822}
3823
3824/// The fields at the start of the table, which are read directly.
3825#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3826#[repr(C, packed)]
3827#[doc(hidden)]
3828pub struct DeviceFixedHeader {
3829    pub start_size: BigEndian<u16>,
3830    pub end_size: BigEndian<u16>,
3831    pub delta_format: BigEndian<DeltaFormat>,
3832}
3833
3834#[allow(clippy::absurd_extreme_comparisons)]
3835const _: () = assert!(std::mem::size_of::<DeviceFixedHeader>() <= Device::MIN_SIZE);
3836
3837/// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
3838#[derive(Clone)]
3839pub struct Device<'a> {
3840    data: TableDataRef<'a, DeviceFixedHeader>,
3841}
3842
3843#[allow(clippy::needless_lifetimes)]
3844impl<'a> Device<'a> {
3845    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + DeltaFormat::RAW_BYTE_LEN);
3846    basic_table_impls!(impl_the_methods);
3847
3848    /// Smallest size to correct, in ppem
3849    pub fn start_size(&self) -> u16 {
3850        self.data.header().start_size.get()
3851    }
3852
3853    /// Largest size to correct, in ppem
3854    pub fn end_size(&self) -> u16 {
3855        self.data.header().end_size.get()
3856    }
3857
3858    /// Format of deltaValue array data: 0x0001, 0x0002, or 0x0003
3859    pub fn delta_format(&self) -> DeltaFormat {
3860        self.data.header().delta_format.get()
3861    }
3862
3863    /// Array of compressed data
3864    pub fn delta_value(&self) -> &'a [BigEndian<u16>] {
3865        let range = self.delta_value_byte_range();
3866        self.offset_data()
3867            .read_array(range)
3868            .ok()
3869            .unwrap_or_default()
3870    }
3871
3872    pub fn start_size_byte_range(&self) -> Range<usize> {
3873        let start = 0;
3874        let end = start + u16::RAW_BYTE_LEN;
3875        start..end
3876    }
3877
3878    pub fn end_size_byte_range(&self) -> Range<usize> {
3879        let start = self.start_size_byte_range().end;
3880        let end = start + u16::RAW_BYTE_LEN;
3881        start..end
3882    }
3883
3884    pub fn delta_format_byte_range(&self) -> Range<usize> {
3885        let start = self.end_size_byte_range().end;
3886        let end = start + DeltaFormat::RAW_BYTE_LEN;
3887        start..end
3888    }
3889
3890    pub fn delta_value_byte_range(&self) -> Range<usize> {
3891        let delta_format = self.delta_format();
3892        let start_size = self.start_size();
3893        let end_size = self.end_size();
3894        let start = self.delta_format_byte_range().end;
3895        let end = start
3896            + (DeltaFormat::value_count(delta_format, start_size, end_size))
3897                .saturating_mul(u16::RAW_BYTE_LEN);
3898        start..end
3899    }
3900}
3901
3902const _: () = assert!(FontData::default_data_long_enough(Device::MIN_SIZE));
3903
3904impl Default for Device<'_> {
3905    fn default() -> Self {
3906        Self {
3907            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3908                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3909        }
3910    }
3911}
3912
3913impl<'a> MinByteRange<'a> for VariationIndex<'a> {
3914    fn min_byte_range(&self) -> Range<usize> {
3915        0..self.delta_format_byte_range().end
3916    }
3917    fn min_table_bytes(&self) -> &'a [u8] {
3918        let range = self.min_byte_range();
3919        self.offset_data().as_bytes().get(range).unwrap_or_default()
3920    }
3921}
3922
3923impl ReadArgs for VariationIndex<'_> {
3924    type Args = ();
3925}
3926
3927impl<'a> FontRead<'a> for VariationIndex<'a> {
3928    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3929        #[allow(clippy::absurd_extreme_comparisons)]
3930        if data.len() < Self::MIN_SIZE {
3931            return Err(ReadError::OutOfBounds);
3932        }
3933        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3934            return Err(ReadError::OutOfBounds);
3935        };
3936        Ok(Self { data })
3937    }
3938}
3939
3940/// The fields at the start of the table, which are read directly.
3941#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3942#[repr(C, packed)]
3943#[doc(hidden)]
3944pub struct VariationIndexFixedHeader {
3945    pub delta_set_outer_index: BigEndian<u16>,
3946    pub delta_set_inner_index: BigEndian<u16>,
3947    pub delta_format: BigEndian<DeltaFormat>,
3948}
3949
3950#[allow(clippy::absurd_extreme_comparisons)]
3951const _: () = assert!(std::mem::size_of::<VariationIndexFixedHeader>() <= VariationIndex::MIN_SIZE);
3952
3953/// Variation index table
3954#[derive(Clone)]
3955pub struct VariationIndex<'a> {
3956    data: TableDataRef<'a, VariationIndexFixedHeader>,
3957}
3958
3959#[allow(clippy::needless_lifetimes)]
3960impl<'a> VariationIndex<'a> {
3961    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + DeltaFormat::RAW_BYTE_LEN);
3962    basic_table_impls!(impl_the_methods);
3963
3964    /// A delta-set outer index — used to select an item variation
3965    /// data subtable within the item variation store.
3966    pub fn delta_set_outer_index(&self) -> u16 {
3967        self.data.header().delta_set_outer_index.get()
3968    }
3969
3970    /// A delta-set inner index — used to select a delta-set row
3971    /// within an item variation data subtable.
3972    pub fn delta_set_inner_index(&self) -> u16 {
3973        self.data.header().delta_set_inner_index.get()
3974    }
3975
3976    /// Format, = 0x8000
3977    pub fn delta_format(&self) -> DeltaFormat {
3978        self.data.header().delta_format.get()
3979    }
3980
3981    pub fn delta_set_outer_index_byte_range(&self) -> Range<usize> {
3982        let start = 0;
3983        let end = start + u16::RAW_BYTE_LEN;
3984        start..end
3985    }
3986
3987    pub fn delta_set_inner_index_byte_range(&self) -> Range<usize> {
3988        let start = self.delta_set_outer_index_byte_range().end;
3989        let end = start + u16::RAW_BYTE_LEN;
3990        start..end
3991    }
3992
3993    pub fn delta_format_byte_range(&self) -> Range<usize> {
3994        let start = self.delta_set_inner_index_byte_range().end;
3995        let end = start + DeltaFormat::RAW_BYTE_LEN;
3996        start..end
3997    }
3998}
3999
4000const _: () = assert!(FontData::default_data_long_enough(VariationIndex::MIN_SIZE));
4001
4002impl Default for VariationIndex<'_> {
4003    fn default() -> Self {
4004        Self {
4005            data: TableDataRef::new(FontData::default_table_data().as_bytes())
4006                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
4007        }
4008    }
4009}
4010
4011/// Either a [Device] table (in a non-variable font) or a [VariationIndex] table (in a variable font)
4012#[derive(Clone)]
4013pub enum DeviceOrVariationIndex<'a> {
4014    Device(Device<'a>),
4015    VariationIndex(VariationIndex<'a>),
4016}
4017
4018impl Default for DeviceOrVariationIndex<'_> {
4019    fn default() -> Self {
4020        Self::Device(Default::default())
4021    }
4022}
4023
4024impl<'a> DeviceOrVariationIndex<'a> {
4025    ///Return the `FontData` used to resolve offsets for this table.
4026    pub fn offset_data(&self) -> FontData<'a> {
4027        match self {
4028            Self::Device(item) => item.offset_data(),
4029            Self::VariationIndex(item) => item.offset_data(),
4030        }
4031    }
4032}
4033
4034impl ReadArgs for DeviceOrVariationIndex<'_> {
4035    type Args = ();
4036}
4037
4038impl<'a> FontRead<'a> for DeviceOrVariationIndex<'a> {
4039    #[inline]
4040    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4041        let format: DeltaFormat = data.read_at(4usize)?;
4042
4043        #[allow(clippy::redundant_guards)]
4044        match format {
4045            format if format != DeltaFormat::VariationIndex => {
4046                Ok(Self::Device(FontRead::read(data)?))
4047            }
4048            format if format == DeltaFormat::VariationIndex => {
4049                Ok(Self::VariationIndex(FontRead::read(data)?))
4050            }
4051            other => Err(ReadError::InvalidFormat(other.into())),
4052        }
4053    }
4054}
4055
4056impl<'a> MinByteRange<'a> for DeviceOrVariationIndex<'a> {
4057    fn min_byte_range(&self) -> Range<usize> {
4058        match self {
4059            Self::Device(item) => item.min_byte_range(),
4060            Self::VariationIndex(item) => item.min_byte_range(),
4061        }
4062    }
4063    fn min_table_bytes(&self) -> &'a [u8] {
4064        match self {
4065            Self::Device(item) => item.min_table_bytes(),
4066            Self::VariationIndex(item) => item.min_table_bytes(),
4067        }
4068    }
4069}
4070
4071impl<'a> MinByteRange<'a> for FeatureVariations<'a> {
4072    fn min_byte_range(&self) -> Range<usize> {
4073        0..self.feature_variation_records_byte_range().end
4074    }
4075    fn min_table_bytes(&self) -> &'a [u8] {
4076        let range = self.min_byte_range();
4077        self.offset_data().as_bytes().get(range).unwrap_or_default()
4078    }
4079}
4080
4081impl ReadArgs for FeatureVariations<'_> {
4082    type Args = ();
4083}
4084
4085impl<'a> FontRead<'a> for FeatureVariations<'a> {
4086    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4087        #[allow(clippy::absurd_extreme_comparisons)]
4088        if data.len() < Self::MIN_SIZE {
4089            return Err(ReadError::OutOfBounds);
4090        }
4091        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4092            return Err(ReadError::OutOfBounds);
4093        };
4094        Ok(Self { data })
4095    }
4096}
4097
4098/// The fields at the start of the table, which are read directly.
4099#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4100#[repr(C, packed)]
4101#[doc(hidden)]
4102pub struct FeatureVariationsFixedHeader {
4103    pub version: BigEndian<MajorMinor>,
4104    pub feature_variation_record_count: BigEndian<u32>,
4105}
4106
4107#[allow(clippy::absurd_extreme_comparisons)]
4108const _: () =
4109    assert!(std::mem::size_of::<FeatureVariationsFixedHeader>() <= FeatureVariations::MIN_SIZE);
4110
4111/// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table)
4112/// Version 1.1 is defined in ISO/IEC 14496-22:2026, 6.2.11.
4113#[derive(Clone)]
4114pub struct FeatureVariations<'a> {
4115    data: TableDataRef<'a, FeatureVariationsFixedHeader>,
4116}
4117
4118#[allow(clippy::needless_lifetimes)]
4119impl<'a> FeatureVariations<'a> {
4120    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
4121    basic_table_impls!(impl_the_methods);
4122
4123    pub fn version(&self) -> MajorMinor {
4124        self.data.header().version.get()
4125    }
4126
4127    /// Number of feature variation records.
4128    pub fn feature_variation_record_count(&self) -> u32 {
4129        self.data.header().feature_variation_record_count.get()
4130    }
4131
4132    /// Array of feature variation records.
4133    pub fn feature_variation_records(&self) -> &'a [FeatureVariationRecord] {
4134        let range = self.feature_variation_records_byte_range();
4135        self.offset_data()
4136            .read_array(range)
4137            .ok()
4138            .unwrap_or_default()
4139    }
4140
4141    /// Number of lookup variation records.
4142    pub fn lookup_variation_record_count(&self) -> Option<u32> {
4143        let range = self.lookup_variation_record_count_byte_range();
4144        (!range.is_empty())
4145            .then(|| self.offset_data().read_at(range.start).ok())
4146            .flatten()
4147    }
4148
4149    pub fn version_byte_range(&self) -> Range<usize> {
4150        let start = 0;
4151        let end = start + MajorMinor::RAW_BYTE_LEN;
4152        start..end
4153    }
4154
4155    pub fn feature_variation_record_count_byte_range(&self) -> Range<usize> {
4156        let start = self.version_byte_range().end;
4157        let end = start + u32::RAW_BYTE_LEN;
4158        start..end
4159    }
4160
4161    pub fn feature_variation_records_byte_range(&self) -> Range<usize> {
4162        let feature_variation_record_count = self.feature_variation_record_count();
4163        let start = self.feature_variation_record_count_byte_range().end;
4164        let end = start
4165            + (transforms::to_usize(feature_variation_record_count))
4166                .saturating_mul(FeatureVariationRecord::RAW_BYTE_LEN);
4167        start..end
4168    }
4169
4170    pub fn lookup_variation_record_count_byte_range(&self) -> Range<usize> {
4171        let start = self.feature_variation_records_byte_range().end;
4172        let end = if self.version().compatible((1u16, 1u16)) {
4173            start + u32::RAW_BYTE_LEN
4174        } else {
4175            start
4176        };
4177        start..end
4178    }
4179
4180    pub fn lookup_variation_records_byte_range(&self) -> Range<usize> {
4181        let lookup_variation_record_count =
4182            self.lookup_variation_record_count().unwrap_or_default();
4183        let start = self.lookup_variation_record_count_byte_range().end;
4184        let end = if self.version().compatible((1u16, 1u16)) {
4185            start
4186                + (transforms::to_usize(lookup_variation_record_count))
4187                    .saturating_mul(LookupVariationRecord::RAW_BYTE_LEN)
4188        } else {
4189            start
4190        };
4191        start..end
4192    }
4193}
4194
4195const _: () = assert!(FontData::default_data_long_enough(
4196    FeatureVariations::MIN_SIZE
4197));
4198
4199impl Default for FeatureVariations<'_> {
4200    fn default() -> Self {
4201        Self {
4202            data: TableDataRef::new(FontData::default_table_data().as_bytes())
4203                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
4204        }
4205    }
4206}
4207
4208/// Part of [FeatureVariations]
4209#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
4210#[repr(C)]
4211#[repr(packed)]
4212pub struct FeatureVariationRecord {
4213    /// Offset to a condition set table, from beginning of
4214    /// FeatureVariations table.
4215    pub condition_set_offset: BigEndian<Nullable<Offset32>>,
4216    /// Offset to a feature table substitution table, from beginning of
4217    /// the FeatureVariations table.
4218    pub feature_table_substitution_offset: BigEndian<Nullable<Offset32>>,
4219}
4220
4221impl FeatureVariationRecord {
4222    /// Offset to a condition set table, from beginning of
4223    /// FeatureVariations table.
4224    pub fn condition_set_offset(&self) -> Nullable<Offset32> {
4225        self.condition_set_offset.get()
4226    }
4227
4228    /// Offset to a condition set table, from beginning of
4229    /// FeatureVariations table.
4230    ///
4231    /// The `data` argument should be retrieved from the parent table
4232    /// By calling its `offset_data` method.
4233    pub fn condition_set<'a>(
4234        &self,
4235        data: FontData<'a>,
4236    ) -> Option<Result<ConditionSet<'a>, ReadError>> {
4237        self.condition_set_offset().resolve(data)
4238    }
4239
4240    /// Offset to a feature table substitution table, from beginning of
4241    /// the FeatureVariations table.
4242    pub fn feature_table_substitution_offset(&self) -> Nullable<Offset32> {
4243        self.feature_table_substitution_offset.get()
4244    }
4245
4246    /// Offset to a feature table substitution table, from beginning of
4247    /// the FeatureVariations table.
4248    ///
4249    /// The `data` argument should be retrieved from the parent table
4250    /// By calling its `offset_data` method.
4251    pub fn feature_table_substitution<'a>(
4252        &self,
4253        data: FontData<'a>,
4254    ) -> Option<Result<FeatureTableSubstitution<'a>, ReadError>> {
4255        self.feature_table_substitution_offset().resolve(data)
4256    }
4257}
4258
4259impl FixedSize for FeatureVariationRecord {
4260    const RAW_BYTE_LEN: usize = Offset32::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
4261}
4262
4263/// Lookup variations for one feature (ISO/IEC 14496-22:2026, 6.2.11).
4264#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
4265#[repr(C)]
4266#[repr(packed)]
4267pub struct LookupVariationRecord {
4268    pub feature_index: BigEndian<u16>,
4269    /// Offset from the beginning of FeatureVariations.
4270    pub feature_lookups_offset: BigEndian<Offset32>,
4271}
4272
4273impl LookupVariationRecord {
4274    pub fn feature_index(&self) -> u16 {
4275        self.feature_index.get()
4276    }
4277
4278    /// Offset from the beginning of FeatureVariations.
4279    pub fn feature_lookups_offset(&self) -> Offset32 {
4280        self.feature_lookups_offset.get()
4281    }
4282
4283    /// Offset from the beginning of FeatureVariations.
4284    ///
4285    /// The `data` argument should be retrieved from the parent table
4286    /// By calling its `offset_data` method.
4287    pub fn feature_lookups<'a>(&self, data: FontData<'a>) -> Result<FeatureLookups<'a>, ReadError> {
4288        self.feature_lookups_offset().resolve(data)
4289    }
4290}
4291
4292impl FixedSize for LookupVariationRecord {
4293    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
4294}
4295
4296impl<'a> MinByteRange<'a> for FeatureLookups<'a> {
4297    fn min_byte_range(&self) -> Range<usize> {
4298        0..self.lookup_condition_records_byte_range().end
4299    }
4300    fn min_table_bytes(&self) -> &'a [u8] {
4301        let range = self.min_byte_range();
4302        self.offset_data().as_bytes().get(range).unwrap_or_default()
4303    }
4304}
4305
4306impl ReadArgs for FeatureLookups<'_> {
4307    type Args = ();
4308}
4309
4310impl<'a> FontRead<'a> for FeatureLookups<'a> {
4311    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4312        #[allow(clippy::absurd_extreme_comparisons)]
4313        if data.len() < Self::MIN_SIZE {
4314            return Err(ReadError::OutOfBounds);
4315        }
4316        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4317            return Err(ReadError::OutOfBounds);
4318        };
4319        Ok(Self { data })
4320    }
4321}
4322
4323/// The fields at the start of the table, which are read directly.
4324#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4325#[repr(C, packed)]
4326#[doc(hidden)]
4327pub struct FeatureLookupsFixedHeader {
4328    pub version: BigEndian<MajorMinor>,
4329    pub flags: BigEndian<FeatureLookupsFlags>,
4330    pub lookup_condition_count: BigEndian<u32>,
4331}
4332
4333#[allow(clippy::absurd_extreme_comparisons)]
4334const _: () = assert!(std::mem::size_of::<FeatureLookupsFixedHeader>() <= FeatureLookups::MIN_SIZE);
4335
4336/// Conditional lookup sets for one feature (ISO/IEC 14496-22:2026, 6.2.11).
4337#[derive(Clone)]
4338pub struct FeatureLookups<'a> {
4339    data: TableDataRef<'a, FeatureLookupsFixedHeader>,
4340}
4341
4342#[allow(clippy::needless_lifetimes)]
4343impl<'a> FeatureLookups<'a> {
4344    pub const MIN_SIZE: usize =
4345        (MajorMinor::RAW_BYTE_LEN + FeatureLookupsFlags::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
4346    basic_table_impls!(impl_the_methods);
4347
4348    pub fn version(&self) -> MajorMinor {
4349        self.data.header().version.get()
4350    }
4351
4352    pub fn flags(&self) -> FeatureLookupsFlags {
4353        self.data.header().flags.get()
4354    }
4355
4356    pub fn lookup_condition_count(&self) -> u32 {
4357        self.data.header().lookup_condition_count.get()
4358    }
4359
4360    pub fn lookup_condition_records(&self) -> &'a [LookupConditionRecord] {
4361        let range = self.lookup_condition_records_byte_range();
4362        self.offset_data()
4363            .read_array(range)
4364            .ok()
4365            .unwrap_or_default()
4366    }
4367
4368    pub fn version_byte_range(&self) -> Range<usize> {
4369        let start = 0;
4370        let end = start + MajorMinor::RAW_BYTE_LEN;
4371        start..end
4372    }
4373
4374    pub fn flags_byte_range(&self) -> Range<usize> {
4375        let start = self.version_byte_range().end;
4376        let end = start + FeatureLookupsFlags::RAW_BYTE_LEN;
4377        start..end
4378    }
4379
4380    pub fn lookup_condition_count_byte_range(&self) -> Range<usize> {
4381        let start = self.flags_byte_range().end;
4382        let end = start + u32::RAW_BYTE_LEN;
4383        start..end
4384    }
4385
4386    pub fn lookup_condition_records_byte_range(&self) -> Range<usize> {
4387        let lookup_condition_count = self.lookup_condition_count();
4388        let start = self.lookup_condition_count_byte_range().end;
4389        let end = start
4390            + (transforms::to_usize(lookup_condition_count))
4391                .saturating_mul(LookupConditionRecord::RAW_BYTE_LEN);
4392        start..end
4393    }
4394}
4395
4396const _: () = assert!(FontData::default_data_long_enough(FeatureLookups::MIN_SIZE));
4397
4398impl Default for FeatureLookups<'_> {
4399    fn default() -> Self {
4400        Self {
4401            data: TableDataRef::new(FontData::default_table_data().as_bytes())
4402                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
4403        }
4404    }
4405}
4406
4407/// Qualifiers for [FeatureLookups].
4408#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, bytemuck :: AnyBitPattern)]
4409#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4410#[repr(transparent)]
4411pub struct FeatureLookupsFlags {
4412    bits: u16,
4413}
4414
4415impl FeatureLookupsFlags {
4416    /// Include the current feature's lookups as well as conditional lookups.
4417    pub const ADD_DEFAULT_LOOKUPS: Self = Self { bits: 0x0001 };
4418}
4419
4420impl FeatureLookupsFlags {
4421    ///  Returns an empty set of flags.
4422    #[inline]
4423    pub const fn empty() -> Self {
4424        Self { bits: 0 }
4425    }
4426
4427    /// Returns the set containing all flags.
4428    #[inline]
4429    pub const fn all() -> Self {
4430        Self {
4431            bits: Self::ADD_DEFAULT_LOOKUPS.bits,
4432        }
4433    }
4434
4435    /// Returns the raw value of the flags currently stored.
4436    #[inline]
4437    pub const fn bits(&self) -> u16 {
4438        self.bits
4439    }
4440
4441    /// Convert from underlying bit representation, unless that
4442    /// representation contains bits that do not correspond to a flag.
4443    #[inline]
4444    pub const fn from_bits(bits: u16) -> Option<Self> {
4445        if (bits & !Self::all().bits()) == 0 {
4446            Some(Self { bits })
4447        } else {
4448            None
4449        }
4450    }
4451
4452    /// Convert from underlying bit representation, dropping any bits
4453    /// that do not correspond to flags.
4454    #[inline]
4455    pub const fn from_bits_truncate(bits: u16) -> Self {
4456        Self {
4457            bits: bits & Self::all().bits,
4458        }
4459    }
4460
4461    /// Returns `true` if no flags are currently stored.
4462    #[inline]
4463    pub const fn is_empty(&self) -> bool {
4464        self.bits() == Self::empty().bits()
4465    }
4466
4467    /// Returns `true` if there are flags common to both `self` and `other`.
4468    #[inline]
4469    pub const fn intersects(&self, other: Self) -> bool {
4470        !(Self {
4471            bits: self.bits & other.bits,
4472        })
4473        .is_empty()
4474    }
4475
4476    /// Returns `true` if all of the flags in `other` are contained within `self`.
4477    #[inline]
4478    pub const fn contains(&self, other: Self) -> bool {
4479        (self.bits & other.bits) == other.bits
4480    }
4481
4482    /// Inserts the specified flags in-place.
4483    #[inline]
4484    pub fn insert(&mut self, other: Self) {
4485        self.bits |= other.bits;
4486    }
4487
4488    /// Removes the specified flags in-place.
4489    #[inline]
4490    pub fn remove(&mut self, other: Self) {
4491        self.bits &= !other.bits;
4492    }
4493
4494    /// Toggles the specified flags in-place.
4495    #[inline]
4496    pub fn toggle(&mut self, other: Self) {
4497        self.bits ^= other.bits;
4498    }
4499
4500    /// Returns the intersection between the flags in `self` and
4501    /// `other`.
4502    ///
4503    /// Specifically, the returned set contains only the flags which are
4504    /// present in *both* `self` *and* `other`.
4505    ///
4506    /// This is equivalent to using the `&` operator (e.g.
4507    /// [`ops::BitAnd`]), as in `flags & other`.
4508    ///
4509    /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
4510    #[inline]
4511    #[must_use]
4512    pub const fn intersection(self, other: Self) -> Self {
4513        Self {
4514            bits: self.bits & other.bits,
4515        }
4516    }
4517
4518    /// Returns the union of between the flags in `self` and `other`.
4519    ///
4520    /// Specifically, the returned set contains all flags which are
4521    /// present in *either* `self` *or* `other`, including any which are
4522    /// present in both.
4523    ///
4524    /// This is equivalent to using the `|` operator (e.g.
4525    /// [`ops::BitOr`]), as in `flags | other`.
4526    ///
4527    /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
4528    #[inline]
4529    #[must_use]
4530    pub const fn union(self, other: Self) -> Self {
4531        Self {
4532            bits: self.bits | other.bits,
4533        }
4534    }
4535
4536    /// Returns the difference between the flags in `self` and `other`.
4537    ///
4538    /// Specifically, the returned set contains all flags present in
4539    /// `self`, except for the ones present in `other`.
4540    ///
4541    /// It is also conceptually equivalent to the "bit-clear" operation:
4542    /// `flags & !other` (and this syntax is also supported).
4543    ///
4544    /// This is equivalent to using the `-` operator (e.g.
4545    /// [`ops::Sub`]), as in `flags - other`.
4546    ///
4547    /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
4548    #[inline]
4549    #[must_use]
4550    pub const fn difference(self, other: Self) -> Self {
4551        Self {
4552            bits: self.bits & !other.bits,
4553        }
4554    }
4555}
4556
4557impl std::ops::BitOr for FeatureLookupsFlags {
4558    type Output = Self;
4559
4560    /// Returns the union of the two sets of flags.
4561    #[inline]
4562    fn bitor(self, other: FeatureLookupsFlags) -> Self {
4563        Self {
4564            bits: self.bits | other.bits,
4565        }
4566    }
4567}
4568
4569impl std::ops::BitOrAssign for FeatureLookupsFlags {
4570    /// Adds the set of flags.
4571    #[inline]
4572    fn bitor_assign(&mut self, other: Self) {
4573        self.bits |= other.bits;
4574    }
4575}
4576
4577impl std::ops::BitXor for FeatureLookupsFlags {
4578    type Output = Self;
4579
4580    /// Returns the left flags, but with all the right flags toggled.
4581    #[inline]
4582    fn bitxor(self, other: Self) -> Self {
4583        Self {
4584            bits: self.bits ^ other.bits,
4585        }
4586    }
4587}
4588
4589impl std::ops::BitXorAssign for FeatureLookupsFlags {
4590    /// Toggles the set of flags.
4591    #[inline]
4592    fn bitxor_assign(&mut self, other: Self) {
4593        self.bits ^= other.bits;
4594    }
4595}
4596
4597impl std::ops::BitAnd for FeatureLookupsFlags {
4598    type Output = Self;
4599
4600    /// Returns the intersection between the two sets of flags.
4601    #[inline]
4602    fn bitand(self, other: Self) -> Self {
4603        Self {
4604            bits: self.bits & other.bits,
4605        }
4606    }
4607}
4608
4609impl std::ops::BitAndAssign for FeatureLookupsFlags {
4610    /// Disables all flags disabled in the set.
4611    #[inline]
4612    fn bitand_assign(&mut self, other: Self) {
4613        self.bits &= other.bits;
4614    }
4615}
4616
4617impl std::ops::Sub for FeatureLookupsFlags {
4618    type Output = Self;
4619
4620    /// Returns the set difference of the two sets of flags.
4621    #[inline]
4622    fn sub(self, other: Self) -> Self {
4623        Self {
4624            bits: self.bits & !other.bits,
4625        }
4626    }
4627}
4628
4629impl std::ops::SubAssign for FeatureLookupsFlags {
4630    /// Disables all flags enabled in the set.
4631    #[inline]
4632    fn sub_assign(&mut self, other: Self) {
4633        self.bits &= !other.bits;
4634    }
4635}
4636
4637impl std::ops::Not for FeatureLookupsFlags {
4638    type Output = Self;
4639
4640    /// Returns the complement of this set of flags.
4641    #[inline]
4642    fn not(self) -> Self {
4643        Self { bits: !self.bits } & Self::all()
4644    }
4645}
4646
4647impl std::fmt::Debug for FeatureLookupsFlags {
4648    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4649        let members: &[(&str, Self)] = &[("ADD_DEFAULT_LOOKUPS", Self::ADD_DEFAULT_LOOKUPS)];
4650        let mut first = true;
4651        for (name, value) in members {
4652            if self.contains(*value) {
4653                if !first {
4654                    f.write_str(" | ")?;
4655                }
4656                first = false;
4657                f.write_str(name)?;
4658            }
4659        }
4660        if first {
4661            f.write_str("(empty)")?;
4662        }
4663        Ok(())
4664    }
4665}
4666
4667impl std::fmt::Binary for FeatureLookupsFlags {
4668    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4669        std::fmt::Binary::fmt(&self.bits, f)
4670    }
4671}
4672
4673impl std::fmt::Octal for FeatureLookupsFlags {
4674    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4675        std::fmt::Octal::fmt(&self.bits, f)
4676    }
4677}
4678
4679impl std::fmt::LowerHex for FeatureLookupsFlags {
4680    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4681        std::fmt::LowerHex::fmt(&self.bits, f)
4682    }
4683}
4684
4685impl std::fmt::UpperHex for FeatureLookupsFlags {
4686    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4687        std::fmt::UpperHex::fmt(&self.bits, f)
4688    }
4689}
4690
4691impl font_types::Scalar for FeatureLookupsFlags {
4692    type Raw = <u16 as font_types::Scalar>::Raw;
4693    fn to_raw(self) -> Self::Raw {
4694        self.bits().to_raw()
4695    }
4696    fn from_raw(raw: Self::Raw) -> Self {
4697        let t = <u16>::from_raw(raw);
4698        Self::from_bits_truncate(t)
4699    }
4700}
4701
4702/// Lookups to include when a condition is true.
4703#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
4704#[repr(C)]
4705#[repr(packed)]
4706pub struct LookupConditionRecord {
4707    /// Offset from the beginning of FeatureLookups; null means true.
4708    pub condition_offset: BigEndian<Nullable<Offset32>>,
4709    /// Offset from the beginning of FeatureLookups.
4710    pub lookup_index_list_offset: BigEndian<Offset32>,
4711}
4712
4713impl LookupConditionRecord {
4714    /// Offset from the beginning of FeatureLookups; null means true.
4715    pub fn condition_offset(&self) -> Nullable<Offset32> {
4716        self.condition_offset.get()
4717    }
4718
4719    /// Offset from the beginning of FeatureLookups; null means true.
4720    ///
4721    /// The `data` argument should be retrieved from the parent table
4722    /// By calling its `offset_data` method.
4723    pub fn condition<'a>(&self, data: FontData<'a>) -> Option<Result<Condition<'a>, ReadError>> {
4724        self.condition_offset().resolve(data)
4725    }
4726
4727    /// Offset from the beginning of FeatureLookups.
4728    pub fn lookup_index_list_offset(&self) -> Offset32 {
4729        self.lookup_index_list_offset.get()
4730    }
4731
4732    /// Offset from the beginning of FeatureLookups.
4733    ///
4734    /// The `data` argument should be retrieved from the parent table
4735    /// By calling its `offset_data` method.
4736    pub fn lookup_index_list<'a>(
4737        &self,
4738        data: FontData<'a>,
4739    ) -> Result<LookupIndexList<'a>, ReadError> {
4740        self.lookup_index_list_offset().resolve(data)
4741    }
4742}
4743
4744impl FixedSize for LookupConditionRecord {
4745    const RAW_BYTE_LEN: usize = Offset32::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
4746}
4747
4748impl<'a> MinByteRange<'a> for LookupIndexList<'a> {
4749    fn min_byte_range(&self) -> Range<usize> {
4750        0..self.lookup_indices_byte_range().end
4751    }
4752    fn min_table_bytes(&self) -> &'a [u8] {
4753        let range = self.min_byte_range();
4754        self.offset_data().as_bytes().get(range).unwrap_or_default()
4755    }
4756}
4757
4758impl ReadArgs for LookupIndexList<'_> {
4759    type Args = ();
4760}
4761
4762impl<'a> FontRead<'a> for LookupIndexList<'a> {
4763    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4764        #[allow(clippy::absurd_extreme_comparisons)]
4765        if data.len() < Self::MIN_SIZE {
4766            return Err(ReadError::OutOfBounds);
4767        }
4768        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4769            return Err(ReadError::OutOfBounds);
4770        };
4771        Ok(Self { data })
4772    }
4773}
4774
4775/// The fields at the start of the table, which are read directly.
4776#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4777#[repr(C, packed)]
4778#[doc(hidden)]
4779pub struct LookupIndexListFixedHeader {
4780    pub lookup_index_count: BigEndian<u16>,
4781}
4782
4783#[allow(clippy::absurd_extreme_comparisons)]
4784const _: () =
4785    assert!(std::mem::size_of::<LookupIndexListFixedHeader>() <= LookupIndexList::MIN_SIZE);
4786
4787/// Indices into the GSUB or GPOS LookupList.
4788#[derive(Clone)]
4789pub struct LookupIndexList<'a> {
4790    data: TableDataRef<'a, LookupIndexListFixedHeader>,
4791}
4792
4793#[allow(clippy::needless_lifetimes)]
4794impl<'a> LookupIndexList<'a> {
4795    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
4796    basic_table_impls!(impl_the_methods);
4797
4798    pub fn lookup_index_count(&self) -> u16 {
4799        self.data.header().lookup_index_count.get()
4800    }
4801
4802    pub fn lookup_indices(&self) -> &'a [BigEndian<u16>] {
4803        let range = self.lookup_indices_byte_range();
4804        self.offset_data()
4805            .read_array(range)
4806            .ok()
4807            .unwrap_or_default()
4808    }
4809
4810    pub fn lookup_index_count_byte_range(&self) -> Range<usize> {
4811        let start = 0;
4812        let end = start + u16::RAW_BYTE_LEN;
4813        start..end
4814    }
4815
4816    pub fn lookup_indices_byte_range(&self) -> Range<usize> {
4817        let lookup_index_count = self.lookup_index_count();
4818        let start = self.lookup_index_count_byte_range().end;
4819        let end =
4820            start + (transforms::to_usize(lookup_index_count)).saturating_mul(u16::RAW_BYTE_LEN);
4821        start..end
4822    }
4823}
4824
4825const _: () = assert!(FontData::default_data_long_enough(
4826    LookupIndexList::MIN_SIZE
4827));
4828
4829impl Default for LookupIndexList<'_> {
4830    fn default() -> Self {
4831        Self {
4832            data: TableDataRef::new(FontData::default_table_data().as_bytes())
4833                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
4834        }
4835    }
4836}
4837
4838impl<'a> MinByteRange<'a> for ConditionSet<'a> {
4839    fn min_byte_range(&self) -> Range<usize> {
4840        0..self.condition_offsets_byte_range().end
4841    }
4842    fn min_table_bytes(&self) -> &'a [u8] {
4843        let range = self.min_byte_range();
4844        self.offset_data().as_bytes().get(range).unwrap_or_default()
4845    }
4846}
4847
4848impl ReadArgs for ConditionSet<'_> {
4849    type Args = ();
4850}
4851
4852impl<'a> FontRead<'a> for ConditionSet<'a> {
4853    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4854        #[allow(clippy::absurd_extreme_comparisons)]
4855        if data.len() < Self::MIN_SIZE {
4856            return Err(ReadError::OutOfBounds);
4857        }
4858        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4859            return Err(ReadError::OutOfBounds);
4860        };
4861        Ok(Self { data })
4862    }
4863}
4864
4865/// The fields at the start of the table, which are read directly.
4866#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4867#[repr(C, packed)]
4868#[doc(hidden)]
4869pub struct ConditionSetFixedHeader {
4870    pub condition_count: BigEndian<u16>,
4871}
4872
4873#[allow(clippy::absurd_extreme_comparisons)]
4874const _: () = assert!(std::mem::size_of::<ConditionSetFixedHeader>() <= ConditionSet::MIN_SIZE);
4875
4876/// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table)
4877#[derive(Clone)]
4878pub struct ConditionSet<'a> {
4879    data: TableDataRef<'a, ConditionSetFixedHeader>,
4880}
4881
4882#[allow(clippy::needless_lifetimes)]
4883impl<'a> ConditionSet<'a> {
4884    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
4885    basic_table_impls!(impl_the_methods);
4886
4887    /// Number of conditions for this condition set.
4888    pub fn condition_count(&self) -> u16 {
4889        self.data.header().condition_count.get()
4890    }
4891
4892    /// Array of offsets to condition tables, from beginning of the
4893    /// ConditionSet table.
4894    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset32>] {
4895        let range = self.condition_offsets_byte_range();
4896        self.offset_data()
4897            .read_array(range)
4898            .ok()
4899            .unwrap_or_default()
4900    }
4901
4902    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
4903    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset32> {
4904        let data = self.offset_data();
4905        let offsets = self.condition_offsets();
4906        ArrayOfOffsets::new(offsets, data, ())
4907    }
4908
4909    pub fn condition_count_byte_range(&self) -> Range<usize> {
4910        let start = 0;
4911        let end = start + u16::RAW_BYTE_LEN;
4912        start..end
4913    }
4914
4915    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
4916        let condition_count = self.condition_count();
4917        let start = self.condition_count_byte_range().end;
4918        let end =
4919            start + (transforms::to_usize(condition_count)).saturating_mul(Offset32::RAW_BYTE_LEN);
4920        start..end
4921    }
4922}
4923
4924const _: () = assert!(FontData::default_data_long_enough(ConditionSet::MIN_SIZE));
4925
4926impl Default for ConditionSet<'_> {
4927    fn default() -> Self {
4928        Self {
4929            data: TableDataRef::new(FontData::default_table_data().as_bytes())
4930                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
4931        }
4932    }
4933}
4934
4935/// [Condition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table)
4936///
4937/// Formats 2..5 are defined in ISO/IEC 14496-22:2026, 6.2.10.
4938#[derive(Clone)]
4939pub enum Condition<'a> {
4940    Format1AxisRange(ConditionFormat1<'a>),
4941    Format2VariableValue(ConditionFormat2<'a>),
4942    Format3And(ConditionFormat3<'a>),
4943    Format4Or(ConditionFormat4<'a>),
4944    Format5Negate(ConditionFormat5<'a>),
4945}
4946
4947impl Default for Condition<'_> {
4948    fn default() -> Self {
4949        Self::Format1AxisRange(Default::default())
4950    }
4951}
4952
4953impl<'a> Condition<'a> {
4954    ///Return the `FontData` used to resolve offsets for this table.
4955    pub fn offset_data(&self) -> FontData<'a> {
4956        match self {
4957            Self::Format1AxisRange(item) => item.offset_data(),
4958            Self::Format2VariableValue(item) => item.offset_data(),
4959            Self::Format3And(item) => item.offset_data(),
4960            Self::Format4Or(item) => item.offset_data(),
4961            Self::Format5Negate(item) => item.offset_data(),
4962        }
4963    }
4964
4965    /// Format, = 1
4966    pub fn format(&self) -> u16 {
4967        match self {
4968            Self::Format1AxisRange(item) => item.format(),
4969            Self::Format2VariableValue(item) => item.format(),
4970            Self::Format3And(item) => item.format(),
4971            Self::Format4Or(item) => item.format(),
4972            Self::Format5Negate(item) => item.format(),
4973        }
4974    }
4975}
4976
4977impl ReadArgs for Condition<'_> {
4978    type Args = ();
4979}
4980
4981impl<'a> FontRead<'a> for Condition<'a> {
4982    #[inline]
4983    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4984        let format: u16 = data.read_at(0usize)?;
4985        match format {
4986            ConditionFormat1::FORMAT => Ok(Self::Format1AxisRange(FontRead::read(data)?)),
4987            ConditionFormat2::FORMAT => Ok(Self::Format2VariableValue(FontRead::read(data)?)),
4988            ConditionFormat3::FORMAT => Ok(Self::Format3And(FontRead::read(data)?)),
4989            ConditionFormat4::FORMAT => Ok(Self::Format4Or(FontRead::read(data)?)),
4990            ConditionFormat5::FORMAT => Ok(Self::Format5Negate(FontRead::read(data)?)),
4991            other => Err(ReadError::InvalidFormat(other.into())),
4992        }
4993    }
4994}
4995
4996impl<'a> MinByteRange<'a> for Condition<'a> {
4997    fn min_byte_range(&self) -> Range<usize> {
4998        match self {
4999            Self::Format1AxisRange(item) => item.min_byte_range(),
5000            Self::Format2VariableValue(item) => item.min_byte_range(),
5001            Self::Format3And(item) => item.min_byte_range(),
5002            Self::Format4Or(item) => item.min_byte_range(),
5003            Self::Format5Negate(item) => item.min_byte_range(),
5004        }
5005    }
5006    fn min_table_bytes(&self) -> &'a [u8] {
5007        match self {
5008            Self::Format1AxisRange(item) => item.min_table_bytes(),
5009            Self::Format2VariableValue(item) => item.min_table_bytes(),
5010            Self::Format3And(item) => item.min_table_bytes(),
5011            Self::Format4Or(item) => item.min_table_bytes(),
5012            Self::Format5Negate(item) => item.min_table_bytes(),
5013        }
5014    }
5015}
5016
5017impl Format<u16> for ConditionFormat1<'_> {
5018    const FORMAT: u16 = 1;
5019}
5020
5021impl<'a> MinByteRange<'a> for ConditionFormat1<'a> {
5022    fn min_byte_range(&self) -> Range<usize> {
5023        0..self.filter_range_max_value_byte_range().end
5024    }
5025    fn min_table_bytes(&self) -> &'a [u8] {
5026        let range = self.min_byte_range();
5027        self.offset_data().as_bytes().get(range).unwrap_or_default()
5028    }
5029}
5030
5031impl ReadArgs for ConditionFormat1<'_> {
5032    type Args = ();
5033}
5034
5035impl<'a> FontRead<'a> for ConditionFormat1<'a> {
5036    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5037        #[allow(clippy::absurd_extreme_comparisons)]
5038        if data.len() < Self::MIN_SIZE {
5039            return Err(ReadError::OutOfBounds);
5040        }
5041        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5042            return Err(ReadError::OutOfBounds);
5043        };
5044        Ok(Self { data })
5045    }
5046}
5047
5048/// The fields at the start of the table, which are read directly.
5049#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5050#[repr(C, packed)]
5051#[doc(hidden)]
5052pub struct ConditionFormat1FixedHeader {
5053    pub format: BigEndian<u16>,
5054    pub axis_index: BigEndian<u16>,
5055    pub filter_range_min_value: BigEndian<F2Dot14>,
5056    pub filter_range_max_value: BigEndian<F2Dot14>,
5057}
5058
5059#[allow(clippy::absurd_extreme_comparisons)]
5060const _: () =
5061    assert!(std::mem::size_of::<ConditionFormat1FixedHeader>() <= ConditionFormat1::MIN_SIZE);
5062
5063/// [Condition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table-format-1-font-variation-axis-range): Font Variation Axis Range
5064#[derive(Clone)]
5065pub struct ConditionFormat1<'a> {
5066    data: TableDataRef<'a, ConditionFormat1FixedHeader>,
5067}
5068
5069#[allow(clippy::needless_lifetimes)]
5070impl<'a> ConditionFormat1<'a> {
5071    pub const MIN_SIZE: usize =
5072        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + F2Dot14::RAW_BYTE_LEN + F2Dot14::RAW_BYTE_LEN);
5073    basic_table_impls!(impl_the_methods);
5074
5075    /// Format, = 1
5076    pub fn format(&self) -> u16 {
5077        self.data.header().format.get()
5078    }
5079
5080    /// Index (zero-based) for the variation axis within the 'fvar'
5081    /// table.
5082    pub fn axis_index(&self) -> u16 {
5083        self.data.header().axis_index.get()
5084    }
5085
5086    /// Minimum value of the font variation instances that satisfy this
5087    /// condition.
5088    pub fn filter_range_min_value(&self) -> F2Dot14 {
5089        self.data.header().filter_range_min_value.get()
5090    }
5091
5092    /// Maximum value of the font variation instances that satisfy this
5093    /// condition.
5094    pub fn filter_range_max_value(&self) -> F2Dot14 {
5095        self.data.header().filter_range_max_value.get()
5096    }
5097
5098    pub fn format_byte_range(&self) -> Range<usize> {
5099        let start = 0;
5100        let end = start + u16::RAW_BYTE_LEN;
5101        start..end
5102    }
5103
5104    pub fn axis_index_byte_range(&self) -> Range<usize> {
5105        let start = self.format_byte_range().end;
5106        let end = start + u16::RAW_BYTE_LEN;
5107        start..end
5108    }
5109
5110    pub fn filter_range_min_value_byte_range(&self) -> Range<usize> {
5111        let start = self.axis_index_byte_range().end;
5112        let end = start + F2Dot14::RAW_BYTE_LEN;
5113        start..end
5114    }
5115
5116    pub fn filter_range_max_value_byte_range(&self) -> Range<usize> {
5117        let start = self.filter_range_min_value_byte_range().end;
5118        let end = start + F2Dot14::RAW_BYTE_LEN;
5119        start..end
5120    }
5121}
5122
5123const _: () = assert!(FontData::default_data_long_enough(
5124    ConditionFormat1::MIN_SIZE
5125));
5126
5127impl Default for ConditionFormat1<'_> {
5128    fn default() -> Self {
5129        Self {
5130            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
5131                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5132        }
5133    }
5134}
5135
5136impl Format<u16> for ConditionFormat2<'_> {
5137    const FORMAT: u16 = 2;
5138}
5139
5140impl<'a> MinByteRange<'a> for ConditionFormat2<'a> {
5141    fn min_byte_range(&self) -> Range<usize> {
5142        0..self.var_index_byte_range().end
5143    }
5144    fn min_table_bytes(&self) -> &'a [u8] {
5145        let range = self.min_byte_range();
5146        self.offset_data().as_bytes().get(range).unwrap_or_default()
5147    }
5148}
5149
5150impl ReadArgs for ConditionFormat2<'_> {
5151    type Args = ();
5152}
5153
5154impl<'a> FontRead<'a> for ConditionFormat2<'a> {
5155    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5156        #[allow(clippy::absurd_extreme_comparisons)]
5157        if data.len() < Self::MIN_SIZE {
5158            return Err(ReadError::OutOfBounds);
5159        }
5160        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5161            return Err(ReadError::OutOfBounds);
5162        };
5163        Ok(Self { data })
5164    }
5165}
5166
5167/// The fields at the start of the table, which are read directly.
5168#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5169#[repr(C, packed)]
5170#[doc(hidden)]
5171pub struct ConditionFormat2FixedHeader {
5172    pub format: BigEndian<u16>,
5173    pub default_value: BigEndian<i16>,
5174    pub var_index: BigEndian<u32>,
5175}
5176
5177#[allow(clippy::absurd_extreme_comparisons)]
5178const _: () =
5179    assert!(std::mem::size_of::<ConditionFormat2FixedHeader>() <= ConditionFormat2::MIN_SIZE);
5180
5181/// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index
5182#[derive(Clone)]
5183pub struct ConditionFormat2<'a> {
5184    data: TableDataRef<'a, ConditionFormat2FixedHeader>,
5185}
5186
5187#[allow(clippy::needless_lifetimes)]
5188impl<'a> ConditionFormat2<'a> {
5189    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
5190    basic_table_impls!(impl_the_methods);
5191
5192    /// Format, = 2
5193    pub fn format(&self) -> u16 {
5194        self.data.header().format.get()
5195    }
5196
5197    /// Value at default instance.
5198    pub fn default_value(&self) -> i16 {
5199        self.data.header().default_value.get()
5200    }
5201
5202    /// Variation index to vary the value based on current designspace location.
5203    pub fn var_index(&self) -> u32 {
5204        self.data.header().var_index.get()
5205    }
5206
5207    pub fn format_byte_range(&self) -> Range<usize> {
5208        let start = 0;
5209        let end = start + u16::RAW_BYTE_LEN;
5210        start..end
5211    }
5212
5213    pub fn default_value_byte_range(&self) -> Range<usize> {
5214        let start = self.format_byte_range().end;
5215        let end = start + i16::RAW_BYTE_LEN;
5216        start..end
5217    }
5218
5219    pub fn var_index_byte_range(&self) -> Range<usize> {
5220        let start = self.default_value_byte_range().end;
5221        let end = start + u32::RAW_BYTE_LEN;
5222        start..end
5223    }
5224}
5225
5226impl Format<u16> for ConditionFormat3<'_> {
5227    const FORMAT: u16 = 3;
5228}
5229
5230impl<'a> MinByteRange<'a> for ConditionFormat3<'a> {
5231    fn min_byte_range(&self) -> Range<usize> {
5232        0..self.condition_offsets_byte_range().end
5233    }
5234    fn min_table_bytes(&self) -> &'a [u8] {
5235        let range = self.min_byte_range();
5236        self.offset_data().as_bytes().get(range).unwrap_or_default()
5237    }
5238}
5239
5240impl ReadArgs for ConditionFormat3<'_> {
5241    type Args = ();
5242}
5243
5244impl<'a> FontRead<'a> for ConditionFormat3<'a> {
5245    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5246        #[allow(clippy::absurd_extreme_comparisons)]
5247        if data.len() < Self::MIN_SIZE {
5248            return Err(ReadError::OutOfBounds);
5249        }
5250        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5251            return Err(ReadError::OutOfBounds);
5252        };
5253        Ok(Self { data })
5254    }
5255}
5256
5257/// The fields at the start of the table, which are read directly.
5258#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5259#[repr(C, packed)]
5260#[doc(hidden)]
5261pub struct ConditionFormat3FixedHeader {
5262    pub format: BigEndian<u16>,
5263    pub condition_count: u8,
5264}
5265
5266#[allow(clippy::absurd_extreme_comparisons)]
5267const _: () =
5268    assert!(std::mem::size_of::<ConditionFormat3FixedHeader>() <= ConditionFormat3::MIN_SIZE);
5269
5270/// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND
5271#[derive(Clone)]
5272pub struct ConditionFormat3<'a> {
5273    data: TableDataRef<'a, ConditionFormat3FixedHeader>,
5274}
5275
5276#[allow(clippy::needless_lifetimes)]
5277impl<'a> ConditionFormat3<'a> {
5278    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
5279    basic_table_impls!(impl_the_methods);
5280
5281    /// Format, = 3
5282    pub fn format(&self) -> u16 {
5283        self.data.header().format.get()
5284    }
5285
5286    /// Number of conditions.
5287    pub fn condition_count(&self) -> u8 {
5288        self.data.header().condition_count
5289    }
5290
5291    /// Array of condition tables for this conjunction (AND) expression.
5292    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
5293        let range = self.condition_offsets_byte_range();
5294        self.offset_data()
5295            .read_array(range)
5296            .ok()
5297            .unwrap_or_default()
5298    }
5299
5300    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
5301    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
5302        let data = self.offset_data();
5303        let offsets = self.condition_offsets();
5304        ArrayOfOffsets::new(offsets, data, ())
5305    }
5306
5307    pub fn format_byte_range(&self) -> Range<usize> {
5308        let start = 0;
5309        let end = start + u16::RAW_BYTE_LEN;
5310        start..end
5311    }
5312
5313    pub fn condition_count_byte_range(&self) -> Range<usize> {
5314        let start = self.format_byte_range().end;
5315        let end = start + u8::RAW_BYTE_LEN;
5316        start..end
5317    }
5318
5319    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
5320        let condition_count = self.condition_count();
5321        let start = self.condition_count_byte_range().end;
5322        let end =
5323            start + (transforms::to_usize(condition_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
5324        start..end
5325    }
5326}
5327
5328impl Format<u16> for ConditionFormat4<'_> {
5329    const FORMAT: u16 = 4;
5330}
5331
5332impl<'a> MinByteRange<'a> for ConditionFormat4<'a> {
5333    fn min_byte_range(&self) -> Range<usize> {
5334        0..self.condition_offsets_byte_range().end
5335    }
5336    fn min_table_bytes(&self) -> &'a [u8] {
5337        let range = self.min_byte_range();
5338        self.offset_data().as_bytes().get(range).unwrap_or_default()
5339    }
5340}
5341
5342impl ReadArgs for ConditionFormat4<'_> {
5343    type Args = ();
5344}
5345
5346impl<'a> FontRead<'a> for ConditionFormat4<'a> {
5347    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5348        #[allow(clippy::absurd_extreme_comparisons)]
5349        if data.len() < Self::MIN_SIZE {
5350            return Err(ReadError::OutOfBounds);
5351        }
5352        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5353            return Err(ReadError::OutOfBounds);
5354        };
5355        Ok(Self { data })
5356    }
5357}
5358
5359/// The fields at the start of the table, which are read directly.
5360#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5361#[repr(C, packed)]
5362#[doc(hidden)]
5363pub struct ConditionFormat4FixedHeader {
5364    pub format: BigEndian<u16>,
5365    pub condition_count: u8,
5366}
5367
5368#[allow(clippy::absurd_extreme_comparisons)]
5369const _: () =
5370    assert!(std::mem::size_of::<ConditionFormat4FixedHeader>() <= ConditionFormat4::MIN_SIZE);
5371
5372/// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR
5373#[derive(Clone)]
5374pub struct ConditionFormat4<'a> {
5375    data: TableDataRef<'a, ConditionFormat4FixedHeader>,
5376}
5377
5378#[allow(clippy::needless_lifetimes)]
5379impl<'a> ConditionFormat4<'a> {
5380    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
5381    basic_table_impls!(impl_the_methods);
5382
5383    /// Format, = 4
5384    pub fn format(&self) -> u16 {
5385        self.data.header().format.get()
5386    }
5387
5388    /// Number of conditions.
5389    pub fn condition_count(&self) -> u8 {
5390        self.data.header().condition_count
5391    }
5392
5393    /// Array of condition tables for this disjunction (OR) expression.
5394    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
5395        let range = self.condition_offsets_byte_range();
5396        self.offset_data()
5397            .read_array(range)
5398            .ok()
5399            .unwrap_or_default()
5400    }
5401
5402    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
5403    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
5404        let data = self.offset_data();
5405        let offsets = self.condition_offsets();
5406        ArrayOfOffsets::new(offsets, data, ())
5407    }
5408
5409    pub fn format_byte_range(&self) -> Range<usize> {
5410        let start = 0;
5411        let end = start + u16::RAW_BYTE_LEN;
5412        start..end
5413    }
5414
5415    pub fn condition_count_byte_range(&self) -> Range<usize> {
5416        let start = self.format_byte_range().end;
5417        let end = start + u8::RAW_BYTE_LEN;
5418        start..end
5419    }
5420
5421    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
5422        let condition_count = self.condition_count();
5423        let start = self.condition_count_byte_range().end;
5424        let end =
5425            start + (transforms::to_usize(condition_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
5426        start..end
5427    }
5428}
5429
5430impl Format<u16> for ConditionFormat5<'_> {
5431    const FORMAT: u16 = 5;
5432}
5433
5434impl<'a> MinByteRange<'a> for ConditionFormat5<'a> {
5435    fn min_byte_range(&self) -> Range<usize> {
5436        0..self.condition_offset_byte_range().end
5437    }
5438    fn min_table_bytes(&self) -> &'a [u8] {
5439        let range = self.min_byte_range();
5440        self.offset_data().as_bytes().get(range).unwrap_or_default()
5441    }
5442}
5443
5444impl ReadArgs for ConditionFormat5<'_> {
5445    type Args = ();
5446}
5447
5448impl<'a> FontRead<'a> for ConditionFormat5<'a> {
5449    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5450        #[allow(clippy::absurd_extreme_comparisons)]
5451        if data.len() < Self::MIN_SIZE {
5452            return Err(ReadError::OutOfBounds);
5453        }
5454        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5455            return Err(ReadError::OutOfBounds);
5456        };
5457        Ok(Self { data })
5458    }
5459}
5460
5461/// The fields at the start of the table, which are read directly.
5462#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5463#[repr(C, packed)]
5464#[doc(hidden)]
5465pub struct ConditionFormat5FixedHeader {
5466    pub format: BigEndian<u16>,
5467    pub condition_offset: BigEndian<Offset24>,
5468}
5469
5470#[allow(clippy::absurd_extreme_comparisons)]
5471const _: () =
5472    assert!(std::mem::size_of::<ConditionFormat5FixedHeader>() <= ConditionFormat5::MIN_SIZE);
5473
5474/// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT
5475#[derive(Clone)]
5476pub struct ConditionFormat5<'a> {
5477    data: TableDataRef<'a, ConditionFormat5FixedHeader>,
5478}
5479
5480#[allow(clippy::needless_lifetimes)]
5481impl<'a> ConditionFormat5<'a> {
5482    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset24::RAW_BYTE_LEN);
5483    basic_table_impls!(impl_the_methods);
5484
5485    /// Format, = 5
5486    pub fn format(&self) -> u16 {
5487        self.data.header().format.get()
5488    }
5489
5490    /// Condition to negate.
5491    pub fn condition_offset(&self) -> Offset24 {
5492        self.data.header().condition_offset.get()
5493    }
5494
5495    /// Attempt to resolve [`condition_offset`][Self::condition_offset].
5496    pub fn condition(&self) -> Result<Condition<'a>, ReadError> {
5497        let data = self.offset_data();
5498        self.condition_offset().resolve(data)
5499    }
5500
5501    pub fn format_byte_range(&self) -> Range<usize> {
5502        let start = 0;
5503        let end = start + u16::RAW_BYTE_LEN;
5504        start..end
5505    }
5506
5507    pub fn condition_offset_byte_range(&self) -> Range<usize> {
5508        let start = self.format_byte_range().end;
5509        let end = start + Offset24::RAW_BYTE_LEN;
5510        start..end
5511    }
5512}
5513
5514impl<'a> MinByteRange<'a> for FeatureTableSubstitution<'a> {
5515    fn min_byte_range(&self) -> Range<usize> {
5516        0..self.substitutions_byte_range().end
5517    }
5518    fn min_table_bytes(&self) -> &'a [u8] {
5519        let range = self.min_byte_range();
5520        self.offset_data().as_bytes().get(range).unwrap_or_default()
5521    }
5522}
5523
5524impl ReadArgs for FeatureTableSubstitution<'_> {
5525    type Args = ();
5526}
5527
5528impl<'a> FontRead<'a> for FeatureTableSubstitution<'a> {
5529    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5530        #[allow(clippy::absurd_extreme_comparisons)]
5531        if data.len() < Self::MIN_SIZE {
5532            return Err(ReadError::OutOfBounds);
5533        }
5534        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5535            return Err(ReadError::OutOfBounds);
5536        };
5537        Ok(Self { data })
5538    }
5539}
5540
5541/// The fields at the start of the table, which are read directly.
5542#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5543#[repr(C, packed)]
5544#[doc(hidden)]
5545pub struct FeatureTableSubstitutionFixedHeader {
5546    pub version: BigEndian<MajorMinor>,
5547    pub substitution_count: BigEndian<u16>,
5548}
5549
5550#[allow(clippy::absurd_extreme_comparisons)]
5551const _: () = assert!(
5552    std::mem::size_of::<FeatureTableSubstitutionFixedHeader>()
5553        <= FeatureTableSubstitution::MIN_SIZE
5554);
5555
5556/// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table)
5557#[derive(Clone)]
5558pub struct FeatureTableSubstitution<'a> {
5559    data: TableDataRef<'a, FeatureTableSubstitutionFixedHeader>,
5560}
5561
5562#[allow(clippy::needless_lifetimes)]
5563impl<'a> FeatureTableSubstitution<'a> {
5564    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
5565    basic_table_impls!(impl_the_methods);
5566
5567    /// Major & minor version of the table: (1, 0)
5568    pub fn version(&self) -> MajorMinor {
5569        self.data.header().version.get()
5570    }
5571
5572    /// Number of feature table substitution records.
5573    pub fn substitution_count(&self) -> u16 {
5574        self.data.header().substitution_count.get()
5575    }
5576
5577    /// Array of feature table substitution records.
5578    pub fn substitutions(&self) -> &'a [FeatureTableSubstitutionRecord] {
5579        let range = self.substitutions_byte_range();
5580        self.offset_data()
5581            .read_array(range)
5582            .ok()
5583            .unwrap_or_default()
5584    }
5585
5586    pub fn version_byte_range(&self) -> Range<usize> {
5587        let start = 0;
5588        let end = start + MajorMinor::RAW_BYTE_LEN;
5589        start..end
5590    }
5591
5592    pub fn substitution_count_byte_range(&self) -> Range<usize> {
5593        let start = self.version_byte_range().end;
5594        let end = start + u16::RAW_BYTE_LEN;
5595        start..end
5596    }
5597
5598    pub fn substitutions_byte_range(&self) -> Range<usize> {
5599        let substitution_count = self.substitution_count();
5600        let start = self.substitution_count_byte_range().end;
5601        let end = start
5602            + (transforms::to_usize(substitution_count))
5603                .saturating_mul(FeatureTableSubstitutionRecord::RAW_BYTE_LEN);
5604        start..end
5605    }
5606}
5607
5608const _: () = assert!(FontData::default_data_long_enough(
5609    FeatureTableSubstitution::MIN_SIZE
5610));
5611
5612impl Default for FeatureTableSubstitution<'_> {
5613    fn default() -> Self {
5614        Self {
5615            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5616                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5617        }
5618    }
5619}
5620
5621/// Used in [FeatureTableSubstitution]
5622#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
5623#[repr(C)]
5624#[repr(packed)]
5625pub struct FeatureTableSubstitutionRecord {
5626    /// The feature table index to match.
5627    pub feature_index: BigEndian<u16>,
5628    /// Offset to an alternate feature table, from start of the
5629    /// FeatureTableSubstitution table.
5630    pub alternate_feature_offset: BigEndian<Offset32>,
5631}
5632
5633impl FeatureTableSubstitutionRecord {
5634    /// The feature table index to match.
5635    pub fn feature_index(&self) -> u16 {
5636        self.feature_index.get()
5637    }
5638
5639    /// Offset to an alternate feature table, from start of the
5640    /// FeatureTableSubstitution table.
5641    pub fn alternate_feature_offset(&self) -> Offset32 {
5642        self.alternate_feature_offset.get()
5643    }
5644}
5645
5646impl FixedSize for FeatureTableSubstitutionRecord {
5647    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
5648}
5649
5650impl<'a> MinByteRange<'a> for SizeParams<'a> {
5651    fn min_byte_range(&self) -> Range<usize> {
5652        0..self.range_end_byte_range().end
5653    }
5654    fn min_table_bytes(&self) -> &'a [u8] {
5655        let range = self.min_byte_range();
5656        self.offset_data().as_bytes().get(range).unwrap_or_default()
5657    }
5658}
5659
5660impl ReadArgs for SizeParams<'_> {
5661    type Args = ();
5662}
5663
5664impl<'a> FontRead<'a> for SizeParams<'a> {
5665    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5666        #[allow(clippy::absurd_extreme_comparisons)]
5667        if data.len() < Self::MIN_SIZE {
5668            return Err(ReadError::OutOfBounds);
5669        }
5670        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5671            return Err(ReadError::OutOfBounds);
5672        };
5673        Ok(Self { data })
5674    }
5675}
5676
5677/// The fields at the start of the table, which are read directly.
5678#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5679#[repr(C, packed)]
5680#[doc(hidden)]
5681pub struct SizeParamsFixedHeader {
5682    pub design_size: BigEndian<u16>,
5683    pub identifier: BigEndian<u16>,
5684    pub name_entry: BigEndian<u16>,
5685    pub range_start: BigEndian<u16>,
5686    pub range_end: BigEndian<u16>,
5687}
5688
5689#[allow(clippy::absurd_extreme_comparisons)]
5690const _: () = assert!(std::mem::size_of::<SizeParamsFixedHeader>() <= SizeParams::MIN_SIZE);
5691
5692#[derive(Clone)]
5693pub struct SizeParams<'a> {
5694    data: TableDataRef<'a, SizeParamsFixedHeader>,
5695}
5696
5697#[allow(clippy::needless_lifetimes)]
5698impl<'a> SizeParams<'a> {
5699    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5700        + u16::RAW_BYTE_LEN
5701        + u16::RAW_BYTE_LEN
5702        + u16::RAW_BYTE_LEN
5703        + u16::RAW_BYTE_LEN);
5704    basic_table_impls!(impl_the_methods);
5705
5706    /// The first value represents the design size in 720/inch units (decipoints).
5707    ///
5708    /// The design size entry must be non-zero. When there is a design size but
5709    /// no recommended size range, the rest of the array will consist of zeros.
5710    pub fn design_size(&self) -> u16 {
5711        self.data.header().design_size.get()
5712    }
5713
5714    /// The second value has no independent meaning, but serves as an identifier that associates fonts in a subfamily.
5715    ///
5716    /// All fonts which share a Typographic or Font Family name and which differ
5717    /// only by size range shall have the same subfamily value, and no fonts
5718    /// which differ in weight or style shall have the same subfamily value.
5719    /// If this value is zero, the remaining fields in the array will be ignored.
5720    pub fn identifier(&self) -> u16 {
5721        self.data.header().identifier.get()
5722    }
5723
5724    /// The third value enables applications to use a single name for the subfamily identified by the second value.
5725    ///
5726    /// If the preceding value is non-zero, this value must be set in the range
5727    /// 256 – 32767 (inclusive). It records the value of a field in the 'name'
5728    /// table, which must contain English-language strings encoded in Windows
5729    /// Unicode and Macintosh Roman, and may contain additional strings localized
5730    /// to other scripts and languages. Each of these strings is the name
5731    /// an application should use, in combination with the family name, to
5732    /// represent the subfamily in a menu. Applications will choose the
5733    /// appropriate version based on their selection criteria.
5734    pub fn name_entry(&self) -> u16 {
5735        self.data.header().name_entry.get()
5736    }
5737
5738    /// The fourth and fifth values represent the small end of the recommended
5739    /// usage range (exclusive) and the large end of the recommended usage range
5740    /// (inclusive), stored in 720/inch units (decipoints).
5741    ///
5742    /// Ranges must not overlap, and should generally be contiguous.
5743    pub fn range_start(&self) -> u16 {
5744        self.data.header().range_start.get()
5745    }
5746
5747    pub fn range_end(&self) -> u16 {
5748        self.data.header().range_end.get()
5749    }
5750
5751    pub fn design_size_byte_range(&self) -> Range<usize> {
5752        let start = 0;
5753        let end = start + u16::RAW_BYTE_LEN;
5754        start..end
5755    }
5756
5757    pub fn identifier_byte_range(&self) -> Range<usize> {
5758        let start = self.design_size_byte_range().end;
5759        let end = start + u16::RAW_BYTE_LEN;
5760        start..end
5761    }
5762
5763    pub fn name_entry_byte_range(&self) -> Range<usize> {
5764        let start = self.identifier_byte_range().end;
5765        let end = start + u16::RAW_BYTE_LEN;
5766        start..end
5767    }
5768
5769    pub fn range_start_byte_range(&self) -> Range<usize> {
5770        let start = self.name_entry_byte_range().end;
5771        let end = start + u16::RAW_BYTE_LEN;
5772        start..end
5773    }
5774
5775    pub fn range_end_byte_range(&self) -> Range<usize> {
5776        let start = self.range_start_byte_range().end;
5777        let end = start + u16::RAW_BYTE_LEN;
5778        start..end
5779    }
5780}
5781
5782const _: () = assert!(FontData::default_data_long_enough(SizeParams::MIN_SIZE));
5783
5784impl Default for SizeParams<'_> {
5785    fn default() -> Self {
5786        Self {
5787            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5788                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5789        }
5790    }
5791}
5792
5793impl<'a> MinByteRange<'a> for StylisticSetParams<'a> {
5794    fn min_byte_range(&self) -> Range<usize> {
5795        0..self.ui_name_id_byte_range().end
5796    }
5797    fn min_table_bytes(&self) -> &'a [u8] {
5798        let range = self.min_byte_range();
5799        self.offset_data().as_bytes().get(range).unwrap_or_default()
5800    }
5801}
5802
5803impl ReadArgs for StylisticSetParams<'_> {
5804    type Args = ();
5805}
5806
5807impl<'a> FontRead<'a> for StylisticSetParams<'a> {
5808    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5809        #[allow(clippy::absurd_extreme_comparisons)]
5810        if data.len() < Self::MIN_SIZE {
5811            return Err(ReadError::OutOfBounds);
5812        }
5813        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5814            return Err(ReadError::OutOfBounds);
5815        };
5816        Ok(Self { data })
5817    }
5818}
5819
5820/// The fields at the start of the table, which are read directly.
5821#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5822#[repr(C, packed)]
5823#[doc(hidden)]
5824pub struct StylisticSetParamsFixedHeader {
5825    pub version: BigEndian<u16>,
5826    pub ui_name_id: BigEndian<NameId>,
5827}
5828
5829#[allow(clippy::absurd_extreme_comparisons)]
5830const _: () =
5831    assert!(std::mem::size_of::<StylisticSetParamsFixedHeader>() <= StylisticSetParams::MIN_SIZE);
5832
5833#[derive(Clone)]
5834pub struct StylisticSetParams<'a> {
5835    data: TableDataRef<'a, StylisticSetParamsFixedHeader>,
5836}
5837
5838#[allow(clippy::needless_lifetimes)]
5839impl<'a> StylisticSetParams<'a> {
5840    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + NameId::RAW_BYTE_LEN);
5841    basic_table_impls!(impl_the_methods);
5842
5843    pub fn version(&self) -> u16 {
5844        self.data.header().version.get()
5845    }
5846
5847    /// The 'name' table name ID that specifies a string (or strings, for
5848    /// multiple languages) for a user-interface label for this feature.
5849    ///
5850    /// The value of uiLabelNameId is expected to be in the font-specific name
5851    /// ID range (256-32767), though that is not a requirement in this Feature
5852    /// Parameters specification. The user-interface label for the feature can
5853    /// be provided in multiple languages. An English string should be included
5854    /// as a fallback. The string should be kept to a minimal length to fit
5855    /// comfortably with different application interfaces.
5856    pub fn ui_name_id(&self) -> NameId {
5857        self.data.header().ui_name_id.get()
5858    }
5859
5860    pub fn version_byte_range(&self) -> Range<usize> {
5861        let start = 0;
5862        let end = start + u16::RAW_BYTE_LEN;
5863        start..end
5864    }
5865
5866    pub fn ui_name_id_byte_range(&self) -> Range<usize> {
5867        let start = self.version_byte_range().end;
5868        let end = start + NameId::RAW_BYTE_LEN;
5869        start..end
5870    }
5871}
5872
5873const _: () = assert!(FontData::default_data_long_enough(
5874    StylisticSetParams::MIN_SIZE
5875));
5876
5877impl Default for StylisticSetParams<'_> {
5878    fn default() -> Self {
5879        Self {
5880            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5881                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5882        }
5883    }
5884}
5885
5886impl Format<u16> for CharacterVariantParams<'_> {
5887    const FORMAT: u16 = 0;
5888}
5889
5890impl<'a> MinByteRange<'a> for CharacterVariantParams<'a> {
5891    fn min_byte_range(&self) -> Range<usize> {
5892        0..self.character_byte_range().end
5893    }
5894    fn min_table_bytes(&self) -> &'a [u8] {
5895        let range = self.min_byte_range();
5896        self.offset_data().as_bytes().get(range).unwrap_or_default()
5897    }
5898}
5899
5900impl ReadArgs for CharacterVariantParams<'_> {
5901    type Args = ();
5902}
5903
5904impl<'a> FontRead<'a> for CharacterVariantParams<'a> {
5905    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5906        #[allow(clippy::absurd_extreme_comparisons)]
5907        if data.len() < Self::MIN_SIZE {
5908            return Err(ReadError::OutOfBounds);
5909        }
5910        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5911            return Err(ReadError::OutOfBounds);
5912        };
5913        Ok(Self { data })
5914    }
5915}
5916
5917/// The fields at the start of the table, which are read directly.
5918#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5919#[repr(C, packed)]
5920#[doc(hidden)]
5921pub struct CharacterVariantParamsFixedHeader {
5922    pub format: BigEndian<u16>,
5923    pub feat_ui_label_name_id: BigEndian<NameId>,
5924    pub feat_ui_tooltip_text_name_id: BigEndian<NameId>,
5925    pub sample_text_name_id: BigEndian<NameId>,
5926    pub num_named_parameters: BigEndian<u16>,
5927    pub first_param_ui_label_name_id: BigEndian<NameId>,
5928    pub char_count: BigEndian<u16>,
5929}
5930
5931#[allow(clippy::absurd_extreme_comparisons)]
5932const _: () = assert!(
5933    std::mem::size_of::<CharacterVariantParamsFixedHeader>() <= CharacterVariantParams::MIN_SIZE
5934);
5935
5936/// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99)
5937#[derive(Clone)]
5938pub struct CharacterVariantParams<'a> {
5939    data: TableDataRef<'a, CharacterVariantParamsFixedHeader>,
5940}
5941
5942#[allow(clippy::needless_lifetimes)]
5943impl<'a> CharacterVariantParams<'a> {
5944    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5945        + NameId::RAW_BYTE_LEN
5946        + NameId::RAW_BYTE_LEN
5947        + NameId::RAW_BYTE_LEN
5948        + u16::RAW_BYTE_LEN
5949        + NameId::RAW_BYTE_LEN
5950        + u16::RAW_BYTE_LEN);
5951    basic_table_impls!(impl_the_methods);
5952
5953    /// Format number is set to 0.
5954    pub fn format(&self) -> u16 {
5955        self.data.header().format.get()
5956    }
5957
5958    /// The 'name' table name ID that specifies a string (or strings,
5959    /// for multiple languages) for a user-interface label for this
5960    /// feature. (May be NULL.)
5961    pub fn feat_ui_label_name_id(&self) -> NameId {
5962        self.data.header().feat_ui_label_name_id.get()
5963    }
5964
5965    /// The 'name' table name ID that specifies a string (or strings,
5966    /// for multiple languages) that an application can use for tooltip
5967    /// text for this feature. (May be NULL.)
5968    pub fn feat_ui_tooltip_text_name_id(&self) -> NameId {
5969        self.data.header().feat_ui_tooltip_text_name_id.get()
5970    }
5971
5972    /// The 'name' table name ID that specifies sample text that
5973    /// illustrates the effect of this feature. (May be NULL.)
5974    pub fn sample_text_name_id(&self) -> NameId {
5975        self.data.header().sample_text_name_id.get()
5976    }
5977
5978    /// Number of named parameters. (May be zero.)
5979    pub fn num_named_parameters(&self) -> u16 {
5980        self.data.header().num_named_parameters.get()
5981    }
5982
5983    /// The first 'name' table name ID used to specify strings for
5984    /// user-interface labels for the feature parameters. (Must be zero
5985    /// if numParameters is zero.)
5986    pub fn first_param_ui_label_name_id(&self) -> NameId {
5987        self.data.header().first_param_ui_label_name_id.get()
5988    }
5989
5990    /// The count of characters for which this feature provides glyph
5991    /// variants. (May be zero.)
5992    pub fn char_count(&self) -> u16 {
5993        self.data.header().char_count.get()
5994    }
5995
5996    /// The Unicode Scalar Value of the characters for which this
5997    /// feature provides glyph variants.
5998    pub fn character(&self) -> &'a [BigEndian<Uint24>] {
5999        let range = self.character_byte_range();
6000        self.offset_data()
6001            .read_array(range)
6002            .ok()
6003            .unwrap_or_default()
6004    }
6005
6006    pub fn format_byte_range(&self) -> Range<usize> {
6007        let start = 0;
6008        let end = start + u16::RAW_BYTE_LEN;
6009        start..end
6010    }
6011
6012    pub fn feat_ui_label_name_id_byte_range(&self) -> Range<usize> {
6013        let start = self.format_byte_range().end;
6014        let end = start + NameId::RAW_BYTE_LEN;
6015        start..end
6016    }
6017
6018    pub fn feat_ui_tooltip_text_name_id_byte_range(&self) -> Range<usize> {
6019        let start = self.feat_ui_label_name_id_byte_range().end;
6020        let end = start + NameId::RAW_BYTE_LEN;
6021        start..end
6022    }
6023
6024    pub fn sample_text_name_id_byte_range(&self) -> Range<usize> {
6025        let start = self.feat_ui_tooltip_text_name_id_byte_range().end;
6026        let end = start + NameId::RAW_BYTE_LEN;
6027        start..end
6028    }
6029
6030    pub fn num_named_parameters_byte_range(&self) -> Range<usize> {
6031        let start = self.sample_text_name_id_byte_range().end;
6032        let end = start + u16::RAW_BYTE_LEN;
6033        start..end
6034    }
6035
6036    pub fn first_param_ui_label_name_id_byte_range(&self) -> Range<usize> {
6037        let start = self.num_named_parameters_byte_range().end;
6038        let end = start + NameId::RAW_BYTE_LEN;
6039        start..end
6040    }
6041
6042    pub fn char_count_byte_range(&self) -> Range<usize> {
6043        let start = self.first_param_ui_label_name_id_byte_range().end;
6044        let end = start + u16::RAW_BYTE_LEN;
6045        start..end
6046    }
6047
6048    pub fn character_byte_range(&self) -> Range<usize> {
6049        let char_count = self.char_count();
6050        let start = self.char_count_byte_range().end;
6051        let end = start + (transforms::to_usize(char_count)).saturating_mul(Uint24::RAW_BYTE_LEN);
6052        start..end
6053    }
6054}
6055
6056const _: () = assert!(FontData::default_data_long_enough(
6057    CharacterVariantParams::MIN_SIZE
6058));
6059
6060impl Default for CharacterVariantParams<'_> {
6061    fn default() -> Self {
6062        Self {
6063            data: TableDataRef::new(FontData::default_table_data().as_bytes())
6064                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
6065        }
6066    }
6067}
6068
6069impl Format<u16> for CoverageFormat3<'_> {
6070    const FORMAT: u16 = 3;
6071}
6072
6073impl<'a> MinByteRange<'a> for CoverageFormat3<'a> {
6074    fn min_byte_range(&self) -> Range<usize> {
6075        0..self.glyph_array_byte_range().end
6076    }
6077    fn min_table_bytes(&self) -> &'a [u8] {
6078        let range = self.min_byte_range();
6079        self.offset_data().as_bytes().get(range).unwrap_or_default()
6080    }
6081}
6082
6083impl ReadArgs for CoverageFormat3<'_> {
6084    type Args = ();
6085}
6086
6087impl<'a> FontRead<'a> for CoverageFormat3<'a> {
6088    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6089        #[allow(clippy::absurd_extreme_comparisons)]
6090        if data.len() < Self::MIN_SIZE {
6091            return Err(ReadError::OutOfBounds);
6092        }
6093        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6094            return Err(ReadError::OutOfBounds);
6095        };
6096        Ok(Self { data })
6097    }
6098}
6099
6100/// The fields at the start of the table, which are read directly.
6101#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6102#[repr(C, packed)]
6103#[doc(hidden)]
6104pub struct CoverageFormat3FixedHeader {
6105    pub coverage_format: BigEndian<u16>,
6106    pub glyph_count: BigEndian<Uint24>,
6107}
6108
6109#[allow(clippy::absurd_extreme_comparisons)]
6110const _: () =
6111    assert!(std::mem::size_of::<CoverageFormat3FixedHeader>() <= CoverageFormat3::MIN_SIZE);
6112
6113/// CoverageFormat3 (ISO/IEC 14496-22:2026).
6114#[derive(Clone)]
6115pub struct CoverageFormat3<'a> {
6116    data: TableDataRef<'a, CoverageFormat3FixedHeader>,
6117}
6118
6119#[allow(clippy::needless_lifetimes)]
6120impl<'a> CoverageFormat3<'a> {
6121    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
6122    basic_table_impls!(impl_the_methods);
6123
6124    /// Format identifier — format = 3
6125    pub fn coverage_format(&self) -> u16 {
6126        self.data.header().coverage_format.get()
6127    }
6128
6129    /// Number of glyphs in the glyph array
6130    pub fn glyph_count(&self) -> Uint24 {
6131        self.data.header().glyph_count.get()
6132    }
6133
6134    /// Array of glyph IDs — in numerical order
6135    pub fn glyph_array(&self) -> &'a [BigEndian<GlyphId24>] {
6136        let range = self.glyph_array_byte_range();
6137        self.offset_data()
6138            .read_array(range)
6139            .ok()
6140            .unwrap_or_default()
6141    }
6142
6143    pub fn coverage_format_byte_range(&self) -> Range<usize> {
6144        let start = 0;
6145        let end = start + u16::RAW_BYTE_LEN;
6146        start..end
6147    }
6148
6149    pub fn glyph_count_byte_range(&self) -> Range<usize> {
6150        let start = self.coverage_format_byte_range().end;
6151        let end = start + Uint24::RAW_BYTE_LEN;
6152        start..end
6153    }
6154
6155    pub fn glyph_array_byte_range(&self) -> Range<usize> {
6156        let glyph_count = self.glyph_count();
6157        let start = self.glyph_count_byte_range().end;
6158        let end =
6159            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
6160        start..end
6161    }
6162}
6163
6164impl Format<u16> for CoverageFormat4<'_> {
6165    const FORMAT: u16 = 4;
6166}
6167
6168impl<'a> MinByteRange<'a> for CoverageFormat4<'a> {
6169    fn min_byte_range(&self) -> Range<usize> {
6170        0..self.range_records_byte_range().end
6171    }
6172    fn min_table_bytes(&self) -> &'a [u8] {
6173        let range = self.min_byte_range();
6174        self.offset_data().as_bytes().get(range).unwrap_or_default()
6175    }
6176}
6177
6178impl ReadArgs for CoverageFormat4<'_> {
6179    type Args = ();
6180}
6181
6182impl<'a> FontRead<'a> for CoverageFormat4<'a> {
6183    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6184        #[allow(clippy::absurd_extreme_comparisons)]
6185        if data.len() < Self::MIN_SIZE {
6186            return Err(ReadError::OutOfBounds);
6187        }
6188        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6189            return Err(ReadError::OutOfBounds);
6190        };
6191        Ok(Self { data })
6192    }
6193}
6194
6195/// The fields at the start of the table, which are read directly.
6196#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6197#[repr(C, packed)]
6198#[doc(hidden)]
6199pub struct CoverageFormat4FixedHeader {
6200    pub coverage_format: BigEndian<u16>,
6201    pub range_count: BigEndian<Uint24>,
6202}
6203
6204#[allow(clippy::absurd_extreme_comparisons)]
6205const _: () =
6206    assert!(std::mem::size_of::<CoverageFormat4FixedHeader>() <= CoverageFormat4::MIN_SIZE);
6207
6208/// CoverageFormat4 (ISO/IEC 14496-22:2026).
6209#[derive(Clone)]
6210pub struct CoverageFormat4<'a> {
6211    data: TableDataRef<'a, CoverageFormat4FixedHeader>,
6212}
6213
6214#[allow(clippy::needless_lifetimes)]
6215impl<'a> CoverageFormat4<'a> {
6216    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
6217    basic_table_impls!(impl_the_methods);
6218
6219    /// Format identifier — format = 4
6220    pub fn coverage_format(&self) -> u16 {
6221        self.data.header().coverage_format.get()
6222    }
6223
6224    /// Number of RangeRecords
6225    pub fn range_count(&self) -> Uint24 {
6226        self.data.header().range_count.get()
6227    }
6228
6229    /// Array of glyph ranges — ordered by startGlyphID.
6230    pub fn range_records(&self) -> &'a [RangeRecord2] {
6231        let range = self.range_records_byte_range();
6232        self.offset_data()
6233            .read_array(range)
6234            .ok()
6235            .unwrap_or_default()
6236    }
6237
6238    pub fn coverage_format_byte_range(&self) -> Range<usize> {
6239        let start = 0;
6240        let end = start + u16::RAW_BYTE_LEN;
6241        start..end
6242    }
6243
6244    pub fn range_count_byte_range(&self) -> Range<usize> {
6245        let start = self.coverage_format_byte_range().end;
6246        let end = start + Uint24::RAW_BYTE_LEN;
6247        start..end
6248    }
6249
6250    pub fn range_records_byte_range(&self) -> Range<usize> {
6251        let range_count = self.range_count();
6252        let start = self.range_count_byte_range().end;
6253        let end =
6254            start + (transforms::to_usize(range_count)).saturating_mul(RangeRecord2::RAW_BYTE_LEN);
6255        start..end
6256    }
6257}
6258
6259/// RangeRecord2 (ISO/IEC 14496-22:2026).
6260#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
6261#[repr(C)]
6262#[repr(packed)]
6263pub struct RangeRecord2 {
6264    /// First glyph ID in the range
6265    pub start_glyph_id: BigEndian<GlyphId24>,
6266    /// Last glyph ID in the range
6267    pub end_glyph_id: BigEndian<GlyphId24>,
6268    /// Coverage Index of first glyph ID in range
6269    pub start_coverage_index: BigEndian<Uint24>,
6270}
6271
6272impl RangeRecord2 {
6273    /// First glyph ID in the range
6274    pub fn start_glyph_id(&self) -> GlyphId24 {
6275        self.start_glyph_id.get()
6276    }
6277
6278    /// Last glyph ID in the range
6279    pub fn end_glyph_id(&self) -> GlyphId24 {
6280        self.end_glyph_id.get()
6281    }
6282
6283    /// Coverage Index of first glyph ID in range
6284    pub fn start_coverage_index(&self) -> Uint24 {
6285        self.start_coverage_index.get()
6286    }
6287}
6288
6289impl FixedSize for RangeRecord2 {
6290    const RAW_BYTE_LEN: usize =
6291        GlyphId24::RAW_BYTE_LEN + GlyphId24::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN;
6292}
6293
6294impl Format<u16> for ClassDefFormat3<'_> {
6295    const FORMAT: u16 = 3;
6296}
6297
6298impl<'a> MinByteRange<'a> for ClassDefFormat3<'a> {
6299    fn min_byte_range(&self) -> Range<usize> {
6300        0..self.class_value_array_byte_range().end
6301    }
6302    fn min_table_bytes(&self) -> &'a [u8] {
6303        let range = self.min_byte_range();
6304        self.offset_data().as_bytes().get(range).unwrap_or_default()
6305    }
6306}
6307
6308impl ReadArgs for ClassDefFormat3<'_> {
6309    type Args = ();
6310}
6311
6312impl<'a> FontRead<'a> for ClassDefFormat3<'a> {
6313    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6314        #[allow(clippy::absurd_extreme_comparisons)]
6315        if data.len() < Self::MIN_SIZE {
6316            return Err(ReadError::OutOfBounds);
6317        }
6318        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6319            return Err(ReadError::OutOfBounds);
6320        };
6321        Ok(Self { data })
6322    }
6323}
6324
6325/// The fields at the start of the table, which are read directly.
6326#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6327#[repr(C, packed)]
6328#[doc(hidden)]
6329pub struct ClassDefFormat3FixedHeader {
6330    pub class_format: BigEndian<u16>,
6331    pub start_glyph_id: BigEndian<GlyphId24>,
6332    pub glyph_count: BigEndian<Uint24>,
6333}
6334
6335#[allow(clippy::absurd_extreme_comparisons)]
6336const _: () =
6337    assert!(std::mem::size_of::<ClassDefFormat3FixedHeader>() <= ClassDefFormat3::MIN_SIZE);
6338
6339/// ClassDefFormat3 (ISO/IEC 14496-22:2026).
6340#[derive(Clone)]
6341pub struct ClassDefFormat3<'a> {
6342    data: TableDataRef<'a, ClassDefFormat3FixedHeader>,
6343}
6344
6345#[allow(clippy::needless_lifetimes)]
6346impl<'a> ClassDefFormat3<'a> {
6347    pub const MIN_SIZE: usize =
6348        (u16::RAW_BYTE_LEN + GlyphId24::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
6349    basic_table_impls!(impl_the_methods);
6350
6351    /// Format identifier — format = 3
6352    pub fn class_format(&self) -> u16 {
6353        self.data.header().class_format.get()
6354    }
6355
6356    /// First glyph ID of the classValueArray
6357    pub fn start_glyph_id(&self) -> GlyphId24 {
6358        self.data.header().start_glyph_id.get()
6359    }
6360
6361    /// Size of the classValueArray
6362    pub fn glyph_count(&self) -> Uint24 {
6363        self.data.header().glyph_count.get()
6364    }
6365
6366    /// Array of Class Values — one per glyph ID
6367    pub fn class_value_array(&self) -> &'a [BigEndian<Uint24>] {
6368        let range = self.class_value_array_byte_range();
6369        self.offset_data()
6370            .read_array(range)
6371            .ok()
6372            .unwrap_or_default()
6373    }
6374
6375    pub fn class_format_byte_range(&self) -> Range<usize> {
6376        let start = 0;
6377        let end = start + u16::RAW_BYTE_LEN;
6378        start..end
6379    }
6380
6381    pub fn start_glyph_id_byte_range(&self) -> Range<usize> {
6382        let start = self.class_format_byte_range().end;
6383        let end = start + GlyphId24::RAW_BYTE_LEN;
6384        start..end
6385    }
6386
6387    pub fn glyph_count_byte_range(&self) -> Range<usize> {
6388        let start = self.start_glyph_id_byte_range().end;
6389        let end = start + Uint24::RAW_BYTE_LEN;
6390        start..end
6391    }
6392
6393    pub fn class_value_array_byte_range(&self) -> Range<usize> {
6394        let glyph_count = self.glyph_count();
6395        let start = self.glyph_count_byte_range().end;
6396        let end = start + (transforms::to_usize(glyph_count)).saturating_mul(Uint24::RAW_BYTE_LEN);
6397        start..end
6398    }
6399}
6400
6401impl Format<u16> for ClassDefFormat4<'_> {
6402    const FORMAT: u16 = 4;
6403}
6404
6405impl<'a> MinByteRange<'a> for ClassDefFormat4<'a> {
6406    fn min_byte_range(&self) -> Range<usize> {
6407        0..self.class_range_records_byte_range().end
6408    }
6409    fn min_table_bytes(&self) -> &'a [u8] {
6410        let range = self.min_byte_range();
6411        self.offset_data().as_bytes().get(range).unwrap_or_default()
6412    }
6413}
6414
6415impl ReadArgs for ClassDefFormat4<'_> {
6416    type Args = ();
6417}
6418
6419impl<'a> FontRead<'a> for ClassDefFormat4<'a> {
6420    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6421        #[allow(clippy::absurd_extreme_comparisons)]
6422        if data.len() < Self::MIN_SIZE {
6423            return Err(ReadError::OutOfBounds);
6424        }
6425        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6426            return Err(ReadError::OutOfBounds);
6427        };
6428        Ok(Self { data })
6429    }
6430}
6431
6432/// The fields at the start of the table, which are read directly.
6433#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6434#[repr(C, packed)]
6435#[doc(hidden)]
6436pub struct ClassDefFormat4FixedHeader {
6437    pub class_format: BigEndian<u16>,
6438    pub class_range_count: BigEndian<Uint24>,
6439}
6440
6441#[allow(clippy::absurd_extreme_comparisons)]
6442const _: () =
6443    assert!(std::mem::size_of::<ClassDefFormat4FixedHeader>() <= ClassDefFormat4::MIN_SIZE);
6444
6445/// ClassDefFormat4 (ISO/IEC 14496-22:2026).
6446#[derive(Clone)]
6447pub struct ClassDefFormat4<'a> {
6448    data: TableDataRef<'a, ClassDefFormat4FixedHeader>,
6449}
6450
6451#[allow(clippy::needless_lifetimes)]
6452impl<'a> ClassDefFormat4<'a> {
6453    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
6454    basic_table_impls!(impl_the_methods);
6455
6456    /// Format identifier — format = 4
6457    pub fn class_format(&self) -> u16 {
6458        self.data.header().class_format.get()
6459    }
6460
6461    /// Number of ClassRangeRecords
6462    pub fn class_range_count(&self) -> Uint24 {
6463        self.data.header().class_range_count.get()
6464    }
6465
6466    /// Array of ClassRangeRecords — ordered by startGlyphID
6467    pub fn class_range_records(&self) -> &'a [ClassRangeRecord2] {
6468        let range = self.class_range_records_byte_range();
6469        self.offset_data()
6470            .read_array(range)
6471            .ok()
6472            .unwrap_or_default()
6473    }
6474
6475    pub fn class_format_byte_range(&self) -> Range<usize> {
6476        let start = 0;
6477        let end = start + u16::RAW_BYTE_LEN;
6478        start..end
6479    }
6480
6481    pub fn class_range_count_byte_range(&self) -> Range<usize> {
6482        let start = self.class_format_byte_range().end;
6483        let end = start + Uint24::RAW_BYTE_LEN;
6484        start..end
6485    }
6486
6487    pub fn class_range_records_byte_range(&self) -> Range<usize> {
6488        let class_range_count = self.class_range_count();
6489        let start = self.class_range_count_byte_range().end;
6490        let end = start
6491            + (transforms::to_usize(class_range_count))
6492                .saturating_mul(ClassRangeRecord2::RAW_BYTE_LEN);
6493        start..end
6494    }
6495}
6496
6497/// ClassRangeRecord2 (ISO/IEC 14496-22:2026).
6498#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
6499#[repr(C)]
6500#[repr(packed)]
6501pub struct ClassRangeRecord2 {
6502    /// First glyph ID in the range
6503    pub start_glyph_id: BigEndian<GlyphId24>,
6504    /// Last glyph ID in the range
6505    pub end_glyph_id: BigEndian<GlyphId24>,
6506    /// Applied to all glyphs in the range
6507    pub class: BigEndian<u16>,
6508}
6509
6510impl ClassRangeRecord2 {
6511    /// First glyph ID in the range
6512    pub fn start_glyph_id(&self) -> GlyphId24 {
6513        self.start_glyph_id.get()
6514    }
6515
6516    /// Last glyph ID in the range
6517    pub fn end_glyph_id(&self) -> GlyphId24 {
6518        self.end_glyph_id.get()
6519    }
6520
6521    /// Applied to all glyphs in the range
6522    pub fn class(&self) -> u16 {
6523        self.class.get()
6524    }
6525}
6526
6527impl FixedSize for ClassRangeRecord2 {
6528    const RAW_BYTE_LEN: usize =
6529        GlyphId24::RAW_BYTE_LEN + GlyphId24::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
6530}
6531
6532impl Format<u16> for SequenceContextFormat4<'_> {
6533    const FORMAT: u16 = 4;
6534}
6535
6536impl<'a> MinByteRange<'a> for SequenceContextFormat4<'a> {
6537    fn min_byte_range(&self) -> Range<usize> {
6538        0..self.seq_rule_set_offsets_byte_range().end
6539    }
6540    fn min_table_bytes(&self) -> &'a [u8] {
6541        let range = self.min_byte_range();
6542        self.offset_data().as_bytes().get(range).unwrap_or_default()
6543    }
6544}
6545
6546impl ReadArgs for SequenceContextFormat4<'_> {
6547    type Args = ();
6548}
6549
6550impl<'a> FontRead<'a> for SequenceContextFormat4<'a> {
6551    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6552        #[allow(clippy::absurd_extreme_comparisons)]
6553        if data.len() < Self::MIN_SIZE {
6554            return Err(ReadError::OutOfBounds);
6555        }
6556        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6557            return Err(ReadError::OutOfBounds);
6558        };
6559        Ok(Self { data })
6560    }
6561}
6562
6563/// The fields at the start of the table, which are read directly.
6564#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6565#[repr(C, packed)]
6566#[doc(hidden)]
6567pub struct SequenceContextFormat4FixedHeader {
6568    pub format: BigEndian<u16>,
6569    pub coverage_offset: BigEndian<Offset32>,
6570    pub seq_rule_set_count: BigEndian<Uint24>,
6571}
6572
6573#[allow(clippy::absurd_extreme_comparisons)]
6574const _: () = assert!(
6575    std::mem::size_of::<SequenceContextFormat4FixedHeader>() <= SequenceContextFormat4::MIN_SIZE
6576);
6577
6578/// SequenceContextFormat4 (ISO/IEC 14496-22:2026).
6579#[derive(Clone)]
6580pub struct SequenceContextFormat4<'a> {
6581    data: TableDataRef<'a, SequenceContextFormat4FixedHeader>,
6582}
6583
6584#[allow(clippy::needless_lifetimes)]
6585impl<'a> SequenceContextFormat4<'a> {
6586    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
6587    basic_table_impls!(impl_the_methods);
6588
6589    /// Format identifier: format = 4
6590    pub fn format(&self) -> u16 {
6591        self.data.header().format.get()
6592    }
6593
6594    /// Offset to Coverage table, from beginning of
6595    /// SequenceContextFormat4 table
6596    pub fn coverage_offset(&self) -> Offset32 {
6597        self.data.header().coverage_offset.get()
6598    }
6599
6600    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
6601    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
6602        let data = self.offset_data();
6603        self.coverage_offset().resolve(data)
6604    }
6605
6606    /// Number of SequenceRuleSet tables
6607    pub fn seq_rule_set_count(&self) -> Uint24 {
6608        self.data.header().seq_rule_set_count.get()
6609    }
6610
6611    /// Array of offsets to SequenceRuleSet tables, from beginning of
6612    /// SequenceContextFormat4 table (offsets may be NULL)
6613    pub fn seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
6614        let range = self.seq_rule_set_offsets_byte_range();
6615        self.offset_data()
6616            .read_array(range)
6617            .ok()
6618            .unwrap_or_default()
6619    }
6620
6621    /// A dynamically resolving wrapper for [`seq_rule_set_offsets`][Self::seq_rule_set_offsets].
6622    pub fn seq_rule_sets(&self) -> ArrayOfNullableOffsets<'a, SequenceRuleSet2<'a>, Offset24> {
6623        let data = self.offset_data();
6624        let offsets = self.seq_rule_set_offsets();
6625        ArrayOfNullableOffsets::new(offsets, data, ())
6626    }
6627
6628    pub fn format_byte_range(&self) -> Range<usize> {
6629        let start = 0;
6630        let end = start + u16::RAW_BYTE_LEN;
6631        start..end
6632    }
6633
6634    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
6635        let start = self.format_byte_range().end;
6636        let end = start + Offset32::RAW_BYTE_LEN;
6637        start..end
6638    }
6639
6640    pub fn seq_rule_set_count_byte_range(&self) -> Range<usize> {
6641        let start = self.coverage_offset_byte_range().end;
6642        let end = start + Uint24::RAW_BYTE_LEN;
6643        start..end
6644    }
6645
6646    pub fn seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
6647        let seq_rule_set_count = self.seq_rule_set_count();
6648        let start = self.seq_rule_set_count_byte_range().end;
6649        let end = start
6650            + (transforms::to_usize(seq_rule_set_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
6651        start..end
6652    }
6653}
6654
6655impl<'a> MinByteRange<'a> for SequenceRuleSet2<'a> {
6656    fn min_byte_range(&self) -> Range<usize> {
6657        0..self.seq_rule_offsets_byte_range().end
6658    }
6659    fn min_table_bytes(&self) -> &'a [u8] {
6660        let range = self.min_byte_range();
6661        self.offset_data().as_bytes().get(range).unwrap_or_default()
6662    }
6663}
6664
6665impl ReadArgs for SequenceRuleSet2<'_> {
6666    type Args = ();
6667}
6668
6669impl<'a> FontRead<'a> for SequenceRuleSet2<'a> {
6670    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6671        #[allow(clippy::absurd_extreme_comparisons)]
6672        if data.len() < Self::MIN_SIZE {
6673            return Err(ReadError::OutOfBounds);
6674        }
6675        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6676            return Err(ReadError::OutOfBounds);
6677        };
6678        Ok(Self { data })
6679    }
6680}
6681
6682/// The fields at the start of the table, which are read directly.
6683#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6684#[repr(C, packed)]
6685#[doc(hidden)]
6686pub struct SequenceRuleSet2FixedHeader {
6687    pub seq_rule_count: BigEndian<u16>,
6688}
6689
6690#[allow(clippy::absurd_extreme_comparisons)]
6691const _: () =
6692    assert!(std::mem::size_of::<SequenceRuleSet2FixedHeader>() <= SequenceRuleSet2::MIN_SIZE);
6693
6694/// SequenceRuleSet2 (ISO/IEC 14496-22:2026).
6695#[derive(Clone)]
6696pub struct SequenceRuleSet2<'a> {
6697    data: TableDataRef<'a, SequenceRuleSet2FixedHeader>,
6698}
6699
6700#[allow(clippy::needless_lifetimes)]
6701impl<'a> SequenceRuleSet2<'a> {
6702    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
6703    basic_table_impls!(impl_the_methods);
6704
6705    /// Number of SequenceRule tables
6706    pub fn seq_rule_count(&self) -> u16 {
6707        self.data.header().seq_rule_count.get()
6708    }
6709
6710    /// Array of offsets to SequenceRule tables, from beginning of the
6711    /// SequenceRuleSet2 table
6712    pub fn seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
6713        let range = self.seq_rule_offsets_byte_range();
6714        self.offset_data()
6715            .read_array(range)
6716            .ok()
6717            .unwrap_or_default()
6718    }
6719
6720    /// A dynamically resolving wrapper for [`seq_rule_offsets`][Self::seq_rule_offsets].
6721    pub fn seq_rules(&self) -> ArrayOfOffsets<'a, SequenceRule2<'a>, Offset16> {
6722        let data = self.offset_data();
6723        let offsets = self.seq_rule_offsets();
6724        ArrayOfOffsets::new(offsets, data, ())
6725    }
6726
6727    pub fn seq_rule_count_byte_range(&self) -> Range<usize> {
6728        let start = 0;
6729        let end = start + u16::RAW_BYTE_LEN;
6730        start..end
6731    }
6732
6733    pub fn seq_rule_offsets_byte_range(&self) -> Range<usize> {
6734        let seq_rule_count = self.seq_rule_count();
6735        let start = self.seq_rule_count_byte_range().end;
6736        let end =
6737            start + (transforms::to_usize(seq_rule_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
6738        start..end
6739    }
6740}
6741
6742const _: () = assert!(FontData::default_data_long_enough(
6743    SequenceRuleSet2::MIN_SIZE
6744));
6745
6746impl Default for SequenceRuleSet2<'_> {
6747    fn default() -> Self {
6748        Self {
6749            data: TableDataRef::new(FontData::default_table_data().as_bytes())
6750                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
6751        }
6752    }
6753}
6754
6755impl<'a> MinByteRange<'a> for SequenceRule2<'a> {
6756    fn min_byte_range(&self) -> Range<usize> {
6757        0..self.seq_lookup_records_byte_range().end
6758    }
6759    fn min_table_bytes(&self) -> &'a [u8] {
6760        let range = self.min_byte_range();
6761        self.offset_data().as_bytes().get(range).unwrap_or_default()
6762    }
6763}
6764
6765impl ReadArgs for SequenceRule2<'_> {
6766    type Args = ();
6767}
6768
6769impl<'a> FontRead<'a> for SequenceRule2<'a> {
6770    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6771        #[allow(clippy::absurd_extreme_comparisons)]
6772        if data.len() < Self::MIN_SIZE {
6773            return Err(ReadError::OutOfBounds);
6774        }
6775        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6776            return Err(ReadError::OutOfBounds);
6777        };
6778        Ok(Self { data })
6779    }
6780}
6781
6782/// The fields at the start of the table, which are read directly.
6783#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6784#[repr(C, packed)]
6785#[doc(hidden)]
6786pub struct SequenceRule2FixedHeader {
6787    pub glyph_count: BigEndian<u16>,
6788    pub seq_lookup_count: BigEndian<u16>,
6789}
6790
6791#[allow(clippy::absurd_extreme_comparisons)]
6792const _: () = assert!(std::mem::size_of::<SequenceRule2FixedHeader>() <= SequenceRule2::MIN_SIZE);
6793
6794/// SequenceRule2 (ISO/IEC 14496-22:2026).
6795#[derive(Clone)]
6796pub struct SequenceRule2<'a> {
6797    data: TableDataRef<'a, SequenceRule2FixedHeader>,
6798}
6799
6800#[allow(clippy::needless_lifetimes)]
6801impl<'a> SequenceRule2<'a> {
6802    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
6803    basic_table_impls!(impl_the_methods);
6804
6805    /// Number of glyphs in the input glyph sequence
6806    pub fn glyph_count(&self) -> u16 {
6807        self.data.header().glyph_count.get()
6808    }
6809
6810    /// Number of SequenceLookupRecords
6811    pub fn seq_lookup_count(&self) -> u16 {
6812        self.data.header().seq_lookup_count.get()
6813    }
6814
6815    /// Array of input glyph IDs—starting with the second glyph
6816    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId24>] {
6817        let range = self.input_sequence_byte_range();
6818        self.offset_data()
6819            .read_array(range)
6820            .ok()
6821            .unwrap_or_default()
6822    }
6823
6824    /// Array of Sequence lookup records
6825    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
6826        let range = self.seq_lookup_records_byte_range();
6827        self.offset_data()
6828            .read_array(range)
6829            .ok()
6830            .unwrap_or_default()
6831    }
6832
6833    pub fn glyph_count_byte_range(&self) -> Range<usize> {
6834        let start = 0;
6835        let end = start + u16::RAW_BYTE_LEN;
6836        start..end
6837    }
6838
6839    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
6840        let start = self.glyph_count_byte_range().end;
6841        let end = start + u16::RAW_BYTE_LEN;
6842        start..end
6843    }
6844
6845    pub fn input_sequence_byte_range(&self) -> Range<usize> {
6846        let glyph_count = self.glyph_count();
6847        let start = self.seq_lookup_count_byte_range().end;
6848        let end = start
6849            + (transforms::subtract(glyph_count, 1_usize)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
6850        start..end
6851    }
6852
6853    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
6854        let seq_lookup_count = self.seq_lookup_count();
6855        let start = self.input_sequence_byte_range().end;
6856        let end = start
6857            + (transforms::to_usize(seq_lookup_count))
6858                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
6859        start..end
6860    }
6861}
6862
6863const _: () = assert!(FontData::default_data_long_enough(SequenceRule2::MIN_SIZE));
6864
6865impl Default for SequenceRule2<'_> {
6866    fn default() -> Self {
6867        Self {
6868            data: TableDataRef::new(FontData::default_table_data().as_bytes())
6869                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
6870        }
6871    }
6872}
6873
6874impl Format<u16> for SequenceContextFormat5<'_> {
6875    const FORMAT: u16 = 5;
6876}
6877
6878impl<'a> MinByteRange<'a> for SequenceContextFormat5<'a> {
6879    fn min_byte_range(&self) -> Range<usize> {
6880        0..self.class_seq_rule_set_offsets_byte_range().end
6881    }
6882    fn min_table_bytes(&self) -> &'a [u8] {
6883        let range = self.min_byte_range();
6884        self.offset_data().as_bytes().get(range).unwrap_or_default()
6885    }
6886}
6887
6888impl ReadArgs for SequenceContextFormat5<'_> {
6889    type Args = ();
6890}
6891
6892impl<'a> FontRead<'a> for SequenceContextFormat5<'a> {
6893    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6894        #[allow(clippy::absurd_extreme_comparisons)]
6895        if data.len() < Self::MIN_SIZE {
6896            return Err(ReadError::OutOfBounds);
6897        }
6898        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6899            return Err(ReadError::OutOfBounds);
6900        };
6901        Ok(Self { data })
6902    }
6903}
6904
6905/// The fields at the start of the table, which are read directly.
6906#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6907#[repr(C, packed)]
6908#[doc(hidden)]
6909pub struct SequenceContextFormat5FixedHeader {
6910    pub format: BigEndian<u16>,
6911    pub coverage_offset: BigEndian<Offset32>,
6912    pub class_def_offset: BigEndian<Offset32>,
6913    pub class_seq_rule_set_count: BigEndian<Uint24>,
6914}
6915
6916#[allow(clippy::absurd_extreme_comparisons)]
6917const _: () = assert!(
6918    std::mem::size_of::<SequenceContextFormat5FixedHeader>() <= SequenceContextFormat5::MIN_SIZE
6919);
6920
6921/// SequenceContextFormat5 (ISO/IEC 14496-22:2026).
6922#[derive(Clone)]
6923pub struct SequenceContextFormat5<'a> {
6924    data: TableDataRef<'a, SequenceContextFormat5FixedHeader>,
6925}
6926
6927#[allow(clippy::needless_lifetimes)]
6928impl<'a> SequenceContextFormat5<'a> {
6929    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
6930        + Offset32::RAW_BYTE_LEN
6931        + Offset32::RAW_BYTE_LEN
6932        + Uint24::RAW_BYTE_LEN);
6933    basic_table_impls!(impl_the_methods);
6934
6935    /// Format identifier: format = 5
6936    pub fn format(&self) -> u16 {
6937        self.data.header().format.get()
6938    }
6939
6940    /// Offset to Coverage table, from beginning of
6941    /// SequenceContextFormat5 table
6942    pub fn coverage_offset(&self) -> Offset32 {
6943        self.data.header().coverage_offset.get()
6944    }
6945
6946    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
6947    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
6948        let data = self.offset_data();
6949        self.coverage_offset().resolve(data)
6950    }
6951
6952    /// Offset to ClassDef table, from beginning of
6953    /// SequenceContextFormat5 table
6954    pub fn class_def_offset(&self) -> Offset32 {
6955        self.data.header().class_def_offset.get()
6956    }
6957
6958    /// Attempt to resolve [`class_def_offset`][Self::class_def_offset].
6959    pub fn class_def(&self) -> Result<ClassDef<'a>, ReadError> {
6960        let data = self.offset_data();
6961        self.class_def_offset().resolve(data)
6962    }
6963
6964    /// Number of ClassSequenceRuleSet tables
6965    pub fn class_seq_rule_set_count(&self) -> Uint24 {
6966        self.data.header().class_seq_rule_set_count.get()
6967    }
6968
6969    /// Array of offsets to ClassSequenceRuleSet tables, from beginning
6970    /// of SequenceContextFormat5 table (may be NULL)
6971    pub fn class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
6972        let range = self.class_seq_rule_set_offsets_byte_range();
6973        self.offset_data()
6974            .read_array(range)
6975            .ok()
6976            .unwrap_or_default()
6977    }
6978
6979    /// A dynamically resolving wrapper for [`class_seq_rule_set_offsets`][Self::class_seq_rule_set_offsets].
6980    pub fn class_seq_rule_sets(
6981        &self,
6982    ) -> ArrayOfNullableOffsets<'a, ClassSequenceRuleSet2<'a>, Offset24> {
6983        let data = self.offset_data();
6984        let offsets = self.class_seq_rule_set_offsets();
6985        ArrayOfNullableOffsets::new(offsets, data, ())
6986    }
6987
6988    pub fn format_byte_range(&self) -> Range<usize> {
6989        let start = 0;
6990        let end = start + u16::RAW_BYTE_LEN;
6991        start..end
6992    }
6993
6994    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
6995        let start = self.format_byte_range().end;
6996        let end = start + Offset32::RAW_BYTE_LEN;
6997        start..end
6998    }
6999
7000    pub fn class_def_offset_byte_range(&self) -> Range<usize> {
7001        let start = self.coverage_offset_byte_range().end;
7002        let end = start + Offset32::RAW_BYTE_LEN;
7003        start..end
7004    }
7005
7006    pub fn class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
7007        let start = self.class_def_offset_byte_range().end;
7008        let end = start + Uint24::RAW_BYTE_LEN;
7009        start..end
7010    }
7011
7012    pub fn class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
7013        let class_seq_rule_set_count = self.class_seq_rule_set_count();
7014        let start = self.class_seq_rule_set_count_byte_range().end;
7015        let end = start
7016            + (transforms::to_usize(class_seq_rule_set_count))
7017                .saturating_mul(Offset24::RAW_BYTE_LEN);
7018        start..end
7019    }
7020}
7021
7022impl<'a> MinByteRange<'a> for ClassSequenceRuleSet2<'a> {
7023    fn min_byte_range(&self) -> Range<usize> {
7024        0..self.class_seq_rule_offsets_byte_range().end
7025    }
7026    fn min_table_bytes(&self) -> &'a [u8] {
7027        let range = self.min_byte_range();
7028        self.offset_data().as_bytes().get(range).unwrap_or_default()
7029    }
7030}
7031
7032impl ReadArgs for ClassSequenceRuleSet2<'_> {
7033    type Args = ();
7034}
7035
7036impl<'a> FontRead<'a> for ClassSequenceRuleSet2<'a> {
7037    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7038        #[allow(clippy::absurd_extreme_comparisons)]
7039        if data.len() < Self::MIN_SIZE {
7040            return Err(ReadError::OutOfBounds);
7041        }
7042        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7043            return Err(ReadError::OutOfBounds);
7044        };
7045        Ok(Self { data })
7046    }
7047}
7048
7049/// The fields at the start of the table, which are read directly.
7050#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7051#[repr(C, packed)]
7052#[doc(hidden)]
7053pub struct ClassSequenceRuleSet2FixedHeader {
7054    pub class_seq_rule_count: BigEndian<u16>,
7055}
7056
7057#[allow(clippy::absurd_extreme_comparisons)]
7058const _: () = assert!(
7059    std::mem::size_of::<ClassSequenceRuleSet2FixedHeader>() <= ClassSequenceRuleSet2::MIN_SIZE
7060);
7061
7062/// ClassSequenceRuleSet2 (ISO/IEC 14496-22:2026).
7063#[derive(Clone)]
7064pub struct ClassSequenceRuleSet2<'a> {
7065    data: TableDataRef<'a, ClassSequenceRuleSet2FixedHeader>,
7066}
7067
7068#[allow(clippy::needless_lifetimes)]
7069impl<'a> ClassSequenceRuleSet2<'a> {
7070    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
7071    basic_table_impls!(impl_the_methods);
7072
7073    /// Number of ClassSequenceRule tables
7074    pub fn class_seq_rule_count(&self) -> u16 {
7075        self.data.header().class_seq_rule_count.get()
7076    }
7077
7078    /// Array of offsets to ClassSequenceRule tables, from beginning of
7079    /// ClassSequenceRuleSet2 table
7080    pub fn class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset24>] {
7081        let range = self.class_seq_rule_offsets_byte_range();
7082        self.offset_data()
7083            .read_array(range)
7084            .ok()
7085            .unwrap_or_default()
7086    }
7087
7088    /// A dynamically resolving wrapper for [`class_seq_rule_offsets`][Self::class_seq_rule_offsets].
7089    pub fn class_seq_rules(&self) -> ArrayOfOffsets<'a, ClassSequenceRule<'a>, Offset24> {
7090        let data = self.offset_data();
7091        let offsets = self.class_seq_rule_offsets();
7092        ArrayOfOffsets::new(offsets, data, ())
7093    }
7094
7095    pub fn class_seq_rule_count_byte_range(&self) -> Range<usize> {
7096        let start = 0;
7097        let end = start + u16::RAW_BYTE_LEN;
7098        start..end
7099    }
7100
7101    pub fn class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
7102        let class_seq_rule_count = self.class_seq_rule_count();
7103        let start = self.class_seq_rule_count_byte_range().end;
7104        let end = start
7105            + (transforms::to_usize(class_seq_rule_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
7106        start..end
7107    }
7108}
7109
7110const _: () = assert!(FontData::default_data_long_enough(
7111    ClassSequenceRuleSet2::MIN_SIZE
7112));
7113
7114impl Default for ClassSequenceRuleSet2<'_> {
7115    fn default() -> Self {
7116        Self {
7117            data: TableDataRef::new(FontData::default_table_data().as_bytes())
7118                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
7119        }
7120    }
7121}
7122
7123impl Format<u16> for SequenceContextFormat6<'_> {
7124    const FORMAT: u16 = 6;
7125}
7126
7127impl<'a> MinByteRange<'a> for SequenceContextFormat6<'a> {
7128    fn min_byte_range(&self) -> Range<usize> {
7129        0..self.seq_lookup_records_byte_range().end
7130    }
7131    fn min_table_bytes(&self) -> &'a [u8] {
7132        let range = self.min_byte_range();
7133        self.offset_data().as_bytes().get(range).unwrap_or_default()
7134    }
7135}
7136
7137impl ReadArgs for SequenceContextFormat6<'_> {
7138    type Args = ();
7139}
7140
7141impl<'a> FontRead<'a> for SequenceContextFormat6<'a> {
7142    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7143        #[allow(clippy::absurd_extreme_comparisons)]
7144        if data.len() < Self::MIN_SIZE {
7145            return Err(ReadError::OutOfBounds);
7146        }
7147        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7148            return Err(ReadError::OutOfBounds);
7149        };
7150        Ok(Self { data })
7151    }
7152}
7153
7154/// The fields at the start of the table, which are read directly.
7155#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7156#[repr(C, packed)]
7157#[doc(hidden)]
7158pub struct SequenceContextFormat6FixedHeader {
7159    pub format: BigEndian<u16>,
7160    pub glyph_count: BigEndian<u16>,
7161    pub seq_lookup_count: BigEndian<u16>,
7162}
7163
7164#[allow(clippy::absurd_extreme_comparisons)]
7165const _: () = assert!(
7166    std::mem::size_of::<SequenceContextFormat6FixedHeader>() <= SequenceContextFormat6::MIN_SIZE
7167);
7168
7169/// SequenceContextFormat6 (ISO/IEC 14496-22:2026).
7170#[derive(Clone)]
7171pub struct SequenceContextFormat6<'a> {
7172    data: TableDataRef<'a, SequenceContextFormat6FixedHeader>,
7173}
7174
7175#[allow(clippy::needless_lifetimes)]
7176impl<'a> SequenceContextFormat6<'a> {
7177    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
7178    basic_table_impls!(impl_the_methods);
7179
7180    /// Format identifier: format = 6
7181    pub fn format(&self) -> u16 {
7182        self.data.header().format.get()
7183    }
7184
7185    /// Number of glyphs in the input sequence
7186    pub fn glyph_count(&self) -> u16 {
7187        self.data.header().glyph_count.get()
7188    }
7189
7190    /// Number of SequenceLookupRecords
7191    pub fn seq_lookup_count(&self) -> u16 {
7192        self.data.header().seq_lookup_count.get()
7193    }
7194
7195    /// Array of offsets to Coverage tables, from beginning of
7196    /// SequenceContextFormat6 subtable
7197    pub fn coverage_offsets(&self) -> &'a [BigEndian<Offset24>] {
7198        let range = self.coverage_offsets_byte_range();
7199        self.offset_data()
7200            .read_array(range)
7201            .ok()
7202            .unwrap_or_default()
7203    }
7204
7205    /// A dynamically resolving wrapper for [`coverage_offsets`][Self::coverage_offsets].
7206    pub fn coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset24> {
7207        let data = self.offset_data();
7208        let offsets = self.coverage_offsets();
7209        ArrayOfOffsets::new(offsets, data, ())
7210    }
7211
7212    /// Array of SequenceLookupRecords
7213    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
7214        let range = self.seq_lookup_records_byte_range();
7215        self.offset_data()
7216            .read_array(range)
7217            .ok()
7218            .unwrap_or_default()
7219    }
7220
7221    pub fn format_byte_range(&self) -> Range<usize> {
7222        let start = 0;
7223        let end = start + u16::RAW_BYTE_LEN;
7224        start..end
7225    }
7226
7227    pub fn glyph_count_byte_range(&self) -> Range<usize> {
7228        let start = self.format_byte_range().end;
7229        let end = start + u16::RAW_BYTE_LEN;
7230        start..end
7231    }
7232
7233    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
7234        let start = self.glyph_count_byte_range().end;
7235        let end = start + u16::RAW_BYTE_LEN;
7236        start..end
7237    }
7238
7239    pub fn coverage_offsets_byte_range(&self) -> Range<usize> {
7240        let glyph_count = self.glyph_count();
7241        let start = self.seq_lookup_count_byte_range().end;
7242        let end =
7243            start + (transforms::to_usize(glyph_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
7244        start..end
7245    }
7246
7247    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
7248        let seq_lookup_count = self.seq_lookup_count();
7249        let start = self.coverage_offsets_byte_range().end;
7250        let end = start
7251            + (transforms::to_usize(seq_lookup_count))
7252                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
7253        start..end
7254    }
7255}
7256
7257impl Format<u16> for ChainedSequenceContextFormat4<'_> {
7258    const FORMAT: u16 = 4;
7259}
7260
7261impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat4<'a> {
7262    fn min_byte_range(&self) -> Range<usize> {
7263        0..self.chained_seq_rule_set_offsets_byte_range().end
7264    }
7265    fn min_table_bytes(&self) -> &'a [u8] {
7266        let range = self.min_byte_range();
7267        self.offset_data().as_bytes().get(range).unwrap_or_default()
7268    }
7269}
7270
7271impl ReadArgs for ChainedSequenceContextFormat4<'_> {
7272    type Args = ();
7273}
7274
7275impl<'a> FontRead<'a> for ChainedSequenceContextFormat4<'a> {
7276    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7277        #[allow(clippy::absurd_extreme_comparisons)]
7278        if data.len() < Self::MIN_SIZE {
7279            return Err(ReadError::OutOfBounds);
7280        }
7281        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7282            return Err(ReadError::OutOfBounds);
7283        };
7284        Ok(Self { data })
7285    }
7286}
7287
7288/// The fields at the start of the table, which are read directly.
7289#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7290#[repr(C, packed)]
7291#[doc(hidden)]
7292pub struct ChainedSequenceContextFormat4FixedHeader {
7293    pub format: BigEndian<u16>,
7294    pub coverage_offset: BigEndian<Offset32>,
7295    pub chained_seq_rule_set_count: BigEndian<Uint24>,
7296}
7297
7298#[allow(clippy::absurd_extreme_comparisons)]
7299const _: () = assert!(
7300    std::mem::size_of::<ChainedSequenceContextFormat4FixedHeader>()
7301        <= ChainedSequenceContextFormat4::MIN_SIZE
7302);
7303
7304/// ChainedSequenceContextFormat4 (ISO/IEC 14496-22:2026).
7305#[derive(Clone)]
7306pub struct ChainedSequenceContextFormat4<'a> {
7307    data: TableDataRef<'a, ChainedSequenceContextFormat4FixedHeader>,
7308}
7309
7310#[allow(clippy::needless_lifetimes)]
7311impl<'a> ChainedSequenceContextFormat4<'a> {
7312    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
7313    basic_table_impls!(impl_the_methods);
7314
7315    /// Format identifier: format = 4
7316    pub fn format(&self) -> u16 {
7317        self.data.header().format.get()
7318    }
7319
7320    /// Offset to Coverage table, from beginning of
7321    /// ChainedSequenceContextFormat4 table
7322    pub fn coverage_offset(&self) -> Offset32 {
7323        self.data.header().coverage_offset.get()
7324    }
7325
7326    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
7327    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
7328        let data = self.offset_data();
7329        self.coverage_offset().resolve(data)
7330    }
7331
7332    /// Number of ChainedSequenceRuleSet tables
7333    pub fn chained_seq_rule_set_count(&self) -> Uint24 {
7334        self.data.header().chained_seq_rule_set_count.get()
7335    }
7336
7337    /// Array of offsets to ChainedSeqRuleSet tables, from beginning of
7338    /// ChainedSequenceContextFormat4 table (may be NULL)
7339    pub fn chained_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
7340        let range = self.chained_seq_rule_set_offsets_byte_range();
7341        self.offset_data()
7342            .read_array(range)
7343            .ok()
7344            .unwrap_or_default()
7345    }
7346
7347    /// A dynamically resolving wrapper for [`chained_seq_rule_set_offsets`][Self::chained_seq_rule_set_offsets].
7348    pub fn chained_seq_rule_sets(
7349        &self,
7350    ) -> ArrayOfNullableOffsets<'a, ChainedSequenceRuleSet2<'a>, Offset24> {
7351        let data = self.offset_data();
7352        let offsets = self.chained_seq_rule_set_offsets();
7353        ArrayOfNullableOffsets::new(offsets, data, ())
7354    }
7355
7356    pub fn format_byte_range(&self) -> Range<usize> {
7357        let start = 0;
7358        let end = start + u16::RAW_BYTE_LEN;
7359        start..end
7360    }
7361
7362    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
7363        let start = self.format_byte_range().end;
7364        let end = start + Offset32::RAW_BYTE_LEN;
7365        start..end
7366    }
7367
7368    pub fn chained_seq_rule_set_count_byte_range(&self) -> Range<usize> {
7369        let start = self.coverage_offset_byte_range().end;
7370        let end = start + Uint24::RAW_BYTE_LEN;
7371        start..end
7372    }
7373
7374    pub fn chained_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
7375        let chained_seq_rule_set_count = self.chained_seq_rule_set_count();
7376        let start = self.chained_seq_rule_set_count_byte_range().end;
7377        let end = start
7378            + (transforms::to_usize(chained_seq_rule_set_count))
7379                .saturating_mul(Offset24::RAW_BYTE_LEN);
7380        start..end
7381    }
7382}
7383
7384impl<'a> MinByteRange<'a> for ChainedSequenceRuleSet2<'a> {
7385    fn min_byte_range(&self) -> Range<usize> {
7386        0..self.chained_seq_rule_offsets_byte_range().end
7387    }
7388    fn min_table_bytes(&self) -> &'a [u8] {
7389        let range = self.min_byte_range();
7390        self.offset_data().as_bytes().get(range).unwrap_or_default()
7391    }
7392}
7393
7394impl ReadArgs for ChainedSequenceRuleSet2<'_> {
7395    type Args = ();
7396}
7397
7398impl<'a> FontRead<'a> for ChainedSequenceRuleSet2<'a> {
7399    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7400        #[allow(clippy::absurd_extreme_comparisons)]
7401        if data.len() < Self::MIN_SIZE {
7402            return Err(ReadError::OutOfBounds);
7403        }
7404        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7405            return Err(ReadError::OutOfBounds);
7406        };
7407        Ok(Self { data })
7408    }
7409}
7410
7411/// The fields at the start of the table, which are read directly.
7412#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7413#[repr(C, packed)]
7414#[doc(hidden)]
7415pub struct ChainedSequenceRuleSet2FixedHeader {
7416    pub chained_seq_rule_count: BigEndian<u16>,
7417}
7418
7419#[allow(clippy::absurd_extreme_comparisons)]
7420const _: () = assert!(
7421    std::mem::size_of::<ChainedSequenceRuleSet2FixedHeader>() <= ChainedSequenceRuleSet2::MIN_SIZE
7422);
7423
7424/// ChainedSequenceRuleSet2 (ISO/IEC 14496-22:2026).
7425#[derive(Clone)]
7426pub struct ChainedSequenceRuleSet2<'a> {
7427    data: TableDataRef<'a, ChainedSequenceRuleSet2FixedHeader>,
7428}
7429
7430#[allow(clippy::needless_lifetimes)]
7431impl<'a> ChainedSequenceRuleSet2<'a> {
7432    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
7433    basic_table_impls!(impl_the_methods);
7434
7435    /// Number of ChainedSequenceRule tables
7436    pub fn chained_seq_rule_count(&self) -> u16 {
7437        self.data.header().chained_seq_rule_count.get()
7438    }
7439
7440    /// Array of offsets to ChainedSequenceRule tables, from beginning
7441    /// of ChainedSequenceRuleSet2 table
7442    pub fn chained_seq_rule_offsets(&self) -> &'a [BigEndian<Offset24>] {
7443        let range = self.chained_seq_rule_offsets_byte_range();
7444        self.offset_data()
7445            .read_array(range)
7446            .ok()
7447            .unwrap_or_default()
7448    }
7449
7450    /// A dynamically resolving wrapper for [`chained_seq_rule_offsets`][Self::chained_seq_rule_offsets].
7451    pub fn chained_seq_rules(&self) -> ArrayOfOffsets<'a, ChainedSequenceRule2<'a>, Offset24> {
7452        let data = self.offset_data();
7453        let offsets = self.chained_seq_rule_offsets();
7454        ArrayOfOffsets::new(offsets, data, ())
7455    }
7456
7457    pub fn chained_seq_rule_count_byte_range(&self) -> Range<usize> {
7458        let start = 0;
7459        let end = start + u16::RAW_BYTE_LEN;
7460        start..end
7461    }
7462
7463    pub fn chained_seq_rule_offsets_byte_range(&self) -> Range<usize> {
7464        let chained_seq_rule_count = self.chained_seq_rule_count();
7465        let start = self.chained_seq_rule_count_byte_range().end;
7466        let end = start
7467            + (transforms::to_usize(chained_seq_rule_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
7468        start..end
7469    }
7470}
7471
7472const _: () = assert!(FontData::default_data_long_enough(
7473    ChainedSequenceRuleSet2::MIN_SIZE
7474));
7475
7476impl Default for ChainedSequenceRuleSet2<'_> {
7477    fn default() -> Self {
7478        Self {
7479            data: TableDataRef::new(FontData::default_table_data().as_bytes())
7480                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
7481        }
7482    }
7483}
7484
7485impl<'a> MinByteRange<'a> for ChainedSequenceRule2<'a> {
7486    fn min_byte_range(&self) -> Range<usize> {
7487        0..self.seq_lookup_records_byte_range().end
7488    }
7489    fn min_table_bytes(&self) -> &'a [u8] {
7490        let range = self.min_byte_range();
7491        self.offset_data().as_bytes().get(range).unwrap_or_default()
7492    }
7493}
7494
7495impl ReadArgs for ChainedSequenceRule2<'_> {
7496    type Args = ();
7497}
7498
7499impl<'a> FontRead<'a> for ChainedSequenceRule2<'a> {
7500    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7501        #[allow(clippy::absurd_extreme_comparisons)]
7502        if data.len() < Self::MIN_SIZE {
7503            return Err(ReadError::OutOfBounds);
7504        }
7505        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7506            return Err(ReadError::OutOfBounds);
7507        };
7508        Ok(Self { data })
7509    }
7510}
7511
7512/// The fields at the start of the table, which are read directly.
7513#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7514#[repr(C, packed)]
7515#[doc(hidden)]
7516pub struct ChainedSequenceRule2FixedHeader {
7517    pub backtrack_glyph_count: BigEndian<u16>,
7518}
7519
7520#[allow(clippy::absurd_extreme_comparisons)]
7521const _: () = assert!(
7522    std::mem::size_of::<ChainedSequenceRule2FixedHeader>() <= ChainedSequenceRule2::MIN_SIZE
7523);
7524
7525/// ChainedSequenceRule2 (ISO/IEC 14496-22:2026).
7526#[derive(Clone)]
7527pub struct ChainedSequenceRule2<'a> {
7528    data: TableDataRef<'a, ChainedSequenceRule2FixedHeader>,
7529}
7530
7531#[allow(clippy::needless_lifetimes)]
7532impl<'a> ChainedSequenceRule2<'a> {
7533    pub const MIN_SIZE: usize =
7534        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
7535    basic_table_impls!(impl_the_methods);
7536
7537    /// Number of glyphs in the backtrack sequence
7538    pub fn backtrack_glyph_count(&self) -> u16 {
7539        self.data.header().backtrack_glyph_count.get()
7540    }
7541
7542    /// Array of backtrack glyph IDs
7543    pub fn backtrack_sequence(&self) -> &'a [BigEndian<GlyphId24>] {
7544        let range = self.backtrack_sequence_byte_range();
7545        self.offset_data()
7546            .read_array(range)
7547            .ok()
7548            .unwrap_or_default()
7549    }
7550
7551    /// Number of glyphs in the input sequence
7552    pub fn input_glyph_count(&self) -> u16 {
7553        let range = self.input_glyph_count_byte_range();
7554        self.offset_data()
7555            .read_at(range.start)
7556            .ok()
7557            .unwrap_or_default()
7558    }
7559
7560    /// Array of input glyph IDs—start with second glyph
7561    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId24>] {
7562        let range = self.input_sequence_byte_range();
7563        self.offset_data()
7564            .read_array(range)
7565            .ok()
7566            .unwrap_or_default()
7567    }
7568
7569    /// Number of glyphs in the lookahead sequence
7570    pub fn lookahead_glyph_count(&self) -> u16 {
7571        let range = self.lookahead_glyph_count_byte_range();
7572        self.offset_data()
7573            .read_at(range.start)
7574            .ok()
7575            .unwrap_or_default()
7576    }
7577
7578    /// Array of lookahead glyph IDs
7579    pub fn lookahead_sequence(&self) -> &'a [BigEndian<GlyphId24>] {
7580        let range = self.lookahead_sequence_byte_range();
7581        self.offset_data()
7582            .read_array(range)
7583            .ok()
7584            .unwrap_or_default()
7585    }
7586
7587    /// Number of SequenceLookupRecords
7588    pub fn seq_lookup_count(&self) -> u16 {
7589        let range = self.seq_lookup_count_byte_range();
7590        self.offset_data()
7591            .read_at(range.start)
7592            .ok()
7593            .unwrap_or_default()
7594    }
7595
7596    /// Array of SequenceLookupRecords
7597    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
7598        let range = self.seq_lookup_records_byte_range();
7599        self.offset_data()
7600            .read_array(range)
7601            .ok()
7602            .unwrap_or_default()
7603    }
7604
7605    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
7606        let start = 0;
7607        let end = start + u16::RAW_BYTE_LEN;
7608        start..end
7609    }
7610
7611    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
7612        let backtrack_glyph_count = self.backtrack_glyph_count();
7613        let start = self.backtrack_glyph_count_byte_range().end;
7614        let end = start
7615            + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
7616        start..end
7617    }
7618
7619    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
7620        let start = self.backtrack_sequence_byte_range().end;
7621        let end = start + u16::RAW_BYTE_LEN;
7622        start..end
7623    }
7624
7625    pub fn input_sequence_byte_range(&self) -> Range<usize> {
7626        let input_glyph_count = self.input_glyph_count();
7627        let start = self.input_glyph_count_byte_range().end;
7628        let end = start
7629            + (transforms::subtract(input_glyph_count, 1_usize))
7630                .saturating_mul(GlyphId24::RAW_BYTE_LEN);
7631        start..end
7632    }
7633
7634    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
7635        let start = self.input_sequence_byte_range().end;
7636        let end = start + u16::RAW_BYTE_LEN;
7637        start..end
7638    }
7639
7640    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
7641        let lookahead_glyph_count = self.lookahead_glyph_count();
7642        let start = self.lookahead_glyph_count_byte_range().end;
7643        let end = start
7644            + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(GlyphId24::RAW_BYTE_LEN);
7645        start..end
7646    }
7647
7648    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
7649        let start = self.lookahead_sequence_byte_range().end;
7650        let end = start + u16::RAW_BYTE_LEN;
7651        start..end
7652    }
7653
7654    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
7655        let seq_lookup_count = self.seq_lookup_count();
7656        let start = self.seq_lookup_count_byte_range().end;
7657        let end = start
7658            + (transforms::to_usize(seq_lookup_count))
7659                .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN);
7660        start..end
7661    }
7662}
7663
7664const _: () = assert!(FontData::default_data_long_enough(
7665    ChainedSequenceRule2::MIN_SIZE
7666));
7667
7668impl Default for ChainedSequenceRule2<'_> {
7669    fn default() -> Self {
7670        Self {
7671            data: TableDataRef::new(FontData::default_table_data().as_bytes())
7672                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
7673        }
7674    }
7675}
7676
7677impl Format<u16> for ChainedSequenceContextFormat5<'_> {
7678    const FORMAT: u16 = 5;
7679}
7680
7681impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat5<'a> {
7682    fn min_byte_range(&self) -> Range<usize> {
7683        0..self.chained_class_seq_rule_set_offsets_byte_range().end
7684    }
7685    fn min_table_bytes(&self) -> &'a [u8] {
7686        let range = self.min_byte_range();
7687        self.offset_data().as_bytes().get(range).unwrap_or_default()
7688    }
7689}
7690
7691impl ReadArgs for ChainedSequenceContextFormat5<'_> {
7692    type Args = ();
7693}
7694
7695impl<'a> FontRead<'a> for ChainedSequenceContextFormat5<'a> {
7696    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7697        #[allow(clippy::absurd_extreme_comparisons)]
7698        if data.len() < Self::MIN_SIZE {
7699            return Err(ReadError::OutOfBounds);
7700        }
7701        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7702            return Err(ReadError::OutOfBounds);
7703        };
7704        Ok(Self { data })
7705    }
7706}
7707
7708/// The fields at the start of the table, which are read directly.
7709#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7710#[repr(C, packed)]
7711#[doc(hidden)]
7712pub struct ChainedSequenceContextFormat5FixedHeader {
7713    pub format: BigEndian<u16>,
7714    pub coverage_offset: BigEndian<Offset32>,
7715    pub backtrack_class_def_offset: BigEndian<Offset32>,
7716    pub input_class_def_offset: BigEndian<Offset32>,
7717    pub lookahead_class_def_offset: BigEndian<Offset32>,
7718    pub chained_class_seq_rule_set_count: BigEndian<u16>,
7719}
7720
7721#[allow(clippy::absurd_extreme_comparisons)]
7722const _: () = assert!(
7723    std::mem::size_of::<ChainedSequenceContextFormat5FixedHeader>()
7724        <= ChainedSequenceContextFormat5::MIN_SIZE
7725);
7726
7727/// ChainedSequenceContextFormat5 (ISO/IEC 14496-22:2026).
7728#[derive(Clone)]
7729pub struct ChainedSequenceContextFormat5<'a> {
7730    data: TableDataRef<'a, ChainedSequenceContextFormat5FixedHeader>,
7731}
7732
7733#[allow(clippy::needless_lifetimes)]
7734impl<'a> ChainedSequenceContextFormat5<'a> {
7735    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
7736        + Offset32::RAW_BYTE_LEN
7737        + Offset32::RAW_BYTE_LEN
7738        + Offset32::RAW_BYTE_LEN
7739        + Offset32::RAW_BYTE_LEN
7740        + u16::RAW_BYTE_LEN);
7741    basic_table_impls!(impl_the_methods);
7742
7743    /// Format identifier: format = 5
7744    pub fn format(&self) -> u16 {
7745        self.data.header().format.get()
7746    }
7747
7748    /// Offset to Coverage table, from beginning of
7749    /// ChainedSequenceContextFormat5 table
7750    pub fn coverage_offset(&self) -> Offset32 {
7751        self.data.header().coverage_offset.get()
7752    }
7753
7754    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
7755    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
7756        let data = self.offset_data();
7757        self.coverage_offset().resolve(data)
7758    }
7759
7760    /// Offset to ClassDef table containing backtrack sequence context,
7761    /// from beginning of ChainedSequenceContextFormat5 table
7762    pub fn backtrack_class_def_offset(&self) -> Offset32 {
7763        self.data.header().backtrack_class_def_offset.get()
7764    }
7765
7766    /// Attempt to resolve [`backtrack_class_def_offset`][Self::backtrack_class_def_offset].
7767    pub fn backtrack_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
7768        let data = self.offset_data();
7769        self.backtrack_class_def_offset().resolve(data)
7770    }
7771
7772    /// Offset to ClassDef table containing input sequence context,
7773    /// from beginning of ChainedSequenceContextFormat5 table
7774    pub fn input_class_def_offset(&self) -> Offset32 {
7775        self.data.header().input_class_def_offset.get()
7776    }
7777
7778    /// Attempt to resolve [`input_class_def_offset`][Self::input_class_def_offset].
7779    pub fn input_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
7780        let data = self.offset_data();
7781        self.input_class_def_offset().resolve(data)
7782    }
7783
7784    /// Offset to ClassDef table containing lookahead sequence context,
7785    /// from beginning of ChainedSequenceContextFormat5 table
7786    pub fn lookahead_class_def_offset(&self) -> Offset32 {
7787        self.data.header().lookahead_class_def_offset.get()
7788    }
7789
7790    /// Attempt to resolve [`lookahead_class_def_offset`][Self::lookahead_class_def_offset].
7791    pub fn lookahead_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
7792        let data = self.offset_data();
7793        self.lookahead_class_def_offset().resolve(data)
7794    }
7795
7796    /// Number of ChainedClassSequenceRuleSet tables
7797    pub fn chained_class_seq_rule_set_count(&self) -> u16 {
7798        self.data.header().chained_class_seq_rule_set_count.get()
7799    }
7800
7801    /// Array of offsets to ChainedClassSequenceRuleSet tables, from
7802    /// beginning of ChainedSequenceContextFormat5 table (may be NULL)
7803    pub fn chained_class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
7804        let range = self.chained_class_seq_rule_set_offsets_byte_range();
7805        self.offset_data()
7806            .read_array(range)
7807            .ok()
7808            .unwrap_or_default()
7809    }
7810
7811    /// A dynamically resolving wrapper for [`chained_class_seq_rule_set_offsets`][Self::chained_class_seq_rule_set_offsets].
7812    pub fn chained_class_seq_rule_sets(
7813        &self,
7814    ) -> ArrayOfNullableOffsets<'a, ChainedClassSequenceRuleSet2<'a>, Offset24> {
7815        let data = self.offset_data();
7816        let offsets = self.chained_class_seq_rule_set_offsets();
7817        ArrayOfNullableOffsets::new(offsets, data, ())
7818    }
7819
7820    pub fn format_byte_range(&self) -> Range<usize> {
7821        let start = 0;
7822        let end = start + u16::RAW_BYTE_LEN;
7823        start..end
7824    }
7825
7826    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
7827        let start = self.format_byte_range().end;
7828        let end = start + Offset32::RAW_BYTE_LEN;
7829        start..end
7830    }
7831
7832    pub fn backtrack_class_def_offset_byte_range(&self) -> Range<usize> {
7833        let start = self.coverage_offset_byte_range().end;
7834        let end = start + Offset32::RAW_BYTE_LEN;
7835        start..end
7836    }
7837
7838    pub fn input_class_def_offset_byte_range(&self) -> Range<usize> {
7839        let start = self.backtrack_class_def_offset_byte_range().end;
7840        let end = start + Offset32::RAW_BYTE_LEN;
7841        start..end
7842    }
7843
7844    pub fn lookahead_class_def_offset_byte_range(&self) -> Range<usize> {
7845        let start = self.input_class_def_offset_byte_range().end;
7846        let end = start + Offset32::RAW_BYTE_LEN;
7847        start..end
7848    }
7849
7850    pub fn chained_class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
7851        let start = self.lookahead_class_def_offset_byte_range().end;
7852        let end = start + u16::RAW_BYTE_LEN;
7853        start..end
7854    }
7855
7856    pub fn chained_class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
7857        let chained_class_seq_rule_set_count = self.chained_class_seq_rule_set_count();
7858        let start = self.chained_class_seq_rule_set_count_byte_range().end;
7859        let end = start
7860            + (transforms::to_usize(chained_class_seq_rule_set_count))
7861                .saturating_mul(Offset24::RAW_BYTE_LEN);
7862        start..end
7863    }
7864}
7865
7866impl<'a> MinByteRange<'a> for ChainedClassSequenceRuleSet2<'a> {
7867    fn min_byte_range(&self) -> Range<usize> {
7868        0..self.chained_class_seq_rule_offsets_byte_range().end
7869    }
7870    fn min_table_bytes(&self) -> &'a [u8] {
7871        let range = self.min_byte_range();
7872        self.offset_data().as_bytes().get(range).unwrap_or_default()
7873    }
7874}
7875
7876impl ReadArgs for ChainedClassSequenceRuleSet2<'_> {
7877    type Args = ();
7878}
7879
7880impl<'a> FontRead<'a> for ChainedClassSequenceRuleSet2<'a> {
7881    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7882        #[allow(clippy::absurd_extreme_comparisons)]
7883        if data.len() < Self::MIN_SIZE {
7884            return Err(ReadError::OutOfBounds);
7885        }
7886        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7887            return Err(ReadError::OutOfBounds);
7888        };
7889        Ok(Self { data })
7890    }
7891}
7892
7893/// The fields at the start of the table, which are read directly.
7894#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
7895#[repr(C, packed)]
7896#[doc(hidden)]
7897pub struct ChainedClassSequenceRuleSet2FixedHeader {
7898    pub chained_class_seq_rule_count: BigEndian<u16>,
7899}
7900
7901#[allow(clippy::absurd_extreme_comparisons)]
7902const _: () = assert!(
7903    std::mem::size_of::<ChainedClassSequenceRuleSet2FixedHeader>()
7904        <= ChainedClassSequenceRuleSet2::MIN_SIZE
7905);
7906
7907/// ChainedClassSequenceRuleSet2 (ISO/IEC 14496-22:2026).
7908#[derive(Clone)]
7909pub struct ChainedClassSequenceRuleSet2<'a> {
7910    data: TableDataRef<'a, ChainedClassSequenceRuleSet2FixedHeader>,
7911}
7912
7913#[allow(clippy::needless_lifetimes)]
7914impl<'a> ChainedClassSequenceRuleSet2<'a> {
7915    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
7916    basic_table_impls!(impl_the_methods);
7917
7918    /// Number of ChainedClassSequenceRule tables
7919    pub fn chained_class_seq_rule_count(&self) -> u16 {
7920        self.data.header().chained_class_seq_rule_count.get()
7921    }
7922
7923    /// Array of offsets to ChainedClassSequenceRule tables, from
7924    /// beginning of ChainedClassSequenceRuleSet2
7925    pub fn chained_class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset24>] {
7926        let range = self.chained_class_seq_rule_offsets_byte_range();
7927        self.offset_data()
7928            .read_array(range)
7929            .ok()
7930            .unwrap_or_default()
7931    }
7932
7933    /// A dynamically resolving wrapper for [`chained_class_seq_rule_offsets`][Self::chained_class_seq_rule_offsets].
7934    pub fn chained_class_seq_rules(
7935        &self,
7936    ) -> ArrayOfOffsets<'a, ChainedClassSequenceRule<'a>, Offset24> {
7937        let data = self.offset_data();
7938        let offsets = self.chained_class_seq_rule_offsets();
7939        ArrayOfOffsets::new(offsets, data, ())
7940    }
7941
7942    pub fn chained_class_seq_rule_count_byte_range(&self) -> Range<usize> {
7943        let start = 0;
7944        let end = start + u16::RAW_BYTE_LEN;
7945        start..end
7946    }
7947
7948    pub fn chained_class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
7949        let chained_class_seq_rule_count = self.chained_class_seq_rule_count();
7950        let start = self.chained_class_seq_rule_count_byte_range().end;
7951        let end = start
7952            + (transforms::to_usize(chained_class_seq_rule_count))
7953                .saturating_mul(Offset24::RAW_BYTE_LEN);
7954        start..end
7955    }
7956}
7957
7958const _: () = assert!(FontData::default_data_long_enough(
7959    ChainedClassSequenceRuleSet2::MIN_SIZE
7960));
7961
7962impl Default for ChainedClassSequenceRuleSet2<'_> {
7963    fn default() -> Self {
7964        Self {
7965            data: TableDataRef::new(FontData::default_table_data().as_bytes())
7966                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
7967        }
7968    }
7969}
7970
7971impl<'a, T> MinByteRange<'a> for LookupList2<'a, T> {
7972    fn min_byte_range(&self) -> Range<usize> {
7973        0..self.lookup_offsets_byte_range().end
7974    }
7975    fn min_table_bytes(&self) -> &'a [u8] {
7976        let range = self.min_byte_range();
7977        self.offset_data().as_bytes().get(range).unwrap_or_default()
7978    }
7979}
7980
7981impl<T> ReadArgs for LookupList2<'_, T> {
7982    type Args = ();
7983}
7984
7985impl<'a, T> FontRead<'a> for LookupList2<'a, T> {
7986    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
7987        #[allow(clippy::absurd_extreme_comparisons)]
7988        if data.len() < Self::MIN_SIZE {
7989            return Err(ReadError::OutOfBounds);
7990        }
7991        let Some(data) = TableDataRef::new(data.as_bytes()) else {
7992            return Err(ReadError::OutOfBounds);
7993        };
7994        Ok(Self {
7995            data,
7996            offset_type: std::marker::PhantomData,
7997        })
7998    }
7999}
8000
8001impl<'a, T> LookupList2<'a, T> {
8002    #[allow(dead_code)]
8003    /// Replace the specific generic type on this implementation with `()`
8004    pub(crate) fn of_unit_type(&self) -> LookupList2<'a, ()> {
8005        LookupList2 {
8006            data: self.data,
8007            offset_type: std::marker::PhantomData,
8008        }
8009    }
8010}
8011
8012/// The fields at the start of the table, which are read directly.
8013#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
8014#[repr(C, packed)]
8015#[doc(hidden)]
8016pub struct LookupList2FixedHeader {
8017    pub lookup_count: BigEndian<u16>,
8018}
8019
8020#[allow(clippy::absurd_extreme_comparisons)]
8021const _: () = assert!(std::mem::size_of::<LookupList2FixedHeader>() <= LookupList2::<()>::MIN_SIZE);
8022
8023/// LookupList2 (ISO/IEC 14496-22:2026).
8024#[derive(Clone)]
8025pub struct LookupList2<'a, T = ()> {
8026    data: TableDataRef<'a, LookupList2FixedHeader>,
8027    offset_type: std::marker::PhantomData<*const T>,
8028}
8029
8030#[allow(clippy::needless_lifetimes)]
8031impl<'a, T> LookupList2<'a, T> {
8032    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
8033    basic_table_impls!(impl_the_methods);
8034
8035    /// Number of lookups in this table
8036    pub fn lookup_count(&self) -> u16 {
8037        self.data.header().lookup_count.get()
8038    }
8039
8040    /// Array of offsets to Lookup tables, from beginning of LookupList2
8041    /// — zero based (first lookup is Lookup index = 0)
8042    pub fn lookup_offsets(&self) -> &'a [BigEndian<Offset32>] {
8043        let range = self.lookup_offsets_byte_range();
8044        self.offset_data()
8045            .read_array(range)
8046            .ok()
8047            .unwrap_or_default()
8048    }
8049
8050    /// A dynamically resolving wrapper for [`lookup_offsets`][Self::lookup_offsets].
8051    pub fn lookups(&self) -> ArrayOfOffsets<'a, T, Offset32>
8052    where
8053        T: FontRead<'a, Args = ()>,
8054    {
8055        let data = self.offset_data();
8056        let offsets = self.lookup_offsets();
8057        ArrayOfOffsets::new(offsets, data, ())
8058    }
8059
8060    pub fn lookup_count_byte_range(&self) -> Range<usize> {
8061        let start = 0;
8062        let end = start + u16::RAW_BYTE_LEN;
8063        start..end
8064    }
8065
8066    pub fn lookup_offsets_byte_range(&self) -> Range<usize> {
8067        let lookup_count = self.lookup_count();
8068        let start = self.lookup_count_byte_range().end;
8069        let end =
8070            start + (transforms::to_usize(lookup_count)).saturating_mul(Offset32::RAW_BYTE_LEN);
8071        start..end
8072    }
8073}
8074
8075const _: () = assert!(FontData::default_data_long_enough(
8076    LookupList2::<()>::MIN_SIZE
8077));
8078
8079impl<T> Default for LookupList2<'_, T> {
8080    fn default() -> Self {
8081        Self {
8082            data: TableDataRef::new(FontData::default_table_data().as_bytes())
8083                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
8084            offset_type: std::marker::PhantomData,
8085        }
8086    }
8087}