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.data.as_bytes().get(range).unwrap_or_default()
15    }
16}
17
18impl<'a> FontRead<'a> for ScriptList<'a> {
19    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
20        #[allow(clippy::absurd_extreme_comparisons)]
21        if data.len() < Self::MIN_SIZE {
22            return Err(ReadError::OutOfBounds);
23        }
24        Ok(Self { data })
25    }
26}
27
28/// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
29#[derive(Clone)]
30pub struct ScriptList<'a> {
31    data: FontData<'a>,
32}
33
34#[allow(clippy::needless_lifetimes)]
35impl<'a> ScriptList<'a> {
36    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
37    basic_table_impls!(impl_the_methods);
38
39    /// Number of ScriptRecords
40    pub fn script_count(&self) -> u16 {
41        let range = self.script_count_byte_range();
42        self.data.read_at(range.start).ok().unwrap()
43    }
44
45    /// Array of ScriptRecords, listed alphabetically by script tag
46    pub fn script_records(&self) -> &'a [ScriptRecord] {
47        let range = self.script_records_byte_range();
48        self.data.read_array(range).ok().unwrap_or_default()
49    }
50
51    pub fn script_count_byte_range(&self) -> Range<usize> {
52        let start = 0;
53        start..start + u16::RAW_BYTE_LEN
54    }
55
56    pub fn script_records_byte_range(&self) -> Range<usize> {
57        let script_count = self.script_count();
58        let start = self.script_count_byte_range().end;
59        start
60            ..start
61                + (transforms::to_usize(script_count)).saturating_mul(ScriptRecord::RAW_BYTE_LEN)
62    }
63}
64
65const _: () = assert!(FontData::default_data_long_enough(ScriptList::MIN_SIZE));
66
67impl Default for ScriptList<'_> {
68    fn default() -> Self {
69        Self {
70            data: FontData::default_table_data(),
71        }
72    }
73}
74
75#[cfg(feature = "experimental_traverse")]
76impl<'a> SomeTable<'a> for ScriptList<'a> {
77    fn type_name(&self) -> &str {
78        "ScriptList"
79    }
80    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
81        match idx {
82            0usize => Some(Field::new("script_count", self.script_count())),
83            1usize => Some(Field::new(
84                "script_records",
85                traversal::FieldType::array_of_records(
86                    stringify!(ScriptRecord),
87                    self.script_records(),
88                    self.offset_data(),
89                ),
90            )),
91            _ => None,
92        }
93    }
94}
95
96#[cfg(feature = "experimental_traverse")]
97#[allow(clippy::needless_lifetimes)]
98impl<'a> std::fmt::Debug for ScriptList<'a> {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        (self as &dyn SomeTable<'a>).fmt(f)
101    }
102}
103
104/// [Script Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
105#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
106#[repr(C)]
107#[repr(packed)]
108pub struct ScriptRecord {
109    /// 4-byte script tag identifier
110    pub script_tag: BigEndian<Tag>,
111    /// Offset to Script table, from beginning of ScriptList
112    pub script_offset: BigEndian<Offset16>,
113}
114
115impl ScriptRecord {
116    /// 4-byte script tag identifier
117    pub fn script_tag(&self) -> Tag {
118        self.script_tag.get()
119    }
120
121    /// Offset to Script table, from beginning of ScriptList
122    pub fn script_offset(&self) -> Offset16 {
123        self.script_offset.get()
124    }
125
126    /// Offset to Script table, from beginning of ScriptList
127    ///
128    /// The `data` argument should be retrieved from the parent table
129    /// By calling its `offset_data` method.
130    pub fn script<'a>(&self, data: FontData<'a>) -> Result<Script<'a>, ReadError> {
131        self.script_offset().resolve(data)
132    }
133}
134
135impl FixedSize for ScriptRecord {
136    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
137}
138
139#[cfg(feature = "experimental_traverse")]
140impl<'a> SomeRecord<'a> for ScriptRecord {
141    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
142        RecordResolver {
143            name: "ScriptRecord",
144            get_field: Box::new(move |idx, _data| match idx {
145                0usize => Some(Field::new("script_tag", self.script_tag())),
146                1usize => Some(Field::new(
147                    "script_offset",
148                    FieldType::offset(self.script_offset(), self.script(_data)),
149                )),
150                _ => None,
151            }),
152            data,
153        }
154    }
155}
156
157impl<'a> MinByteRange<'a> for Script<'a> {
158    fn min_byte_range(&self) -> Range<usize> {
159        0..self.lang_sys_records_byte_range().end
160    }
161    fn min_table_bytes(&self) -> &'a [u8] {
162        let range = self.min_byte_range();
163        self.data.as_bytes().get(range).unwrap_or_default()
164    }
165}
166
167impl<'a> FontRead<'a> for Script<'a> {
168    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
169        #[allow(clippy::absurd_extreme_comparisons)]
170        if data.len() < Self::MIN_SIZE {
171            return Err(ReadError::OutOfBounds);
172        }
173        Ok(Self { data })
174    }
175}
176
177/// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record)
178#[derive(Clone)]
179pub struct Script<'a> {
180    data: FontData<'a>,
181}
182
183#[allow(clippy::needless_lifetimes)]
184impl<'a> Script<'a> {
185    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
186    basic_table_impls!(impl_the_methods);
187
188    /// Offset to default LangSys table, from beginning of Script table
189    /// — may be NULL
190    pub fn default_lang_sys_offset(&self) -> Nullable<Offset16> {
191        let range = self.default_lang_sys_offset_byte_range();
192        self.data.read_at(range.start).ok().unwrap()
193    }
194
195    /// Attempt to resolve [`default_lang_sys_offset`][Self::default_lang_sys_offset].
196    pub fn default_lang_sys(&self) -> Option<Result<LangSys<'a>, ReadError>> {
197        let data = self.data;
198        self.default_lang_sys_offset().resolve(data)
199    }
200
201    /// Number of LangSysRecords for this script — excluding the
202    /// default LangSys
203    pub fn lang_sys_count(&self) -> u16 {
204        let range = self.lang_sys_count_byte_range();
205        self.data.read_at(range.start).ok().unwrap()
206    }
207
208    /// Array of LangSysRecords, listed alphabetically by LangSys tag
209    pub fn lang_sys_records(&self) -> &'a [LangSysRecord] {
210        let range = self.lang_sys_records_byte_range();
211        self.data.read_array(range).ok().unwrap_or_default()
212    }
213
214    pub fn default_lang_sys_offset_byte_range(&self) -> Range<usize> {
215        let start = 0;
216        start..start + Offset16::RAW_BYTE_LEN
217    }
218
219    pub fn lang_sys_count_byte_range(&self) -> Range<usize> {
220        let start = self.default_lang_sys_offset_byte_range().end;
221        start..start + u16::RAW_BYTE_LEN
222    }
223
224    pub fn lang_sys_records_byte_range(&self) -> Range<usize> {
225        let lang_sys_count = self.lang_sys_count();
226        let start = self.lang_sys_count_byte_range().end;
227        start
228            ..start
229                + (transforms::to_usize(lang_sys_count)).saturating_mul(LangSysRecord::RAW_BYTE_LEN)
230    }
231}
232
233const _: () = assert!(FontData::default_data_long_enough(Script::MIN_SIZE));
234
235impl Default for Script<'_> {
236    fn default() -> Self {
237        Self {
238            data: FontData::default_table_data(),
239        }
240    }
241}
242
243#[cfg(feature = "experimental_traverse")]
244impl<'a> SomeTable<'a> for Script<'a> {
245    fn type_name(&self) -> &str {
246        "Script"
247    }
248    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
249        match idx {
250            0usize => Some(Field::new(
251                "default_lang_sys_offset",
252                FieldType::offset(self.default_lang_sys_offset(), self.default_lang_sys()),
253            )),
254            1usize => Some(Field::new("lang_sys_count", self.lang_sys_count())),
255            2usize => Some(Field::new(
256                "lang_sys_records",
257                traversal::FieldType::array_of_records(
258                    stringify!(LangSysRecord),
259                    self.lang_sys_records(),
260                    self.offset_data(),
261                ),
262            )),
263            _ => None,
264        }
265    }
266}
267
268#[cfg(feature = "experimental_traverse")]
269#[allow(clippy::needless_lifetimes)]
270impl<'a> std::fmt::Debug for Script<'a> {
271    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272        (self as &dyn SomeTable<'a>).fmt(f)
273    }
274}
275
276#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
277#[repr(C)]
278#[repr(packed)]
279pub struct LangSysRecord {
280    /// 4-byte LangSysTag identifier
281    pub lang_sys_tag: BigEndian<Tag>,
282    /// Offset to LangSys table, from beginning of Script table
283    pub lang_sys_offset: BigEndian<Offset16>,
284}
285
286impl LangSysRecord {
287    /// 4-byte LangSysTag identifier
288    pub fn lang_sys_tag(&self) -> Tag {
289        self.lang_sys_tag.get()
290    }
291
292    /// Offset to LangSys table, from beginning of Script table
293    pub fn lang_sys_offset(&self) -> Offset16 {
294        self.lang_sys_offset.get()
295    }
296
297    /// Offset to LangSys table, from beginning of Script table
298    ///
299    /// The `data` argument should be retrieved from the parent table
300    /// By calling its `offset_data` method.
301    pub fn lang_sys<'a>(&self, data: FontData<'a>) -> Result<LangSys<'a>, ReadError> {
302        self.lang_sys_offset().resolve(data)
303    }
304}
305
306impl FixedSize for LangSysRecord {
307    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
308}
309
310#[cfg(feature = "experimental_traverse")]
311impl<'a> SomeRecord<'a> for LangSysRecord {
312    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
313        RecordResolver {
314            name: "LangSysRecord",
315            get_field: Box::new(move |idx, _data| match idx {
316                0usize => Some(Field::new("lang_sys_tag", self.lang_sys_tag())),
317                1usize => Some(Field::new(
318                    "lang_sys_offset",
319                    FieldType::offset(self.lang_sys_offset(), self.lang_sys(_data)),
320                )),
321                _ => None,
322            }),
323            data,
324        }
325    }
326}
327
328impl<'a> MinByteRange<'a> for LangSys<'a> {
329    fn min_byte_range(&self) -> Range<usize> {
330        0..self.feature_indices_byte_range().end
331    }
332    fn min_table_bytes(&self) -> &'a [u8] {
333        let range = self.min_byte_range();
334        self.data.as_bytes().get(range).unwrap_or_default()
335    }
336}
337
338impl<'a> FontRead<'a> for LangSys<'a> {
339    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
340        #[allow(clippy::absurd_extreme_comparisons)]
341        if data.len() < Self::MIN_SIZE {
342            return Err(ReadError::OutOfBounds);
343        }
344        Ok(Self { data })
345    }
346}
347
348/// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table)
349#[derive(Clone)]
350pub struct LangSys<'a> {
351    data: FontData<'a>,
352}
353
354#[allow(clippy::needless_lifetimes)]
355impl<'a> LangSys<'a> {
356    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
357    basic_table_impls!(impl_the_methods);
358
359    /// Index of a feature required for this language system; if no
360    /// required features = 0xFFFF
361    pub fn required_feature_index(&self) -> u16 {
362        let range = self.required_feature_index_byte_range();
363        self.data.read_at(range.start).ok().unwrap()
364    }
365
366    /// Number of feature index values for this language system —
367    /// excludes the required feature
368    pub fn feature_index_count(&self) -> u16 {
369        let range = self.feature_index_count_byte_range();
370        self.data.read_at(range.start).ok().unwrap()
371    }
372
373    /// Array of indices into the FeatureList, in arbitrary order
374    pub fn feature_indices(&self) -> &'a [BigEndian<u16>] {
375        let range = self.feature_indices_byte_range();
376        self.data.read_array(range).ok().unwrap_or_default()
377    }
378
379    pub fn lookup_order_offset_byte_range(&self) -> Range<usize> {
380        let start = 0;
381        start..start + u16::RAW_BYTE_LEN
382    }
383
384    pub fn required_feature_index_byte_range(&self) -> Range<usize> {
385        let start = self.lookup_order_offset_byte_range().end;
386        start..start + u16::RAW_BYTE_LEN
387    }
388
389    pub fn feature_index_count_byte_range(&self) -> Range<usize> {
390        let start = self.required_feature_index_byte_range().end;
391        start..start + u16::RAW_BYTE_LEN
392    }
393
394    pub fn feature_indices_byte_range(&self) -> Range<usize> {
395        let feature_index_count = self.feature_index_count();
396        let start = self.feature_index_count_byte_range().end;
397        start..start + (transforms::to_usize(feature_index_count)).saturating_mul(u16::RAW_BYTE_LEN)
398    }
399}
400
401const _: () = assert!(FontData::default_data_long_enough(LangSys::MIN_SIZE));
402
403impl Default for LangSys<'_> {
404    fn default() -> Self {
405        Self {
406            data: FontData::default_table_data(),
407        }
408    }
409}
410
411#[cfg(feature = "experimental_traverse")]
412impl<'a> SomeTable<'a> for LangSys<'a> {
413    fn type_name(&self) -> &str {
414        "LangSys"
415    }
416    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
417        match idx {
418            0usize => Some(Field::new(
419                "required_feature_index",
420                self.required_feature_index(),
421            )),
422            1usize => Some(Field::new(
423                "feature_index_count",
424                self.feature_index_count(),
425            )),
426            2usize => Some(Field::new("feature_indices", self.feature_indices())),
427            _ => None,
428        }
429    }
430}
431
432#[cfg(feature = "experimental_traverse")]
433#[allow(clippy::needless_lifetimes)]
434impl<'a> std::fmt::Debug for LangSys<'a> {
435    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
436        (self as &dyn SomeTable<'a>).fmt(f)
437    }
438}
439
440impl<'a> MinByteRange<'a> for FeatureList<'a> {
441    fn min_byte_range(&self) -> Range<usize> {
442        0..self.feature_records_byte_range().end
443    }
444    fn min_table_bytes(&self) -> &'a [u8] {
445        let range = self.min_byte_range();
446        self.data.as_bytes().get(range).unwrap_or_default()
447    }
448}
449
450impl<'a> FontRead<'a> for FeatureList<'a> {
451    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
452        #[allow(clippy::absurd_extreme_comparisons)]
453        if data.len() < Self::MIN_SIZE {
454            return Err(ReadError::OutOfBounds);
455        }
456        Ok(Self { data })
457    }
458}
459
460/// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table)
461#[derive(Clone)]
462pub struct FeatureList<'a> {
463    data: FontData<'a>,
464}
465
466#[allow(clippy::needless_lifetimes)]
467impl<'a> FeatureList<'a> {
468    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
469    basic_table_impls!(impl_the_methods);
470
471    /// Number of FeatureRecords in this table
472    pub fn feature_count(&self) -> u16 {
473        let range = self.feature_count_byte_range();
474        self.data.read_at(range.start).ok().unwrap()
475    }
476
477    /// Array of FeatureRecords — zero-based (first feature has
478    /// FeatureIndex = 0), listed alphabetically by feature tag
479    pub fn feature_records(&self) -> &'a [FeatureRecord] {
480        let range = self.feature_records_byte_range();
481        self.data.read_array(range).ok().unwrap_or_default()
482    }
483
484    pub fn feature_count_byte_range(&self) -> Range<usize> {
485        let start = 0;
486        start..start + u16::RAW_BYTE_LEN
487    }
488
489    pub fn feature_records_byte_range(&self) -> Range<usize> {
490        let feature_count = self.feature_count();
491        let start = self.feature_count_byte_range().end;
492        start
493            ..start
494                + (transforms::to_usize(feature_count)).saturating_mul(FeatureRecord::RAW_BYTE_LEN)
495    }
496}
497
498const _: () = assert!(FontData::default_data_long_enough(FeatureList::MIN_SIZE));
499
500impl Default for FeatureList<'_> {
501    fn default() -> Self {
502        Self {
503            data: FontData::default_table_data(),
504        }
505    }
506}
507
508#[cfg(feature = "experimental_traverse")]
509impl<'a> SomeTable<'a> for FeatureList<'a> {
510    fn type_name(&self) -> &str {
511        "FeatureList"
512    }
513    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
514        match idx {
515            0usize => Some(Field::new("feature_count", self.feature_count())),
516            1usize => Some(Field::new(
517                "feature_records",
518                traversal::FieldType::array_of_records(
519                    stringify!(FeatureRecord),
520                    self.feature_records(),
521                    self.offset_data(),
522                ),
523            )),
524            _ => None,
525        }
526    }
527}
528
529#[cfg(feature = "experimental_traverse")]
530#[allow(clippy::needless_lifetimes)]
531impl<'a> std::fmt::Debug for FeatureList<'a> {
532    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
533        (self as &dyn SomeTable<'a>).fmt(f)
534    }
535}
536
537/// Part of [FeatureList]
538#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
539#[repr(C)]
540#[repr(packed)]
541pub struct FeatureRecord {
542    /// 4-byte feature identification tag
543    pub feature_tag: BigEndian<Tag>,
544    /// Offset to Feature table, from beginning of FeatureList
545    pub feature_offset: BigEndian<Offset16>,
546}
547
548impl FeatureRecord {
549    /// 4-byte feature identification tag
550    pub fn feature_tag(&self) -> Tag {
551        self.feature_tag.get()
552    }
553
554    /// Offset to Feature table, from beginning of FeatureList
555    pub fn feature_offset(&self) -> Offset16 {
556        self.feature_offset.get()
557    }
558
559    /// Offset to Feature table, from beginning of FeatureList
560    ///
561    /// The `data` argument should be retrieved from the parent table
562    /// By calling its `offset_data` method.
563    pub fn feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
564        let args = self.feature_tag();
565        self.feature_offset().resolve_with_args(data, &args)
566    }
567}
568
569impl FixedSize for FeatureRecord {
570    const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
571}
572
573#[cfg(feature = "experimental_traverse")]
574impl<'a> SomeRecord<'a> for FeatureRecord {
575    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
576        RecordResolver {
577            name: "FeatureRecord",
578            get_field: Box::new(move |idx, _data| match idx {
579                0usize => Some(Field::new("feature_tag", self.feature_tag())),
580                1usize => Some(Field::new(
581                    "feature_offset",
582                    FieldType::offset(self.feature_offset(), self.feature(_data)),
583                )),
584                _ => None,
585            }),
586            data,
587        }
588    }
589}
590
591impl<'a> MinByteRange<'a> for Feature<'a> {
592    fn min_byte_range(&self) -> Range<usize> {
593        0..self.lookup_list_indices_byte_range().end
594    }
595    fn min_table_bytes(&self) -> &'a [u8] {
596        let range = self.min_byte_range();
597        self.data.as_bytes().get(range).unwrap_or_default()
598    }
599}
600
601impl ReadArgs for Feature<'_> {
602    type Args = Tag;
603}
604
605impl<'a> FontReadWithArgs<'a> for Feature<'a> {
606    fn read_with_args(data: FontData<'a>, args: &Tag) -> Result<Self, ReadError> {
607        let feature_tag = *args;
608
609        #[allow(clippy::absurd_extreme_comparisons)]
610        if data.len() < Self::MIN_SIZE {
611            return Err(ReadError::OutOfBounds);
612        }
613        Ok(Self { data, feature_tag })
614    }
615}
616
617impl<'a> Feature<'a> {
618    /// A constructor that requires additional arguments.
619    ///
620    /// This type requires some external state in order to be
621    /// parsed.
622    pub fn read(data: FontData<'a>, feature_tag: Tag) -> Result<Self, ReadError> {
623        let args = feature_tag;
624        Self::read_with_args(data, &args)
625    }
626}
627
628/// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table)
629#[derive(Clone)]
630pub struct Feature<'a> {
631    data: FontData<'a>,
632    feature_tag: Tag,
633}
634
635#[allow(clippy::needless_lifetimes)]
636impl<'a> Feature<'a> {
637    pub const MIN_SIZE: usize = (Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
638    basic_table_impls!(impl_the_methods);
639
640    /// Offset from start of Feature table to FeatureParams table, if defined for the feature and present, else NULL
641    pub fn feature_params_offset(&self) -> Nullable<Offset16> {
642        let range = self.feature_params_offset_byte_range();
643        self.data.read_at(range.start).ok().unwrap()
644    }
645
646    /// Attempt to resolve [`feature_params_offset`][Self::feature_params_offset].
647    pub fn feature_params(&self) -> Option<Result<FeatureParams<'a>, ReadError>> {
648        let data = self.data;
649        let args = self.feature_tag();
650        self.feature_params_offset().resolve_with_args(data, &args)
651    }
652
653    /// Number of LookupList indices for this feature
654    pub fn lookup_index_count(&self) -> u16 {
655        let range = self.lookup_index_count_byte_range();
656        self.data.read_at(range.start).ok().unwrap()
657    }
658
659    /// Array of indices into the LookupList — zero-based (first
660    /// lookup is LookupListIndex = 0)
661    pub fn lookup_list_indices(&self) -> &'a [BigEndian<u16>] {
662        let range = self.lookup_list_indices_byte_range();
663        self.data.read_array(range).ok().unwrap_or_default()
664    }
665
666    pub(crate) fn feature_tag(&self) -> Tag {
667        self.feature_tag
668    }
669
670    pub fn feature_params_offset_byte_range(&self) -> Range<usize> {
671        let start = 0;
672        start..start + Offset16::RAW_BYTE_LEN
673    }
674
675    pub fn lookup_index_count_byte_range(&self) -> Range<usize> {
676        let start = self.feature_params_offset_byte_range().end;
677        start..start + u16::RAW_BYTE_LEN
678    }
679
680    pub fn lookup_list_indices_byte_range(&self) -> Range<usize> {
681        let lookup_index_count = self.lookup_index_count();
682        let start = self.lookup_index_count_byte_range().end;
683        start..start + (transforms::to_usize(lookup_index_count)).saturating_mul(u16::RAW_BYTE_LEN)
684    }
685}
686
687const _: () = assert!(FontData::default_data_long_enough(Feature::MIN_SIZE));
688
689impl Default for Feature<'_> {
690    fn default() -> Self {
691        Self {
692            data: FontData::default_table_data(),
693            feature_tag: Default::default(),
694        }
695    }
696}
697
698#[cfg(feature = "experimental_traverse")]
699impl<'a> SomeTable<'a> for Feature<'a> {
700    fn type_name(&self) -> &str {
701        "Feature"
702    }
703    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
704        match idx {
705            0usize => Some(Field::new(
706                "feature_params_offset",
707                FieldType::offset(self.feature_params_offset(), self.feature_params()),
708            )),
709            1usize => Some(Field::new("lookup_index_count", self.lookup_index_count())),
710            2usize => Some(Field::new(
711                "lookup_list_indices",
712                self.lookup_list_indices(),
713            )),
714            _ => None,
715        }
716    }
717}
718
719#[cfg(feature = "experimental_traverse")]
720#[allow(clippy::needless_lifetimes)]
721impl<'a> std::fmt::Debug for Feature<'a> {
722    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
723        (self as &dyn SomeTable<'a>).fmt(f)
724    }
725}
726
727impl<'a, T> MinByteRange<'a> for LookupList<'a, T> {
728    fn min_byte_range(&self) -> Range<usize> {
729        0..self.lookup_offsets_byte_range().end
730    }
731    fn min_table_bytes(&self) -> &'a [u8] {
732        let range = self.min_byte_range();
733        self.data.as_bytes().get(range).unwrap_or_default()
734    }
735}
736
737impl<'a, T> FontRead<'a> for LookupList<'a, T> {
738    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
739        #[allow(clippy::absurd_extreme_comparisons)]
740        if data.len() < Self::MIN_SIZE {
741            return Err(ReadError::OutOfBounds);
742        }
743        Ok(Self {
744            data,
745            offset_type: std::marker::PhantomData,
746        })
747    }
748}
749
750impl<'a, T> LookupList<'a, T> {
751    #[allow(dead_code)]
752    /// Replace the specific generic type on this implementation with `()`
753    pub(crate) fn of_unit_type(&self) -> LookupList<'a, ()> {
754        LookupList {
755            data: self.data,
756            offset_type: std::marker::PhantomData,
757        }
758    }
759}
760
761/// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table)
762#[derive(Clone)]
763pub struct LookupList<'a, T = ()> {
764    data: FontData<'a>,
765    offset_type: std::marker::PhantomData<*const T>,
766}
767
768#[allow(clippy::needless_lifetimes)]
769impl<'a, T> LookupList<'a, T> {
770    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
771    basic_table_impls!(impl_the_methods);
772
773    /// Number of lookups in this table
774    pub fn lookup_count(&self) -> u16 {
775        let range = self.lookup_count_byte_range();
776        self.data.read_at(range.start).ok().unwrap()
777    }
778
779    /// Array of offsets to Lookup tables, from beginning of LookupList
780    /// — zero based (first lookup is Lookup index = 0)
781    pub fn lookup_offsets(&self) -> &'a [BigEndian<Offset16>] {
782        let range = self.lookup_offsets_byte_range();
783        self.data.read_array(range).ok().unwrap_or_default()
784    }
785
786    /// A dynamically resolving wrapper for [`lookup_offsets`][Self::lookup_offsets].
787    pub fn lookups(&self) -> ArrayOfOffsets<'a, T, Offset16>
788    where
789        T: FontRead<'a>,
790    {
791        let data = self.data;
792        let offsets = self.lookup_offsets();
793        ArrayOfOffsets::new(offsets, data, ())
794    }
795
796    pub fn lookup_count_byte_range(&self) -> Range<usize> {
797        let start = 0;
798        start..start + u16::RAW_BYTE_LEN
799    }
800
801    pub fn lookup_offsets_byte_range(&self) -> Range<usize> {
802        let lookup_count = self.lookup_count();
803        let start = self.lookup_count_byte_range().end;
804        start..start + (transforms::to_usize(lookup_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
805    }
806}
807
808const _: () = assert!(FontData::default_data_long_enough(
809    LookupList::<()>::MIN_SIZE
810));
811
812impl<T> Default for LookupList<'_, T> {
813    fn default() -> Self {
814        Self {
815            data: FontData::default_table_data(),
816            offset_type: std::marker::PhantomData,
817        }
818    }
819}
820
821#[cfg(feature = "experimental_traverse")]
822impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for LookupList<'a, T> {
823    fn type_name(&self) -> &str {
824        "LookupList"
825    }
826    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
827        match idx {
828            0usize => Some(Field::new("lookup_count", self.lookup_count())),
829            1usize => Some(Field::new(
830                "lookup_offsets",
831                FieldType::from(self.lookups()),
832            )),
833            _ => None,
834        }
835    }
836}
837
838#[cfg(feature = "experimental_traverse")]
839#[allow(clippy::needless_lifetimes)]
840impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for LookupList<'a, T> {
841    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
842        (self as &dyn SomeTable<'a>).fmt(f)
843    }
844}
845
846impl Discriminant for Lookup<'_, ()> {
847    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
848        data.read_at(0)
849    }
850}
851
852impl<'a, T> MinByteRange<'a> for Lookup<'a, T> {
853    fn min_byte_range(&self) -> Range<usize> {
854        0..self.subtable_offsets_byte_range().end
855    }
856    fn min_table_bytes(&self) -> &'a [u8] {
857        let range = self.min_byte_range();
858        self.data.as_bytes().get(range).unwrap_or_default()
859    }
860}
861
862impl<'a, T> FontRead<'a> for Lookup<'a, T> {
863    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
864        #[allow(clippy::absurd_extreme_comparisons)]
865        if data.len() < Self::MIN_SIZE {
866            return Err(ReadError::OutOfBounds);
867        }
868        Ok(Self {
869            data,
870            offset_type: std::marker::PhantomData,
871        })
872    }
873}
874
875impl<'a, T> Lookup<'a, T> {
876    #[allow(dead_code)]
877    /// Replace the specific generic type on this implementation with `()`
878    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
879        Lookup {
880            data: self.data,
881            offset_type: std::marker::PhantomData,
882        }
883    }
884}
885
886/// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table)
887#[derive(Clone)]
888pub struct Lookup<'a, T = ()> {
889    data: FontData<'a>,
890    offset_type: std::marker::PhantomData<*const T>,
891}
892
893#[allow(clippy::needless_lifetimes)]
894impl<'a, T> Lookup<'a, T> {
895    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + LookupFlag::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
896    basic_table_impls!(impl_the_methods);
897
898    /// Different enumerations for GSUB and GPOS
899    pub fn lookup_type(&self) -> u16 {
900        let range = self.lookup_type_byte_range();
901        self.data.read_at(range.start).ok().unwrap()
902    }
903
904    /// Lookup qualifiers
905    pub fn lookup_flag(&self) -> LookupFlag {
906        let range = self.lookup_flag_byte_range();
907        self.data.read_at(range.start).ok().unwrap()
908    }
909
910    /// Number of subtables for this lookup
911    pub fn sub_table_count(&self) -> u16 {
912        let range = self.sub_table_count_byte_range();
913        self.data.read_at(range.start).ok().unwrap()
914    }
915
916    /// Array of offsets to lookup subtables, from beginning of Lookup
917    /// table
918    pub fn subtable_offsets(&self) -> &'a [BigEndian<Offset16>] {
919        let range = self.subtable_offsets_byte_range();
920        self.data.read_array(range).ok().unwrap_or_default()
921    }
922
923    /// A dynamically resolving wrapper for [`subtable_offsets`][Self::subtable_offsets].
924    pub fn subtables(&self) -> ArrayOfOffsets<'a, T, Offset16>
925    where
926        T: FontRead<'a>,
927    {
928        let data = self.data;
929        let offsets = self.subtable_offsets();
930        ArrayOfOffsets::new(offsets, data, ())
931    }
932
933    /// Index (base 0) into GDEF mark glyph sets structure. This field
934    /// is only present if the USE_MARK_FILTERING_SET lookup flag is
935    /// set.
936    pub fn mark_filtering_set(&self) -> Option<u16> {
937        let range = self.mark_filtering_set_byte_range();
938        (!range.is_empty())
939            .then(|| self.data.read_at(range.start).ok())
940            .flatten()
941    }
942
943    pub fn lookup_type_byte_range(&self) -> Range<usize> {
944        let start = 0;
945        start..start + u16::RAW_BYTE_LEN
946    }
947
948    pub fn lookup_flag_byte_range(&self) -> Range<usize> {
949        let start = self.lookup_type_byte_range().end;
950        start..start + LookupFlag::RAW_BYTE_LEN
951    }
952
953    pub fn sub_table_count_byte_range(&self) -> Range<usize> {
954        let start = self.lookup_flag_byte_range().end;
955        start..start + u16::RAW_BYTE_LEN
956    }
957
958    pub fn subtable_offsets_byte_range(&self) -> Range<usize> {
959        let sub_table_count = self.sub_table_count();
960        let start = self.sub_table_count_byte_range().end;
961        start
962            ..start + (transforms::to_usize(sub_table_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
963    }
964
965    pub fn mark_filtering_set_byte_range(&self) -> Range<usize> {
966        let start = self.subtable_offsets_byte_range().end;
967        start
968            ..(self
969                .lookup_flag()
970                .contains(LookupFlag::USE_MARK_FILTERING_SET))
971            .then(|| start + u16::RAW_BYTE_LEN)
972            .unwrap_or(start)
973    }
974}
975
976const _: () = assert!(FontData::default_data_long_enough(Lookup::<()>::MIN_SIZE));
977
978impl<T> Default for Lookup<'_, T> {
979    fn default() -> Self {
980        Self {
981            data: FontData::default_table_data(),
982            offset_type: std::marker::PhantomData,
983        }
984    }
985}
986
987#[cfg(feature = "experimental_traverse")]
988impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for Lookup<'a, T> {
989    fn type_name(&self) -> &str {
990        "Lookup"
991    }
992    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
993        match idx {
994            0usize => Some(Field::new("lookup_type", self.lookup_type())),
995            1usize => Some(Field::new("lookup_flag", self.traverse_lookup_flag())),
996            2usize => Some(Field::new("sub_table_count", self.sub_table_count())),
997            3usize => Some(Field::new(
998                "subtable_offsets",
999                FieldType::from(self.subtables()),
1000            )),
1001            4usize
1002                if self
1003                    .lookup_flag()
1004                    .contains(LookupFlag::USE_MARK_FILTERING_SET) =>
1005            {
1006                Some(Field::new(
1007                    "mark_filtering_set",
1008                    self.mark_filtering_set().unwrap(),
1009                ))
1010            }
1011            _ => None,
1012        }
1013    }
1014}
1015
1016#[cfg(feature = "experimental_traverse")]
1017#[allow(clippy::needless_lifetimes)]
1018impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for Lookup<'a, T> {
1019    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1020        (self as &dyn SomeTable<'a>).fmt(f)
1021    }
1022}
1023
1024impl Format<u16> for CoverageFormat1<'_> {
1025    const FORMAT: u16 = 1;
1026}
1027
1028impl<'a> MinByteRange<'a> for CoverageFormat1<'a> {
1029    fn min_byte_range(&self) -> Range<usize> {
1030        0..self.glyph_array_byte_range().end
1031    }
1032    fn min_table_bytes(&self) -> &'a [u8] {
1033        let range = self.min_byte_range();
1034        self.data.as_bytes().get(range).unwrap_or_default()
1035    }
1036}
1037
1038impl<'a> FontRead<'a> for CoverageFormat1<'a> {
1039    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1040        #[allow(clippy::absurd_extreme_comparisons)]
1041        if data.len() < Self::MIN_SIZE {
1042            return Err(ReadError::OutOfBounds);
1043        }
1044        Ok(Self { data })
1045    }
1046}
1047
1048/// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1)
1049#[derive(Clone)]
1050pub struct CoverageFormat1<'a> {
1051    data: FontData<'a>,
1052}
1053
1054#[allow(clippy::needless_lifetimes)]
1055impl<'a> CoverageFormat1<'a> {
1056    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1057    basic_table_impls!(impl_the_methods);
1058
1059    /// Format identifier — format = 1
1060    pub fn coverage_format(&self) -> u16 {
1061        let range = self.coverage_format_byte_range();
1062        self.data.read_at(range.start).ok().unwrap()
1063    }
1064
1065    /// Number of glyphs in the glyph array
1066    pub fn glyph_count(&self) -> u16 {
1067        let range = self.glyph_count_byte_range();
1068        self.data.read_at(range.start).ok().unwrap()
1069    }
1070
1071    /// Array of glyph IDs — in numerical order
1072    pub fn glyph_array(&self) -> &'a [BigEndian<GlyphId16>] {
1073        let range = self.glyph_array_byte_range();
1074        self.data.read_array(range).ok().unwrap_or_default()
1075    }
1076
1077    pub fn coverage_format_byte_range(&self) -> Range<usize> {
1078        let start = 0;
1079        start..start + u16::RAW_BYTE_LEN
1080    }
1081
1082    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1083        let start = self.coverage_format_byte_range().end;
1084        start..start + u16::RAW_BYTE_LEN
1085    }
1086
1087    pub fn glyph_array_byte_range(&self) -> Range<usize> {
1088        let glyph_count = self.glyph_count();
1089        let start = self.glyph_count_byte_range().end;
1090        start..start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN)
1091    }
1092}
1093
1094const _: () = assert!(FontData::default_data_long_enough(
1095    CoverageFormat1::MIN_SIZE
1096));
1097
1098impl Default for CoverageFormat1<'_> {
1099    fn default() -> Self {
1100        Self {
1101            data: FontData::default_format_1_u16_table_data(),
1102        }
1103    }
1104}
1105
1106#[cfg(feature = "experimental_traverse")]
1107impl<'a> SomeTable<'a> for CoverageFormat1<'a> {
1108    fn type_name(&self) -> &str {
1109        "CoverageFormat1"
1110    }
1111    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1112        match idx {
1113            0usize => Some(Field::new("coverage_format", self.coverage_format())),
1114            1usize => Some(Field::new("glyph_count", self.glyph_count())),
1115            2usize => Some(Field::new("glyph_array", self.glyph_array())),
1116            _ => None,
1117        }
1118    }
1119}
1120
1121#[cfg(feature = "experimental_traverse")]
1122#[allow(clippy::needless_lifetimes)]
1123impl<'a> std::fmt::Debug for CoverageFormat1<'a> {
1124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1125        (self as &dyn SomeTable<'a>).fmt(f)
1126    }
1127}
1128
1129impl Format<u16> for CoverageFormat2<'_> {
1130    const FORMAT: u16 = 2;
1131}
1132
1133impl<'a> MinByteRange<'a> for CoverageFormat2<'a> {
1134    fn min_byte_range(&self) -> Range<usize> {
1135        0..self.range_records_byte_range().end
1136    }
1137    fn min_table_bytes(&self) -> &'a [u8] {
1138        let range = self.min_byte_range();
1139        self.data.as_bytes().get(range).unwrap_or_default()
1140    }
1141}
1142
1143impl<'a> FontRead<'a> for CoverageFormat2<'a> {
1144    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1145        #[allow(clippy::absurd_extreme_comparisons)]
1146        if data.len() < Self::MIN_SIZE {
1147            return Err(ReadError::OutOfBounds);
1148        }
1149        Ok(Self { data })
1150    }
1151}
1152
1153/// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2)
1154#[derive(Clone)]
1155pub struct CoverageFormat2<'a> {
1156    data: FontData<'a>,
1157}
1158
1159#[allow(clippy::needless_lifetimes)]
1160impl<'a> CoverageFormat2<'a> {
1161    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1162    basic_table_impls!(impl_the_methods);
1163
1164    /// Format identifier — format = 2
1165    pub fn coverage_format(&self) -> u16 {
1166        let range = self.coverage_format_byte_range();
1167        self.data.read_at(range.start).ok().unwrap()
1168    }
1169
1170    /// Number of RangeRecords
1171    pub fn range_count(&self) -> u16 {
1172        let range = self.range_count_byte_range();
1173        self.data.read_at(range.start).ok().unwrap()
1174    }
1175
1176    /// Array of glyph ranges — ordered by startGlyphID.
1177    pub fn range_records(&self) -> &'a [RangeRecord] {
1178        let range = self.range_records_byte_range();
1179        self.data.read_array(range).ok().unwrap_or_default()
1180    }
1181
1182    pub fn coverage_format_byte_range(&self) -> Range<usize> {
1183        let start = 0;
1184        start..start + u16::RAW_BYTE_LEN
1185    }
1186
1187    pub fn range_count_byte_range(&self) -> Range<usize> {
1188        let start = self.coverage_format_byte_range().end;
1189        start..start + u16::RAW_BYTE_LEN
1190    }
1191
1192    pub fn range_records_byte_range(&self) -> Range<usize> {
1193        let range_count = self.range_count();
1194        let start = self.range_count_byte_range().end;
1195        start..start + (transforms::to_usize(range_count)).saturating_mul(RangeRecord::RAW_BYTE_LEN)
1196    }
1197}
1198
1199#[cfg(feature = "experimental_traverse")]
1200impl<'a> SomeTable<'a> for CoverageFormat2<'a> {
1201    fn type_name(&self) -> &str {
1202        "CoverageFormat2"
1203    }
1204    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1205        match idx {
1206            0usize => Some(Field::new("coverage_format", self.coverage_format())),
1207            1usize => Some(Field::new("range_count", self.range_count())),
1208            2usize => Some(Field::new(
1209                "range_records",
1210                traversal::FieldType::array_of_records(
1211                    stringify!(RangeRecord),
1212                    self.range_records(),
1213                    self.offset_data(),
1214                ),
1215            )),
1216            _ => None,
1217        }
1218    }
1219}
1220
1221#[cfg(feature = "experimental_traverse")]
1222#[allow(clippy::needless_lifetimes)]
1223impl<'a> std::fmt::Debug for CoverageFormat2<'a> {
1224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1225        (self as &dyn SomeTable<'a>).fmt(f)
1226    }
1227}
1228
1229/// Used in [CoverageFormat2]
1230#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1231#[repr(C)]
1232#[repr(packed)]
1233pub struct RangeRecord {
1234    /// First glyph ID in the range
1235    pub start_glyph_id: BigEndian<GlyphId16>,
1236    /// Last glyph ID in the range
1237    pub end_glyph_id: BigEndian<GlyphId16>,
1238    /// Coverage Index of first glyph ID in range
1239    pub start_coverage_index: BigEndian<u16>,
1240}
1241
1242impl RangeRecord {
1243    /// First glyph ID in the range
1244    pub fn start_glyph_id(&self) -> GlyphId16 {
1245        self.start_glyph_id.get()
1246    }
1247
1248    /// Last glyph ID in the range
1249    pub fn end_glyph_id(&self) -> GlyphId16 {
1250        self.end_glyph_id.get()
1251    }
1252
1253    /// Coverage Index of first glyph ID in range
1254    pub fn start_coverage_index(&self) -> u16 {
1255        self.start_coverage_index.get()
1256    }
1257}
1258
1259impl FixedSize for RangeRecord {
1260    const RAW_BYTE_LEN: usize =
1261        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1262}
1263
1264#[cfg(feature = "experimental_traverse")]
1265impl<'a> SomeRecord<'a> for RangeRecord {
1266    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1267        RecordResolver {
1268            name: "RangeRecord",
1269            get_field: Box::new(move |idx, _data| match idx {
1270                0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
1271                1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())),
1272                2usize => Some(Field::new(
1273                    "start_coverage_index",
1274                    self.start_coverage_index(),
1275                )),
1276                _ => None,
1277            }),
1278            data,
1279        }
1280    }
1281}
1282
1283/// [Coverage Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-table)
1284#[derive(Clone)]
1285pub enum CoverageTable<'a> {
1286    Format1(CoverageFormat1<'a>),
1287    Format2(CoverageFormat2<'a>),
1288}
1289
1290impl Default for CoverageTable<'_> {
1291    fn default() -> Self {
1292        Self::Format1(Default::default())
1293    }
1294}
1295
1296impl<'a> CoverageTable<'a> {
1297    ///Return the `FontData` used to resolve offsets for this table.
1298    pub fn offset_data(&self) -> FontData<'a> {
1299        match self {
1300            Self::Format1(item) => item.offset_data(),
1301            Self::Format2(item) => item.offset_data(),
1302        }
1303    }
1304
1305    /// Format identifier — format = 1
1306    pub fn coverage_format(&self) -> u16 {
1307        match self {
1308            Self::Format1(item) => item.coverage_format(),
1309            Self::Format2(item) => item.coverage_format(),
1310        }
1311    }
1312}
1313
1314impl<'a> FontRead<'a> for CoverageTable<'a> {
1315    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1316        let format: u16 = data.read_at(0usize)?;
1317        match format {
1318            CoverageFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1319            CoverageFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1320            other => Err(ReadError::InvalidFormat(other.into())),
1321        }
1322    }
1323}
1324
1325impl<'a> MinByteRange<'a> for CoverageTable<'a> {
1326    fn min_byte_range(&self) -> Range<usize> {
1327        match self {
1328            Self::Format1(item) => item.min_byte_range(),
1329            Self::Format2(item) => item.min_byte_range(),
1330        }
1331    }
1332    fn min_table_bytes(&self) -> &'a [u8] {
1333        match self {
1334            Self::Format1(item) => item.min_table_bytes(),
1335            Self::Format2(item) => item.min_table_bytes(),
1336        }
1337    }
1338}
1339
1340#[cfg(feature = "experimental_traverse")]
1341impl<'a> CoverageTable<'a> {
1342    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
1343        match self {
1344            Self::Format1(table) => table,
1345            Self::Format2(table) => table,
1346        }
1347    }
1348}
1349
1350#[cfg(feature = "experimental_traverse")]
1351impl std::fmt::Debug for CoverageTable<'_> {
1352    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1353        self.dyn_inner().fmt(f)
1354    }
1355}
1356
1357#[cfg(feature = "experimental_traverse")]
1358impl<'a> SomeTable<'a> for CoverageTable<'a> {
1359    fn type_name(&self) -> &str {
1360        self.dyn_inner().type_name()
1361    }
1362    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1363        self.dyn_inner().get_field(idx)
1364    }
1365}
1366
1367impl Format<u16> for ClassDefFormat1<'_> {
1368    const FORMAT: u16 = 1;
1369}
1370
1371impl<'a> MinByteRange<'a> for ClassDefFormat1<'a> {
1372    fn min_byte_range(&self) -> Range<usize> {
1373        0..self.class_value_array_byte_range().end
1374    }
1375    fn min_table_bytes(&self) -> &'a [u8] {
1376        let range = self.min_byte_range();
1377        self.data.as_bytes().get(range).unwrap_or_default()
1378    }
1379}
1380
1381impl<'a> FontRead<'a> for ClassDefFormat1<'a> {
1382    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1383        #[allow(clippy::absurd_extreme_comparisons)]
1384        if data.len() < Self::MIN_SIZE {
1385            return Err(ReadError::OutOfBounds);
1386        }
1387        Ok(Self { data })
1388    }
1389}
1390
1391/// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1)
1392#[derive(Clone)]
1393pub struct ClassDefFormat1<'a> {
1394    data: FontData<'a>,
1395}
1396
1397#[allow(clippy::needless_lifetimes)]
1398impl<'a> ClassDefFormat1<'a> {
1399    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1400    basic_table_impls!(impl_the_methods);
1401
1402    /// Format identifier — format = 1
1403    pub fn class_format(&self) -> u16 {
1404        let range = self.class_format_byte_range();
1405        self.data.read_at(range.start).ok().unwrap()
1406    }
1407
1408    /// First glyph ID of the classValueArray
1409    pub fn start_glyph_id(&self) -> GlyphId16 {
1410        let range = self.start_glyph_id_byte_range();
1411        self.data.read_at(range.start).ok().unwrap()
1412    }
1413
1414    /// Size of the classValueArray
1415    pub fn glyph_count(&self) -> u16 {
1416        let range = self.glyph_count_byte_range();
1417        self.data.read_at(range.start).ok().unwrap()
1418    }
1419
1420    /// Array of Class Values — one per glyph ID
1421    pub fn class_value_array(&self) -> &'a [BigEndian<u16>] {
1422        let range = self.class_value_array_byte_range();
1423        self.data.read_array(range).ok().unwrap_or_default()
1424    }
1425
1426    pub fn class_format_byte_range(&self) -> Range<usize> {
1427        let start = 0;
1428        start..start + u16::RAW_BYTE_LEN
1429    }
1430
1431    pub fn start_glyph_id_byte_range(&self) -> Range<usize> {
1432        let start = self.class_format_byte_range().end;
1433        start..start + GlyphId16::RAW_BYTE_LEN
1434    }
1435
1436    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1437        let start = self.start_glyph_id_byte_range().end;
1438        start..start + u16::RAW_BYTE_LEN
1439    }
1440
1441    pub fn class_value_array_byte_range(&self) -> Range<usize> {
1442        let glyph_count = self.glyph_count();
1443        let start = self.glyph_count_byte_range().end;
1444        start..start + (transforms::to_usize(glyph_count)).saturating_mul(u16::RAW_BYTE_LEN)
1445    }
1446}
1447
1448const _: () = assert!(FontData::default_data_long_enough(
1449    ClassDefFormat1::MIN_SIZE
1450));
1451
1452impl Default for ClassDefFormat1<'_> {
1453    fn default() -> Self {
1454        Self {
1455            data: FontData::default_format_1_u16_table_data(),
1456        }
1457    }
1458}
1459
1460#[cfg(feature = "experimental_traverse")]
1461impl<'a> SomeTable<'a> for ClassDefFormat1<'a> {
1462    fn type_name(&self) -> &str {
1463        "ClassDefFormat1"
1464    }
1465    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1466        match idx {
1467            0usize => Some(Field::new("class_format", self.class_format())),
1468            1usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
1469            2usize => Some(Field::new("glyph_count", self.glyph_count())),
1470            3usize => Some(Field::new("class_value_array", self.class_value_array())),
1471            _ => None,
1472        }
1473    }
1474}
1475
1476#[cfg(feature = "experimental_traverse")]
1477#[allow(clippy::needless_lifetimes)]
1478impl<'a> std::fmt::Debug for ClassDefFormat1<'a> {
1479    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1480        (self as &dyn SomeTable<'a>).fmt(f)
1481    }
1482}
1483
1484impl Format<u16> for ClassDefFormat2<'_> {
1485    const FORMAT: u16 = 2;
1486}
1487
1488impl<'a> MinByteRange<'a> for ClassDefFormat2<'a> {
1489    fn min_byte_range(&self) -> Range<usize> {
1490        0..self.class_range_records_byte_range().end
1491    }
1492    fn min_table_bytes(&self) -> &'a [u8] {
1493        let range = self.min_byte_range();
1494        self.data.as_bytes().get(range).unwrap_or_default()
1495    }
1496}
1497
1498impl<'a> FontRead<'a> for ClassDefFormat2<'a> {
1499    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1500        #[allow(clippy::absurd_extreme_comparisons)]
1501        if data.len() < Self::MIN_SIZE {
1502            return Err(ReadError::OutOfBounds);
1503        }
1504        Ok(Self { data })
1505    }
1506}
1507
1508/// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2)
1509#[derive(Clone)]
1510pub struct ClassDefFormat2<'a> {
1511    data: FontData<'a>,
1512}
1513
1514#[allow(clippy::needless_lifetimes)]
1515impl<'a> ClassDefFormat2<'a> {
1516    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1517    basic_table_impls!(impl_the_methods);
1518
1519    /// Format identifier — format = 2
1520    pub fn class_format(&self) -> u16 {
1521        let range = self.class_format_byte_range();
1522        self.data.read_at(range.start).ok().unwrap()
1523    }
1524
1525    /// Number of ClassRangeRecords
1526    pub fn class_range_count(&self) -> u16 {
1527        let range = self.class_range_count_byte_range();
1528        self.data.read_at(range.start).ok().unwrap()
1529    }
1530
1531    /// Array of ClassRangeRecords — ordered by startGlyphID
1532    pub fn class_range_records(&self) -> &'a [ClassRangeRecord] {
1533        let range = self.class_range_records_byte_range();
1534        self.data.read_array(range).ok().unwrap_or_default()
1535    }
1536
1537    pub fn class_format_byte_range(&self) -> Range<usize> {
1538        let start = 0;
1539        start..start + u16::RAW_BYTE_LEN
1540    }
1541
1542    pub fn class_range_count_byte_range(&self) -> Range<usize> {
1543        let start = self.class_format_byte_range().end;
1544        start..start + u16::RAW_BYTE_LEN
1545    }
1546
1547    pub fn class_range_records_byte_range(&self) -> Range<usize> {
1548        let class_range_count = self.class_range_count();
1549        let start = self.class_range_count_byte_range().end;
1550        start
1551            ..start
1552                + (transforms::to_usize(class_range_count))
1553                    .saturating_mul(ClassRangeRecord::RAW_BYTE_LEN)
1554    }
1555}
1556
1557#[cfg(feature = "experimental_traverse")]
1558impl<'a> SomeTable<'a> for ClassDefFormat2<'a> {
1559    fn type_name(&self) -> &str {
1560        "ClassDefFormat2"
1561    }
1562    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1563        match idx {
1564            0usize => Some(Field::new("class_format", self.class_format())),
1565            1usize => Some(Field::new("class_range_count", self.class_range_count())),
1566            2usize => Some(Field::new(
1567                "class_range_records",
1568                traversal::FieldType::array_of_records(
1569                    stringify!(ClassRangeRecord),
1570                    self.class_range_records(),
1571                    self.offset_data(),
1572                ),
1573            )),
1574            _ => None,
1575        }
1576    }
1577}
1578
1579#[cfg(feature = "experimental_traverse")]
1580#[allow(clippy::needless_lifetimes)]
1581impl<'a> std::fmt::Debug for ClassDefFormat2<'a> {
1582    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1583        (self as &dyn SomeTable<'a>).fmt(f)
1584    }
1585}
1586
1587/// Used in [ClassDefFormat2]
1588#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1589#[repr(C)]
1590#[repr(packed)]
1591pub struct ClassRangeRecord {
1592    /// First glyph ID in the range
1593    pub start_glyph_id: BigEndian<GlyphId16>,
1594    /// Last glyph ID in the range
1595    pub end_glyph_id: BigEndian<GlyphId16>,
1596    /// Applied to all glyphs in the range
1597    pub class: BigEndian<u16>,
1598}
1599
1600impl ClassRangeRecord {
1601    /// First glyph ID in the range
1602    pub fn start_glyph_id(&self) -> GlyphId16 {
1603        self.start_glyph_id.get()
1604    }
1605
1606    /// Last glyph ID in the range
1607    pub fn end_glyph_id(&self) -> GlyphId16 {
1608        self.end_glyph_id.get()
1609    }
1610
1611    /// Applied to all glyphs in the range
1612    pub fn class(&self) -> u16 {
1613        self.class.get()
1614    }
1615}
1616
1617impl FixedSize for ClassRangeRecord {
1618    const RAW_BYTE_LEN: usize =
1619        GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1620}
1621
1622#[cfg(feature = "experimental_traverse")]
1623impl<'a> SomeRecord<'a> for ClassRangeRecord {
1624    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1625        RecordResolver {
1626            name: "ClassRangeRecord",
1627            get_field: Box::new(move |idx, _data| match idx {
1628                0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
1629                1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())),
1630                2usize => Some(Field::new("class", self.class())),
1631                _ => None,
1632            }),
1633            data,
1634        }
1635    }
1636}
1637
1638/// A [Class Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table)
1639#[derive(Clone)]
1640pub enum ClassDef<'a> {
1641    Format1(ClassDefFormat1<'a>),
1642    Format2(ClassDefFormat2<'a>),
1643}
1644
1645impl Default for ClassDef<'_> {
1646    fn default() -> Self {
1647        Self::Format1(Default::default())
1648    }
1649}
1650
1651impl<'a> ClassDef<'a> {
1652    ///Return the `FontData` used to resolve offsets for this table.
1653    pub fn offset_data(&self) -> FontData<'a> {
1654        match self {
1655            Self::Format1(item) => item.offset_data(),
1656            Self::Format2(item) => item.offset_data(),
1657        }
1658    }
1659
1660    /// Format identifier — format = 1
1661    pub fn class_format(&self) -> u16 {
1662        match self {
1663            Self::Format1(item) => item.class_format(),
1664            Self::Format2(item) => item.class_format(),
1665        }
1666    }
1667}
1668
1669impl<'a> FontRead<'a> for ClassDef<'a> {
1670    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1671        let format: u16 = data.read_at(0usize)?;
1672        match format {
1673            ClassDefFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1674            ClassDefFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1675            other => Err(ReadError::InvalidFormat(other.into())),
1676        }
1677    }
1678}
1679
1680impl<'a> MinByteRange<'a> for ClassDef<'a> {
1681    fn min_byte_range(&self) -> Range<usize> {
1682        match self {
1683            Self::Format1(item) => item.min_byte_range(),
1684            Self::Format2(item) => item.min_byte_range(),
1685        }
1686    }
1687    fn min_table_bytes(&self) -> &'a [u8] {
1688        match self {
1689            Self::Format1(item) => item.min_table_bytes(),
1690            Self::Format2(item) => item.min_table_bytes(),
1691        }
1692    }
1693}
1694
1695#[cfg(feature = "experimental_traverse")]
1696impl<'a> ClassDef<'a> {
1697    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
1698        match self {
1699            Self::Format1(table) => table,
1700            Self::Format2(table) => table,
1701        }
1702    }
1703}
1704
1705#[cfg(feature = "experimental_traverse")]
1706impl std::fmt::Debug for ClassDef<'_> {
1707    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1708        self.dyn_inner().fmt(f)
1709    }
1710}
1711
1712#[cfg(feature = "experimental_traverse")]
1713impl<'a> SomeTable<'a> for ClassDef<'a> {
1714    fn type_name(&self) -> &str {
1715        self.dyn_inner().type_name()
1716    }
1717    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1718        self.dyn_inner().get_field(idx)
1719    }
1720}
1721
1722/// [Sequence Lookup Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-lookup-record)
1723#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
1724#[repr(C)]
1725#[repr(packed)]
1726pub struct SequenceLookupRecord {
1727    /// Index (zero-based) into the input glyph sequence
1728    pub sequence_index: BigEndian<u16>,
1729    /// Index (zero-based) into the LookupList
1730    pub lookup_list_index: BigEndian<u16>,
1731}
1732
1733impl SequenceLookupRecord {
1734    /// Index (zero-based) into the input glyph sequence
1735    pub fn sequence_index(&self) -> u16 {
1736        self.sequence_index.get()
1737    }
1738
1739    /// Index (zero-based) into the LookupList
1740    pub fn lookup_list_index(&self) -> u16 {
1741        self.lookup_list_index.get()
1742    }
1743}
1744
1745impl FixedSize for SequenceLookupRecord {
1746    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
1747}
1748
1749#[cfg(feature = "experimental_traverse")]
1750impl<'a> SomeRecord<'a> for SequenceLookupRecord {
1751    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
1752        RecordResolver {
1753            name: "SequenceLookupRecord",
1754            get_field: Box::new(move |idx, _data| match idx {
1755                0usize => Some(Field::new("sequence_index", self.sequence_index())),
1756                1usize => Some(Field::new("lookup_list_index", self.lookup_list_index())),
1757                _ => None,
1758            }),
1759            data,
1760        }
1761    }
1762}
1763
1764impl Format<u16> for SequenceContextFormat1<'_> {
1765    const FORMAT: u16 = 1;
1766}
1767
1768impl<'a> MinByteRange<'a> for SequenceContextFormat1<'a> {
1769    fn min_byte_range(&self) -> Range<usize> {
1770        0..self.seq_rule_set_offsets_byte_range().end
1771    }
1772    fn min_table_bytes(&self) -> &'a [u8] {
1773        let range = self.min_byte_range();
1774        self.data.as_bytes().get(range).unwrap_or_default()
1775    }
1776}
1777
1778impl<'a> FontRead<'a> for SequenceContextFormat1<'a> {
1779    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1780        #[allow(clippy::absurd_extreme_comparisons)]
1781        if data.len() < Self::MIN_SIZE {
1782            return Err(ReadError::OutOfBounds);
1783        }
1784        Ok(Self { data })
1785    }
1786}
1787
1788/// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts)
1789#[derive(Clone)]
1790pub struct SequenceContextFormat1<'a> {
1791    data: FontData<'a>,
1792}
1793
1794#[allow(clippy::needless_lifetimes)]
1795impl<'a> SequenceContextFormat1<'a> {
1796    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1797    basic_table_impls!(impl_the_methods);
1798
1799    /// Format identifier: format = 1
1800    pub fn format(&self) -> u16 {
1801        let range = self.format_byte_range();
1802        self.data.read_at(range.start).ok().unwrap()
1803    }
1804
1805    /// Offset to Coverage table, from beginning of
1806    /// SequenceContextFormat1 table
1807    pub fn coverage_offset(&self) -> Offset16 {
1808        let range = self.coverage_offset_byte_range();
1809        self.data.read_at(range.start).ok().unwrap()
1810    }
1811
1812    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1813    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1814        let data = self.data;
1815        self.coverage_offset().resolve(data)
1816    }
1817
1818    /// Number of SequenceRuleSet tables
1819    pub fn seq_rule_set_count(&self) -> u16 {
1820        let range = self.seq_rule_set_count_byte_range();
1821        self.data.read_at(range.start).ok().unwrap()
1822    }
1823
1824    /// Array of offsets to SequenceRuleSet tables, from beginning of
1825    /// SequenceContextFormat1 table (offsets may be NULL)
1826    pub fn seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
1827        let range = self.seq_rule_set_offsets_byte_range();
1828        self.data.read_array(range).ok().unwrap_or_default()
1829    }
1830
1831    /// A dynamically resolving wrapper for [`seq_rule_set_offsets`][Self::seq_rule_set_offsets].
1832    pub fn seq_rule_sets(&self) -> ArrayOfNullableOffsets<'a, SequenceRuleSet<'a>, Offset16> {
1833        let data = self.data;
1834        let offsets = self.seq_rule_set_offsets();
1835        ArrayOfNullableOffsets::new(offsets, data, ())
1836    }
1837
1838    pub fn format_byte_range(&self) -> Range<usize> {
1839        let start = 0;
1840        start..start + u16::RAW_BYTE_LEN
1841    }
1842
1843    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1844        let start = self.format_byte_range().end;
1845        start..start + Offset16::RAW_BYTE_LEN
1846    }
1847
1848    pub fn seq_rule_set_count_byte_range(&self) -> Range<usize> {
1849        let start = self.coverage_offset_byte_range().end;
1850        start..start + u16::RAW_BYTE_LEN
1851    }
1852
1853    pub fn seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
1854        let seq_rule_set_count = self.seq_rule_set_count();
1855        let start = self.seq_rule_set_count_byte_range().end;
1856        start
1857            ..start
1858                + (transforms::to_usize(seq_rule_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
1859    }
1860}
1861
1862const _: () = assert!(FontData::default_data_long_enough(
1863    SequenceContextFormat1::MIN_SIZE
1864));
1865
1866impl Default for SequenceContextFormat1<'_> {
1867    fn default() -> Self {
1868        Self {
1869            data: FontData::default_format_1_u16_table_data(),
1870        }
1871    }
1872}
1873
1874#[cfg(feature = "experimental_traverse")]
1875impl<'a> SomeTable<'a> for SequenceContextFormat1<'a> {
1876    fn type_name(&self) -> &str {
1877        "SequenceContextFormat1"
1878    }
1879    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1880        match idx {
1881            0usize => Some(Field::new("format", self.format())),
1882            1usize => Some(Field::new(
1883                "coverage_offset",
1884                FieldType::offset(self.coverage_offset(), self.coverage()),
1885            )),
1886            2usize => Some(Field::new("seq_rule_set_count", self.seq_rule_set_count())),
1887            3usize => Some(Field::new(
1888                "seq_rule_set_offsets",
1889                FieldType::from(self.seq_rule_sets()),
1890            )),
1891            _ => None,
1892        }
1893    }
1894}
1895
1896#[cfg(feature = "experimental_traverse")]
1897#[allow(clippy::needless_lifetimes)]
1898impl<'a> std::fmt::Debug for SequenceContextFormat1<'a> {
1899    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1900        (self as &dyn SomeTable<'a>).fmt(f)
1901    }
1902}
1903
1904impl<'a> MinByteRange<'a> for SequenceRuleSet<'a> {
1905    fn min_byte_range(&self) -> Range<usize> {
1906        0..self.seq_rule_offsets_byte_range().end
1907    }
1908    fn min_table_bytes(&self) -> &'a [u8] {
1909        let range = self.min_byte_range();
1910        self.data.as_bytes().get(range).unwrap_or_default()
1911    }
1912}
1913
1914impl<'a> FontRead<'a> for SequenceRuleSet<'a> {
1915    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1916        #[allow(clippy::absurd_extreme_comparisons)]
1917        if data.len() < Self::MIN_SIZE {
1918            return Err(ReadError::OutOfBounds);
1919        }
1920        Ok(Self { data })
1921    }
1922}
1923
1924/// Part of [SequenceContextFormat1]
1925#[derive(Clone)]
1926pub struct SequenceRuleSet<'a> {
1927    data: FontData<'a>,
1928}
1929
1930#[allow(clippy::needless_lifetimes)]
1931impl<'a> SequenceRuleSet<'a> {
1932    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1933    basic_table_impls!(impl_the_methods);
1934
1935    /// Number of SequenceRule tables
1936    pub fn seq_rule_count(&self) -> u16 {
1937        let range = self.seq_rule_count_byte_range();
1938        self.data.read_at(range.start).ok().unwrap()
1939    }
1940
1941    /// Array of offsets to SequenceRule tables, from beginning of the
1942    /// SequenceRuleSet table
1943    pub fn seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
1944        let range = self.seq_rule_offsets_byte_range();
1945        self.data.read_array(range).ok().unwrap_or_default()
1946    }
1947
1948    /// A dynamically resolving wrapper for [`seq_rule_offsets`][Self::seq_rule_offsets].
1949    pub fn seq_rules(&self) -> ArrayOfOffsets<'a, SequenceRule<'a>, Offset16> {
1950        let data = self.data;
1951        let offsets = self.seq_rule_offsets();
1952        ArrayOfOffsets::new(offsets, data, ())
1953    }
1954
1955    pub fn seq_rule_count_byte_range(&self) -> Range<usize> {
1956        let start = 0;
1957        start..start + u16::RAW_BYTE_LEN
1958    }
1959
1960    pub fn seq_rule_offsets_byte_range(&self) -> Range<usize> {
1961        let seq_rule_count = self.seq_rule_count();
1962        let start = self.seq_rule_count_byte_range().end;
1963        start..start + (transforms::to_usize(seq_rule_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
1964    }
1965}
1966
1967const _: () = assert!(FontData::default_data_long_enough(
1968    SequenceRuleSet::MIN_SIZE
1969));
1970
1971impl Default for SequenceRuleSet<'_> {
1972    fn default() -> Self {
1973        Self {
1974            data: FontData::default_table_data(),
1975        }
1976    }
1977}
1978
1979#[cfg(feature = "experimental_traverse")]
1980impl<'a> SomeTable<'a> for SequenceRuleSet<'a> {
1981    fn type_name(&self) -> &str {
1982        "SequenceRuleSet"
1983    }
1984    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1985        match idx {
1986            0usize => Some(Field::new("seq_rule_count", self.seq_rule_count())),
1987            1usize => Some(Field::new(
1988                "seq_rule_offsets",
1989                FieldType::from(self.seq_rules()),
1990            )),
1991            _ => None,
1992        }
1993    }
1994}
1995
1996#[cfg(feature = "experimental_traverse")]
1997#[allow(clippy::needless_lifetimes)]
1998impl<'a> std::fmt::Debug for SequenceRuleSet<'a> {
1999    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2000        (self as &dyn SomeTable<'a>).fmt(f)
2001    }
2002}
2003
2004impl<'a> MinByteRange<'a> for SequenceRule<'a> {
2005    fn min_byte_range(&self) -> Range<usize> {
2006        0..self.seq_lookup_records_byte_range().end
2007    }
2008    fn min_table_bytes(&self) -> &'a [u8] {
2009        let range = self.min_byte_range();
2010        self.data.as_bytes().get(range).unwrap_or_default()
2011    }
2012}
2013
2014impl<'a> FontRead<'a> for SequenceRule<'a> {
2015    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2016        #[allow(clippy::absurd_extreme_comparisons)]
2017        if data.len() < Self::MIN_SIZE {
2018            return Err(ReadError::OutOfBounds);
2019        }
2020        Ok(Self { data })
2021    }
2022}
2023
2024/// Part of [SequenceContextFormat1]
2025#[derive(Clone)]
2026pub struct SequenceRule<'a> {
2027    data: FontData<'a>,
2028}
2029
2030#[allow(clippy::needless_lifetimes)]
2031impl<'a> SequenceRule<'a> {
2032    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2033    basic_table_impls!(impl_the_methods);
2034
2035    /// Number of glyphs in the input glyph sequence
2036    pub fn glyph_count(&self) -> u16 {
2037        let range = self.glyph_count_byte_range();
2038        self.data.read_at(range.start).ok().unwrap()
2039    }
2040
2041    /// Number of SequenceLookupRecords
2042    pub fn seq_lookup_count(&self) -> u16 {
2043        let range = self.seq_lookup_count_byte_range();
2044        self.data.read_at(range.start).ok().unwrap()
2045    }
2046
2047    /// Array of input glyph IDs—starting with the second glyph
2048    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
2049        let range = self.input_sequence_byte_range();
2050        self.data.read_array(range).ok().unwrap_or_default()
2051    }
2052
2053    /// Array of Sequence lookup records
2054    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2055        let range = self.seq_lookup_records_byte_range();
2056        self.data.read_array(range).ok().unwrap_or_default()
2057    }
2058
2059    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2060        let start = 0;
2061        start..start + u16::RAW_BYTE_LEN
2062    }
2063
2064    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2065        let start = self.glyph_count_byte_range().end;
2066        start..start + u16::RAW_BYTE_LEN
2067    }
2068
2069    pub fn input_sequence_byte_range(&self) -> Range<usize> {
2070        let glyph_count = self.glyph_count();
2071        let start = self.seq_lookup_count_byte_range().end;
2072        start
2073            ..start
2074                + (transforms::subtract(glyph_count, 1_usize))
2075                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
2076    }
2077
2078    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2079        let seq_lookup_count = self.seq_lookup_count();
2080        let start = self.input_sequence_byte_range().end;
2081        start
2082            ..start
2083                + (transforms::to_usize(seq_lookup_count))
2084                    .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
2085    }
2086}
2087
2088const _: () = assert!(FontData::default_data_long_enough(SequenceRule::MIN_SIZE));
2089
2090impl Default for SequenceRule<'_> {
2091    fn default() -> Self {
2092        Self {
2093            data: FontData::default_table_data(),
2094        }
2095    }
2096}
2097
2098#[cfg(feature = "experimental_traverse")]
2099impl<'a> SomeTable<'a> for SequenceRule<'a> {
2100    fn type_name(&self) -> &str {
2101        "SequenceRule"
2102    }
2103    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2104        match idx {
2105            0usize => Some(Field::new("glyph_count", self.glyph_count())),
2106            1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
2107            2usize => Some(Field::new("input_sequence", self.input_sequence())),
2108            3usize => Some(Field::new(
2109                "seq_lookup_records",
2110                traversal::FieldType::array_of_records(
2111                    stringify!(SequenceLookupRecord),
2112                    self.seq_lookup_records(),
2113                    self.offset_data(),
2114                ),
2115            )),
2116            _ => None,
2117        }
2118    }
2119}
2120
2121#[cfg(feature = "experimental_traverse")]
2122#[allow(clippy::needless_lifetimes)]
2123impl<'a> std::fmt::Debug for SequenceRule<'a> {
2124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2125        (self as &dyn SomeTable<'a>).fmt(f)
2126    }
2127}
2128
2129impl Format<u16> for SequenceContextFormat2<'_> {
2130    const FORMAT: u16 = 2;
2131}
2132
2133impl<'a> MinByteRange<'a> for SequenceContextFormat2<'a> {
2134    fn min_byte_range(&self) -> Range<usize> {
2135        0..self.class_seq_rule_set_offsets_byte_range().end
2136    }
2137    fn min_table_bytes(&self) -> &'a [u8] {
2138        let range = self.min_byte_range();
2139        self.data.as_bytes().get(range).unwrap_or_default()
2140    }
2141}
2142
2143impl<'a> FontRead<'a> for SequenceContextFormat2<'a> {
2144    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2145        #[allow(clippy::absurd_extreme_comparisons)]
2146        if data.len() < Self::MIN_SIZE {
2147            return Err(ReadError::OutOfBounds);
2148        }
2149        Ok(Self { data })
2150    }
2151}
2152
2153/// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts)
2154#[derive(Clone)]
2155pub struct SequenceContextFormat2<'a> {
2156    data: FontData<'a>,
2157}
2158
2159#[allow(clippy::needless_lifetimes)]
2160impl<'a> SequenceContextFormat2<'a> {
2161    pub const MIN_SIZE: usize =
2162        (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2163    basic_table_impls!(impl_the_methods);
2164
2165    /// Format identifier: format = 2
2166    pub fn format(&self) -> u16 {
2167        let range = self.format_byte_range();
2168        self.data.read_at(range.start).ok().unwrap()
2169    }
2170
2171    /// Offset to Coverage table, from beginning of
2172    /// SequenceContextFormat2 table
2173    pub fn coverage_offset(&self) -> Offset16 {
2174        let range = self.coverage_offset_byte_range();
2175        self.data.read_at(range.start).ok().unwrap()
2176    }
2177
2178    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2179    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2180        let data = self.data;
2181        self.coverage_offset().resolve(data)
2182    }
2183
2184    /// Offset to ClassDef table, from beginning of
2185    /// SequenceContextFormat2 table
2186    pub fn class_def_offset(&self) -> Offset16 {
2187        let range = self.class_def_offset_byte_range();
2188        self.data.read_at(range.start).ok().unwrap()
2189    }
2190
2191    /// Attempt to resolve [`class_def_offset`][Self::class_def_offset].
2192    pub fn class_def(&self) -> Result<ClassDef<'a>, ReadError> {
2193        let data = self.data;
2194        self.class_def_offset().resolve(data)
2195    }
2196
2197    /// Number of ClassSequenceRuleSet tables
2198    pub fn class_seq_rule_set_count(&self) -> u16 {
2199        let range = self.class_seq_rule_set_count_byte_range();
2200        self.data.read_at(range.start).ok().unwrap()
2201    }
2202
2203    /// Array of offsets to ClassSequenceRuleSet tables, from beginning
2204    /// of SequenceContextFormat2 table (may be NULL)
2205    pub fn class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2206        let range = self.class_seq_rule_set_offsets_byte_range();
2207        self.data.read_array(range).ok().unwrap_or_default()
2208    }
2209
2210    /// A dynamically resolving wrapper for [`class_seq_rule_set_offsets`][Self::class_seq_rule_set_offsets].
2211    pub fn class_seq_rule_sets(
2212        &self,
2213    ) -> ArrayOfNullableOffsets<'a, ClassSequenceRuleSet<'a>, Offset16> {
2214        let data = self.data;
2215        let offsets = self.class_seq_rule_set_offsets();
2216        ArrayOfNullableOffsets::new(offsets, data, ())
2217    }
2218
2219    pub fn format_byte_range(&self) -> Range<usize> {
2220        let start = 0;
2221        start..start + u16::RAW_BYTE_LEN
2222    }
2223
2224    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2225        let start = self.format_byte_range().end;
2226        start..start + Offset16::RAW_BYTE_LEN
2227    }
2228
2229    pub fn class_def_offset_byte_range(&self) -> Range<usize> {
2230        let start = self.coverage_offset_byte_range().end;
2231        start..start + Offset16::RAW_BYTE_LEN
2232    }
2233
2234    pub fn class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
2235        let start = self.class_def_offset_byte_range().end;
2236        start..start + u16::RAW_BYTE_LEN
2237    }
2238
2239    pub fn class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
2240        let class_seq_rule_set_count = self.class_seq_rule_set_count();
2241        let start = self.class_seq_rule_set_count_byte_range().end;
2242        start
2243            ..start
2244                + (transforms::to_usize(class_seq_rule_set_count))
2245                    .saturating_mul(Offset16::RAW_BYTE_LEN)
2246    }
2247}
2248
2249#[cfg(feature = "experimental_traverse")]
2250impl<'a> SomeTable<'a> for SequenceContextFormat2<'a> {
2251    fn type_name(&self) -> &str {
2252        "SequenceContextFormat2"
2253    }
2254    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2255        match idx {
2256            0usize => Some(Field::new("format", self.format())),
2257            1usize => Some(Field::new(
2258                "coverage_offset",
2259                FieldType::offset(self.coverage_offset(), self.coverage()),
2260            )),
2261            2usize => Some(Field::new(
2262                "class_def_offset",
2263                FieldType::offset(self.class_def_offset(), self.class_def()),
2264            )),
2265            3usize => Some(Field::new(
2266                "class_seq_rule_set_count",
2267                self.class_seq_rule_set_count(),
2268            )),
2269            4usize => Some(Field::new(
2270                "class_seq_rule_set_offsets",
2271                FieldType::from(self.class_seq_rule_sets()),
2272            )),
2273            _ => None,
2274        }
2275    }
2276}
2277
2278#[cfg(feature = "experimental_traverse")]
2279#[allow(clippy::needless_lifetimes)]
2280impl<'a> std::fmt::Debug for SequenceContextFormat2<'a> {
2281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2282        (self as &dyn SomeTable<'a>).fmt(f)
2283    }
2284}
2285
2286impl<'a> MinByteRange<'a> for ClassSequenceRuleSet<'a> {
2287    fn min_byte_range(&self) -> Range<usize> {
2288        0..self.class_seq_rule_offsets_byte_range().end
2289    }
2290    fn min_table_bytes(&self) -> &'a [u8] {
2291        let range = self.min_byte_range();
2292        self.data.as_bytes().get(range).unwrap_or_default()
2293    }
2294}
2295
2296impl<'a> FontRead<'a> for ClassSequenceRuleSet<'a> {
2297    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2298        #[allow(clippy::absurd_extreme_comparisons)]
2299        if data.len() < Self::MIN_SIZE {
2300            return Err(ReadError::OutOfBounds);
2301        }
2302        Ok(Self { data })
2303    }
2304}
2305
2306/// Part of [SequenceContextFormat2]
2307#[derive(Clone)]
2308pub struct ClassSequenceRuleSet<'a> {
2309    data: FontData<'a>,
2310}
2311
2312#[allow(clippy::needless_lifetimes)]
2313impl<'a> ClassSequenceRuleSet<'a> {
2314    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2315    basic_table_impls!(impl_the_methods);
2316
2317    /// Number of ClassSequenceRule tables
2318    pub fn class_seq_rule_count(&self) -> u16 {
2319        let range = self.class_seq_rule_count_byte_range();
2320        self.data.read_at(range.start).ok().unwrap()
2321    }
2322
2323    /// Array of offsets to ClassSequenceRule tables, from beginning of
2324    /// ClassSequenceRuleSet table
2325    pub fn class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
2326        let range = self.class_seq_rule_offsets_byte_range();
2327        self.data.read_array(range).ok().unwrap_or_default()
2328    }
2329
2330    /// A dynamically resolving wrapper for [`class_seq_rule_offsets`][Self::class_seq_rule_offsets].
2331    pub fn class_seq_rules(&self) -> ArrayOfOffsets<'a, ClassSequenceRule<'a>, Offset16> {
2332        let data = self.data;
2333        let offsets = self.class_seq_rule_offsets();
2334        ArrayOfOffsets::new(offsets, data, ())
2335    }
2336
2337    pub fn class_seq_rule_count_byte_range(&self) -> Range<usize> {
2338        let start = 0;
2339        start..start + u16::RAW_BYTE_LEN
2340    }
2341
2342    pub fn class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
2343        let class_seq_rule_count = self.class_seq_rule_count();
2344        let start = self.class_seq_rule_count_byte_range().end;
2345        start
2346            ..start
2347                + (transforms::to_usize(class_seq_rule_count))
2348                    .saturating_mul(Offset16::RAW_BYTE_LEN)
2349    }
2350}
2351
2352const _: () = assert!(FontData::default_data_long_enough(
2353    ClassSequenceRuleSet::MIN_SIZE
2354));
2355
2356impl Default for ClassSequenceRuleSet<'_> {
2357    fn default() -> Self {
2358        Self {
2359            data: FontData::default_table_data(),
2360        }
2361    }
2362}
2363
2364#[cfg(feature = "experimental_traverse")]
2365impl<'a> SomeTable<'a> for ClassSequenceRuleSet<'a> {
2366    fn type_name(&self) -> &str {
2367        "ClassSequenceRuleSet"
2368    }
2369    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2370        match idx {
2371            0usize => Some(Field::new(
2372                "class_seq_rule_count",
2373                self.class_seq_rule_count(),
2374            )),
2375            1usize => Some(Field::new(
2376                "class_seq_rule_offsets",
2377                FieldType::from(self.class_seq_rules()),
2378            )),
2379            _ => None,
2380        }
2381    }
2382}
2383
2384#[cfg(feature = "experimental_traverse")]
2385#[allow(clippy::needless_lifetimes)]
2386impl<'a> std::fmt::Debug for ClassSequenceRuleSet<'a> {
2387    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2388        (self as &dyn SomeTable<'a>).fmt(f)
2389    }
2390}
2391
2392impl<'a> MinByteRange<'a> for ClassSequenceRule<'a> {
2393    fn min_byte_range(&self) -> Range<usize> {
2394        0..self.seq_lookup_records_byte_range().end
2395    }
2396    fn min_table_bytes(&self) -> &'a [u8] {
2397        let range = self.min_byte_range();
2398        self.data.as_bytes().get(range).unwrap_or_default()
2399    }
2400}
2401
2402impl<'a> FontRead<'a> for ClassSequenceRule<'a> {
2403    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2404        #[allow(clippy::absurd_extreme_comparisons)]
2405        if data.len() < Self::MIN_SIZE {
2406            return Err(ReadError::OutOfBounds);
2407        }
2408        Ok(Self { data })
2409    }
2410}
2411
2412/// Part of [SequenceContextFormat2]
2413#[derive(Clone)]
2414pub struct ClassSequenceRule<'a> {
2415    data: FontData<'a>,
2416}
2417
2418#[allow(clippy::needless_lifetimes)]
2419impl<'a> ClassSequenceRule<'a> {
2420    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2421    basic_table_impls!(impl_the_methods);
2422
2423    /// Number of glyphs to be matched
2424    pub fn glyph_count(&self) -> u16 {
2425        let range = self.glyph_count_byte_range();
2426        self.data.read_at(range.start).ok().unwrap()
2427    }
2428
2429    /// Number of SequenceLookupRecords
2430    pub fn seq_lookup_count(&self) -> u16 {
2431        let range = self.seq_lookup_count_byte_range();
2432        self.data.read_at(range.start).ok().unwrap()
2433    }
2434
2435    /// Sequence of classes to be matched to the input glyph sequence,
2436    /// beginning with the second glyph position
2437    pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
2438        let range = self.input_sequence_byte_range();
2439        self.data.read_array(range).ok().unwrap_or_default()
2440    }
2441
2442    /// Array of SequenceLookupRecords
2443    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2444        let range = self.seq_lookup_records_byte_range();
2445        self.data.read_array(range).ok().unwrap_or_default()
2446    }
2447
2448    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2449        let start = 0;
2450        start..start + u16::RAW_BYTE_LEN
2451    }
2452
2453    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2454        let start = self.glyph_count_byte_range().end;
2455        start..start + u16::RAW_BYTE_LEN
2456    }
2457
2458    pub fn input_sequence_byte_range(&self) -> Range<usize> {
2459        let glyph_count = self.glyph_count();
2460        let start = self.seq_lookup_count_byte_range().end;
2461        start
2462            ..start + (transforms::subtract(glyph_count, 1_usize)).saturating_mul(u16::RAW_BYTE_LEN)
2463    }
2464
2465    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2466        let seq_lookup_count = self.seq_lookup_count();
2467        let start = self.input_sequence_byte_range().end;
2468        start
2469            ..start
2470                + (transforms::to_usize(seq_lookup_count))
2471                    .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
2472    }
2473}
2474
2475const _: () = assert!(FontData::default_data_long_enough(
2476    ClassSequenceRule::MIN_SIZE
2477));
2478
2479impl Default for ClassSequenceRule<'_> {
2480    fn default() -> Self {
2481        Self {
2482            data: FontData::default_table_data(),
2483        }
2484    }
2485}
2486
2487#[cfg(feature = "experimental_traverse")]
2488impl<'a> SomeTable<'a> for ClassSequenceRule<'a> {
2489    fn type_name(&self) -> &str {
2490        "ClassSequenceRule"
2491    }
2492    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2493        match idx {
2494            0usize => Some(Field::new("glyph_count", self.glyph_count())),
2495            1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
2496            2usize => Some(Field::new("input_sequence", self.input_sequence())),
2497            3usize => Some(Field::new(
2498                "seq_lookup_records",
2499                traversal::FieldType::array_of_records(
2500                    stringify!(SequenceLookupRecord),
2501                    self.seq_lookup_records(),
2502                    self.offset_data(),
2503                ),
2504            )),
2505            _ => None,
2506        }
2507    }
2508}
2509
2510#[cfg(feature = "experimental_traverse")]
2511#[allow(clippy::needless_lifetimes)]
2512impl<'a> std::fmt::Debug for ClassSequenceRule<'a> {
2513    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2514        (self as &dyn SomeTable<'a>).fmt(f)
2515    }
2516}
2517
2518impl Format<u16> for SequenceContextFormat3<'_> {
2519    const FORMAT: u16 = 3;
2520}
2521
2522impl<'a> MinByteRange<'a> for SequenceContextFormat3<'a> {
2523    fn min_byte_range(&self) -> Range<usize> {
2524        0..self.seq_lookup_records_byte_range().end
2525    }
2526    fn min_table_bytes(&self) -> &'a [u8] {
2527        let range = self.min_byte_range();
2528        self.data.as_bytes().get(range).unwrap_or_default()
2529    }
2530}
2531
2532impl<'a> FontRead<'a> for SequenceContextFormat3<'a> {
2533    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2534        #[allow(clippy::absurd_extreme_comparisons)]
2535        if data.len() < Self::MIN_SIZE {
2536            return Err(ReadError::OutOfBounds);
2537        }
2538        Ok(Self { data })
2539    }
2540}
2541
2542/// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts)
2543#[derive(Clone)]
2544pub struct SequenceContextFormat3<'a> {
2545    data: FontData<'a>,
2546}
2547
2548#[allow(clippy::needless_lifetimes)]
2549impl<'a> SequenceContextFormat3<'a> {
2550    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2551    basic_table_impls!(impl_the_methods);
2552
2553    /// Format identifier: format = 3
2554    pub fn format(&self) -> u16 {
2555        let range = self.format_byte_range();
2556        self.data.read_at(range.start).ok().unwrap()
2557    }
2558
2559    /// Number of glyphs in the input sequence
2560    pub fn glyph_count(&self) -> u16 {
2561        let range = self.glyph_count_byte_range();
2562        self.data.read_at(range.start).ok().unwrap()
2563    }
2564
2565    /// Number of SequenceLookupRecords
2566    pub fn seq_lookup_count(&self) -> u16 {
2567        let range = self.seq_lookup_count_byte_range();
2568        self.data.read_at(range.start).ok().unwrap()
2569    }
2570
2571    /// Array of offsets to Coverage tables, from beginning of
2572    /// SequenceContextFormat3 subtable
2573    pub fn coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
2574        let range = self.coverage_offsets_byte_range();
2575        self.data.read_array(range).ok().unwrap_or_default()
2576    }
2577
2578    /// A dynamically resolving wrapper for [`coverage_offsets`][Self::coverage_offsets].
2579    pub fn coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
2580        let data = self.data;
2581        let offsets = self.coverage_offsets();
2582        ArrayOfOffsets::new(offsets, data, ())
2583    }
2584
2585    /// Array of SequenceLookupRecords
2586    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
2587        let range = self.seq_lookup_records_byte_range();
2588        self.data.read_array(range).ok().unwrap_or_default()
2589    }
2590
2591    pub fn format_byte_range(&self) -> Range<usize> {
2592        let start = 0;
2593        start..start + u16::RAW_BYTE_LEN
2594    }
2595
2596    pub fn glyph_count_byte_range(&self) -> Range<usize> {
2597        let start = self.format_byte_range().end;
2598        start..start + u16::RAW_BYTE_LEN
2599    }
2600
2601    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
2602        let start = self.glyph_count_byte_range().end;
2603        start..start + u16::RAW_BYTE_LEN
2604    }
2605
2606    pub fn coverage_offsets_byte_range(&self) -> Range<usize> {
2607        let glyph_count = self.glyph_count();
2608        let start = self.seq_lookup_count_byte_range().end;
2609        start..start + (transforms::to_usize(glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
2610    }
2611
2612    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
2613        let seq_lookup_count = self.seq_lookup_count();
2614        let start = self.coverage_offsets_byte_range().end;
2615        start
2616            ..start
2617                + (transforms::to_usize(seq_lookup_count))
2618                    .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
2619    }
2620}
2621
2622#[cfg(feature = "experimental_traverse")]
2623impl<'a> SomeTable<'a> for SequenceContextFormat3<'a> {
2624    fn type_name(&self) -> &str {
2625        "SequenceContextFormat3"
2626    }
2627    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2628        match idx {
2629            0usize => Some(Field::new("format", self.format())),
2630            1usize => Some(Field::new("glyph_count", self.glyph_count())),
2631            2usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
2632            3usize => Some(Field::new(
2633                "coverage_offsets",
2634                FieldType::from(self.coverages()),
2635            )),
2636            4usize => Some(Field::new(
2637                "seq_lookup_records",
2638                traversal::FieldType::array_of_records(
2639                    stringify!(SequenceLookupRecord),
2640                    self.seq_lookup_records(),
2641                    self.offset_data(),
2642                ),
2643            )),
2644            _ => None,
2645        }
2646    }
2647}
2648
2649#[cfg(feature = "experimental_traverse")]
2650#[allow(clippy::needless_lifetimes)]
2651impl<'a> std::fmt::Debug for SequenceContextFormat3<'a> {
2652    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2653        (self as &dyn SomeTable<'a>).fmt(f)
2654    }
2655}
2656
2657#[derive(Clone)]
2658pub enum SequenceContext<'a> {
2659    Format1(SequenceContextFormat1<'a>),
2660    Format2(SequenceContextFormat2<'a>),
2661    Format3(SequenceContextFormat3<'a>),
2662}
2663
2664impl Default for SequenceContext<'_> {
2665    fn default() -> Self {
2666        Self::Format1(Default::default())
2667    }
2668}
2669
2670impl<'a> SequenceContext<'a> {
2671    ///Return the `FontData` used to resolve offsets for this table.
2672    pub fn offset_data(&self) -> FontData<'a> {
2673        match self {
2674            Self::Format1(item) => item.offset_data(),
2675            Self::Format2(item) => item.offset_data(),
2676            Self::Format3(item) => item.offset_data(),
2677        }
2678    }
2679
2680    /// Format identifier: format = 1
2681    pub fn format(&self) -> u16 {
2682        match self {
2683            Self::Format1(item) => item.format(),
2684            Self::Format2(item) => item.format(),
2685            Self::Format3(item) => item.format(),
2686        }
2687    }
2688}
2689
2690impl<'a> FontRead<'a> for SequenceContext<'a> {
2691    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2692        let format: u16 = data.read_at(0usize)?;
2693        match format {
2694            SequenceContextFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
2695            SequenceContextFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
2696            SequenceContextFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
2697            other => Err(ReadError::InvalidFormat(other.into())),
2698        }
2699    }
2700}
2701
2702impl<'a> MinByteRange<'a> for SequenceContext<'a> {
2703    fn min_byte_range(&self) -> Range<usize> {
2704        match self {
2705            Self::Format1(item) => item.min_byte_range(),
2706            Self::Format2(item) => item.min_byte_range(),
2707            Self::Format3(item) => item.min_byte_range(),
2708        }
2709    }
2710    fn min_table_bytes(&self) -> &'a [u8] {
2711        match self {
2712            Self::Format1(item) => item.min_table_bytes(),
2713            Self::Format2(item) => item.min_table_bytes(),
2714            Self::Format3(item) => item.min_table_bytes(),
2715        }
2716    }
2717}
2718
2719#[cfg(feature = "experimental_traverse")]
2720impl<'a> SequenceContext<'a> {
2721    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
2722        match self {
2723            Self::Format1(table) => table,
2724            Self::Format2(table) => table,
2725            Self::Format3(table) => table,
2726        }
2727    }
2728}
2729
2730#[cfg(feature = "experimental_traverse")]
2731impl std::fmt::Debug for SequenceContext<'_> {
2732    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2733        self.dyn_inner().fmt(f)
2734    }
2735}
2736
2737#[cfg(feature = "experimental_traverse")]
2738impl<'a> SomeTable<'a> for SequenceContext<'a> {
2739    fn type_name(&self) -> &str {
2740        self.dyn_inner().type_name()
2741    }
2742    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2743        self.dyn_inner().get_field(idx)
2744    }
2745}
2746
2747impl Format<u16> for ChainedSequenceContextFormat1<'_> {
2748    const FORMAT: u16 = 1;
2749}
2750
2751impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat1<'a> {
2752    fn min_byte_range(&self) -> Range<usize> {
2753        0..self.chained_seq_rule_set_offsets_byte_range().end
2754    }
2755    fn min_table_bytes(&self) -> &'a [u8] {
2756        let range = self.min_byte_range();
2757        self.data.as_bytes().get(range).unwrap_or_default()
2758    }
2759}
2760
2761impl<'a> FontRead<'a> for ChainedSequenceContextFormat1<'a> {
2762    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2763        #[allow(clippy::absurd_extreme_comparisons)]
2764        if data.len() < Self::MIN_SIZE {
2765            return Err(ReadError::OutOfBounds);
2766        }
2767        Ok(Self { data })
2768    }
2769}
2770
2771/// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts)
2772#[derive(Clone)]
2773pub struct ChainedSequenceContextFormat1<'a> {
2774    data: FontData<'a>,
2775}
2776
2777#[allow(clippy::needless_lifetimes)]
2778impl<'a> ChainedSequenceContextFormat1<'a> {
2779    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2780    basic_table_impls!(impl_the_methods);
2781
2782    /// Format identifier: format = 1
2783    pub fn format(&self) -> u16 {
2784        let range = self.format_byte_range();
2785        self.data.read_at(range.start).ok().unwrap()
2786    }
2787
2788    /// Offset to Coverage table, from beginning of
2789    /// ChainSequenceContextFormat1 table
2790    pub fn coverage_offset(&self) -> Offset16 {
2791        let range = self.coverage_offset_byte_range();
2792        self.data.read_at(range.start).ok().unwrap()
2793    }
2794
2795    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2796    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2797        let data = self.data;
2798        self.coverage_offset().resolve(data)
2799    }
2800
2801    /// Number of ChainedSequenceRuleSet tables
2802    pub fn chained_seq_rule_set_count(&self) -> u16 {
2803        let range = self.chained_seq_rule_set_count_byte_range();
2804        self.data.read_at(range.start).ok().unwrap()
2805    }
2806
2807    /// Array of offsets to ChainedSeqRuleSet tables, from beginning of
2808    /// ChainedSequenceContextFormat1 table (may be NULL)
2809    pub fn chained_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2810        let range = self.chained_seq_rule_set_offsets_byte_range();
2811        self.data.read_array(range).ok().unwrap_or_default()
2812    }
2813
2814    /// A dynamically resolving wrapper for [`chained_seq_rule_set_offsets`][Self::chained_seq_rule_set_offsets].
2815    pub fn chained_seq_rule_sets(
2816        &self,
2817    ) -> ArrayOfNullableOffsets<'a, ChainedSequenceRuleSet<'a>, Offset16> {
2818        let data = self.data;
2819        let offsets = self.chained_seq_rule_set_offsets();
2820        ArrayOfNullableOffsets::new(offsets, data, ())
2821    }
2822
2823    pub fn format_byte_range(&self) -> Range<usize> {
2824        let start = 0;
2825        start..start + u16::RAW_BYTE_LEN
2826    }
2827
2828    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2829        let start = self.format_byte_range().end;
2830        start..start + Offset16::RAW_BYTE_LEN
2831    }
2832
2833    pub fn chained_seq_rule_set_count_byte_range(&self) -> Range<usize> {
2834        let start = self.coverage_offset_byte_range().end;
2835        start..start + u16::RAW_BYTE_LEN
2836    }
2837
2838    pub fn chained_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
2839        let chained_seq_rule_set_count = self.chained_seq_rule_set_count();
2840        let start = self.chained_seq_rule_set_count_byte_range().end;
2841        start
2842            ..start
2843                + (transforms::to_usize(chained_seq_rule_set_count))
2844                    .saturating_mul(Offset16::RAW_BYTE_LEN)
2845    }
2846}
2847
2848const _: () = assert!(FontData::default_data_long_enough(
2849    ChainedSequenceContextFormat1::MIN_SIZE
2850));
2851
2852impl Default for ChainedSequenceContextFormat1<'_> {
2853    fn default() -> Self {
2854        Self {
2855            data: FontData::default_format_1_u16_table_data(),
2856        }
2857    }
2858}
2859
2860#[cfg(feature = "experimental_traverse")]
2861impl<'a> SomeTable<'a> for ChainedSequenceContextFormat1<'a> {
2862    fn type_name(&self) -> &str {
2863        "ChainedSequenceContextFormat1"
2864    }
2865    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2866        match idx {
2867            0usize => Some(Field::new("format", self.format())),
2868            1usize => Some(Field::new(
2869                "coverage_offset",
2870                FieldType::offset(self.coverage_offset(), self.coverage()),
2871            )),
2872            2usize => Some(Field::new(
2873                "chained_seq_rule_set_count",
2874                self.chained_seq_rule_set_count(),
2875            )),
2876            3usize => Some(Field::new(
2877                "chained_seq_rule_set_offsets",
2878                FieldType::from(self.chained_seq_rule_sets()),
2879            )),
2880            _ => None,
2881        }
2882    }
2883}
2884
2885#[cfg(feature = "experimental_traverse")]
2886#[allow(clippy::needless_lifetimes)]
2887impl<'a> std::fmt::Debug for ChainedSequenceContextFormat1<'a> {
2888    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2889        (self as &dyn SomeTable<'a>).fmt(f)
2890    }
2891}
2892
2893impl<'a> MinByteRange<'a> for ChainedSequenceRuleSet<'a> {
2894    fn min_byte_range(&self) -> Range<usize> {
2895        0..self.chained_seq_rule_offsets_byte_range().end
2896    }
2897    fn min_table_bytes(&self) -> &'a [u8] {
2898        let range = self.min_byte_range();
2899        self.data.as_bytes().get(range).unwrap_or_default()
2900    }
2901}
2902
2903impl<'a> FontRead<'a> for ChainedSequenceRuleSet<'a> {
2904    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2905        #[allow(clippy::absurd_extreme_comparisons)]
2906        if data.len() < Self::MIN_SIZE {
2907            return Err(ReadError::OutOfBounds);
2908        }
2909        Ok(Self { data })
2910    }
2911}
2912
2913/// Part of [ChainedSequenceContextFormat1]
2914#[derive(Clone)]
2915pub struct ChainedSequenceRuleSet<'a> {
2916    data: FontData<'a>,
2917}
2918
2919#[allow(clippy::needless_lifetimes)]
2920impl<'a> ChainedSequenceRuleSet<'a> {
2921    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2922    basic_table_impls!(impl_the_methods);
2923
2924    /// Number of ChainedSequenceRule tables
2925    pub fn chained_seq_rule_count(&self) -> u16 {
2926        let range = self.chained_seq_rule_count_byte_range();
2927        self.data.read_at(range.start).ok().unwrap()
2928    }
2929
2930    /// Array of offsets to ChainedSequenceRule tables, from beginning
2931    /// of ChainedSequenceRuleSet table
2932    pub fn chained_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
2933        let range = self.chained_seq_rule_offsets_byte_range();
2934        self.data.read_array(range).ok().unwrap_or_default()
2935    }
2936
2937    /// A dynamically resolving wrapper for [`chained_seq_rule_offsets`][Self::chained_seq_rule_offsets].
2938    pub fn chained_seq_rules(&self) -> ArrayOfOffsets<'a, ChainedSequenceRule<'a>, Offset16> {
2939        let data = self.data;
2940        let offsets = self.chained_seq_rule_offsets();
2941        ArrayOfOffsets::new(offsets, data, ())
2942    }
2943
2944    pub fn chained_seq_rule_count_byte_range(&self) -> Range<usize> {
2945        let start = 0;
2946        start..start + u16::RAW_BYTE_LEN
2947    }
2948
2949    pub fn chained_seq_rule_offsets_byte_range(&self) -> Range<usize> {
2950        let chained_seq_rule_count = self.chained_seq_rule_count();
2951        let start = self.chained_seq_rule_count_byte_range().end;
2952        start
2953            ..start
2954                + (transforms::to_usize(chained_seq_rule_count))
2955                    .saturating_mul(Offset16::RAW_BYTE_LEN)
2956    }
2957}
2958
2959const _: () = assert!(FontData::default_data_long_enough(
2960    ChainedSequenceRuleSet::MIN_SIZE
2961));
2962
2963impl Default for ChainedSequenceRuleSet<'_> {
2964    fn default() -> Self {
2965        Self {
2966            data: FontData::default_table_data(),
2967        }
2968    }
2969}
2970
2971#[cfg(feature = "experimental_traverse")]
2972impl<'a> SomeTable<'a> for ChainedSequenceRuleSet<'a> {
2973    fn type_name(&self) -> &str {
2974        "ChainedSequenceRuleSet"
2975    }
2976    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2977        match idx {
2978            0usize => Some(Field::new(
2979                "chained_seq_rule_count",
2980                self.chained_seq_rule_count(),
2981            )),
2982            1usize => Some(Field::new(
2983                "chained_seq_rule_offsets",
2984                FieldType::from(self.chained_seq_rules()),
2985            )),
2986            _ => None,
2987        }
2988    }
2989}
2990
2991#[cfg(feature = "experimental_traverse")]
2992#[allow(clippy::needless_lifetimes)]
2993impl<'a> std::fmt::Debug for ChainedSequenceRuleSet<'a> {
2994    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2995        (self as &dyn SomeTable<'a>).fmt(f)
2996    }
2997}
2998
2999impl<'a> MinByteRange<'a> for ChainedSequenceRule<'a> {
3000    fn min_byte_range(&self) -> Range<usize> {
3001        0..self.seq_lookup_records_byte_range().end
3002    }
3003    fn min_table_bytes(&self) -> &'a [u8] {
3004        let range = self.min_byte_range();
3005        self.data.as_bytes().get(range).unwrap_or_default()
3006    }
3007}
3008
3009impl<'a> FontRead<'a> for ChainedSequenceRule<'a> {
3010    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3011        #[allow(clippy::absurd_extreme_comparisons)]
3012        if data.len() < Self::MIN_SIZE {
3013            return Err(ReadError::OutOfBounds);
3014        }
3015        Ok(Self { data })
3016    }
3017}
3018
3019/// Part of [ChainedSequenceContextFormat1]
3020#[derive(Clone)]
3021pub struct ChainedSequenceRule<'a> {
3022    data: FontData<'a>,
3023}
3024
3025#[allow(clippy::needless_lifetimes)]
3026impl<'a> ChainedSequenceRule<'a> {
3027    pub const MIN_SIZE: usize =
3028        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
3029    basic_table_impls!(impl_the_methods);
3030
3031    /// Number of glyphs in the backtrack sequence
3032    pub fn backtrack_glyph_count(&self) -> u16 {
3033        let range = self.backtrack_glyph_count_byte_range();
3034        self.data.read_at(range.start).ok().unwrap()
3035    }
3036
3037    /// Array of backtrack glyph IDs
3038    pub fn backtrack_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
3039        let range = self.backtrack_sequence_byte_range();
3040        self.data.read_array(range).ok().unwrap_or_default()
3041    }
3042
3043    /// Number of glyphs in the input sequence
3044    pub fn input_glyph_count(&self) -> u16 {
3045        let range = self.input_glyph_count_byte_range();
3046        self.data.read_at(range.start).ok().unwrap_or_default()
3047    }
3048
3049    /// Array of input glyph IDs—start with second glyph
3050    pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
3051        let range = self.input_sequence_byte_range();
3052        self.data.read_array(range).ok().unwrap_or_default()
3053    }
3054
3055    /// Number of glyphs in the lookahead sequence
3056    pub fn lookahead_glyph_count(&self) -> u16 {
3057        let range = self.lookahead_glyph_count_byte_range();
3058        self.data.read_at(range.start).ok().unwrap_or_default()
3059    }
3060
3061    /// Array of lookahead glyph IDs
3062    pub fn lookahead_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
3063        let range = self.lookahead_sequence_byte_range();
3064        self.data.read_array(range).ok().unwrap_or_default()
3065    }
3066
3067    /// Number of SequenceLookupRecords
3068    pub fn seq_lookup_count(&self) -> u16 {
3069        let range = self.seq_lookup_count_byte_range();
3070        self.data.read_at(range.start).ok().unwrap_or_default()
3071    }
3072
3073    /// Array of SequenceLookupRecords
3074    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3075        let range = self.seq_lookup_records_byte_range();
3076        self.data.read_array(range).ok().unwrap_or_default()
3077    }
3078
3079    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3080        let start = 0;
3081        start..start + u16::RAW_BYTE_LEN
3082    }
3083
3084    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
3085        let backtrack_glyph_count = self.backtrack_glyph_count();
3086        let start = self.backtrack_glyph_count_byte_range().end;
3087        start
3088            ..start
3089                + (transforms::to_usize(backtrack_glyph_count))
3090                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
3091    }
3092
3093    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3094        let start = self.backtrack_sequence_byte_range().end;
3095        start..start + u16::RAW_BYTE_LEN
3096    }
3097
3098    pub fn input_sequence_byte_range(&self) -> Range<usize> {
3099        let input_glyph_count = self.input_glyph_count();
3100        let start = self.input_glyph_count_byte_range().end;
3101        start
3102            ..start
3103                + (transforms::subtract(input_glyph_count, 1_usize))
3104                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
3105    }
3106
3107    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3108        let start = self.input_sequence_byte_range().end;
3109        start..start + u16::RAW_BYTE_LEN
3110    }
3111
3112    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
3113        let lookahead_glyph_count = self.lookahead_glyph_count();
3114        let start = self.lookahead_glyph_count_byte_range().end;
3115        start
3116            ..start
3117                + (transforms::to_usize(lookahead_glyph_count))
3118                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
3119    }
3120
3121    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3122        let start = self.lookahead_sequence_byte_range().end;
3123        start..start + u16::RAW_BYTE_LEN
3124    }
3125
3126    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3127        let seq_lookup_count = self.seq_lookup_count();
3128        let start = self.seq_lookup_count_byte_range().end;
3129        start
3130            ..start
3131                + (transforms::to_usize(seq_lookup_count))
3132                    .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
3133    }
3134}
3135
3136const _: () = assert!(FontData::default_data_long_enough(
3137    ChainedSequenceRule::MIN_SIZE
3138));
3139
3140impl Default for ChainedSequenceRule<'_> {
3141    fn default() -> Self {
3142        Self {
3143            data: FontData::default_table_data(),
3144        }
3145    }
3146}
3147
3148#[cfg(feature = "experimental_traverse")]
3149impl<'a> SomeTable<'a> for ChainedSequenceRule<'a> {
3150    fn type_name(&self) -> &str {
3151        "ChainedSequenceRule"
3152    }
3153    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3154        match idx {
3155            0usize => Some(Field::new(
3156                "backtrack_glyph_count",
3157                self.backtrack_glyph_count(),
3158            )),
3159            1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())),
3160            2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
3161            3usize => Some(Field::new("input_sequence", self.input_sequence())),
3162            4usize => Some(Field::new(
3163                "lookahead_glyph_count",
3164                self.lookahead_glyph_count(),
3165            )),
3166            5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())),
3167            6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
3168            7usize => Some(Field::new(
3169                "seq_lookup_records",
3170                traversal::FieldType::array_of_records(
3171                    stringify!(SequenceLookupRecord),
3172                    self.seq_lookup_records(),
3173                    self.offset_data(),
3174                ),
3175            )),
3176            _ => None,
3177        }
3178    }
3179}
3180
3181#[cfg(feature = "experimental_traverse")]
3182#[allow(clippy::needless_lifetimes)]
3183impl<'a> std::fmt::Debug for ChainedSequenceRule<'a> {
3184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3185        (self as &dyn SomeTable<'a>).fmt(f)
3186    }
3187}
3188
3189impl Format<u16> for ChainedSequenceContextFormat2<'_> {
3190    const FORMAT: u16 = 2;
3191}
3192
3193impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat2<'a> {
3194    fn min_byte_range(&self) -> Range<usize> {
3195        0..self.chained_class_seq_rule_set_offsets_byte_range().end
3196    }
3197    fn min_table_bytes(&self) -> &'a [u8] {
3198        let range = self.min_byte_range();
3199        self.data.as_bytes().get(range).unwrap_or_default()
3200    }
3201}
3202
3203impl<'a> FontRead<'a> for ChainedSequenceContextFormat2<'a> {
3204    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3205        #[allow(clippy::absurd_extreme_comparisons)]
3206        if data.len() < Self::MIN_SIZE {
3207            return Err(ReadError::OutOfBounds);
3208        }
3209        Ok(Self { data })
3210    }
3211}
3212
3213/// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts)
3214#[derive(Clone)]
3215pub struct ChainedSequenceContextFormat2<'a> {
3216    data: FontData<'a>,
3217}
3218
3219#[allow(clippy::needless_lifetimes)]
3220impl<'a> ChainedSequenceContextFormat2<'a> {
3221    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3222        + Offset16::RAW_BYTE_LEN
3223        + Offset16::RAW_BYTE_LEN
3224        + Offset16::RAW_BYTE_LEN
3225        + Offset16::RAW_BYTE_LEN
3226        + u16::RAW_BYTE_LEN);
3227    basic_table_impls!(impl_the_methods);
3228
3229    /// Format identifier: format = 2
3230    pub fn format(&self) -> u16 {
3231        let range = self.format_byte_range();
3232        self.data.read_at(range.start).ok().unwrap()
3233    }
3234
3235    /// Offset to Coverage table, from beginning of
3236    /// ChainedSequenceContextFormat2 table
3237    pub fn coverage_offset(&self) -> Offset16 {
3238        let range = self.coverage_offset_byte_range();
3239        self.data.read_at(range.start).ok().unwrap()
3240    }
3241
3242    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
3243    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3244        let data = self.data;
3245        self.coverage_offset().resolve(data)
3246    }
3247
3248    /// Offset to ClassDef table containing backtrack sequence context,
3249    /// from beginning of ChainedSequenceContextFormat2 table
3250    pub fn backtrack_class_def_offset(&self) -> Offset16 {
3251        let range = self.backtrack_class_def_offset_byte_range();
3252        self.data.read_at(range.start).ok().unwrap()
3253    }
3254
3255    /// Attempt to resolve [`backtrack_class_def_offset`][Self::backtrack_class_def_offset].
3256    pub fn backtrack_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3257        let data = self.data;
3258        self.backtrack_class_def_offset().resolve(data)
3259    }
3260
3261    /// Offset to ClassDef table containing input sequence context,
3262    /// from beginning of ChainedSequenceContextFormat2 table
3263    pub fn input_class_def_offset(&self) -> Offset16 {
3264        let range = self.input_class_def_offset_byte_range();
3265        self.data.read_at(range.start).ok().unwrap()
3266    }
3267
3268    /// Attempt to resolve [`input_class_def_offset`][Self::input_class_def_offset].
3269    pub fn input_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3270        let data = self.data;
3271        self.input_class_def_offset().resolve(data)
3272    }
3273
3274    /// Offset to ClassDef table containing lookahead sequence context,
3275    /// from beginning of ChainedSequenceContextFormat2 table
3276    pub fn lookahead_class_def_offset(&self) -> Offset16 {
3277        let range = self.lookahead_class_def_offset_byte_range();
3278        self.data.read_at(range.start).ok().unwrap()
3279    }
3280
3281    /// Attempt to resolve [`lookahead_class_def_offset`][Self::lookahead_class_def_offset].
3282    pub fn lookahead_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
3283        let data = self.data;
3284        self.lookahead_class_def_offset().resolve(data)
3285    }
3286
3287    /// Number of ChainedClassSequenceRuleSet tables
3288    pub fn chained_class_seq_rule_set_count(&self) -> u16 {
3289        let range = self.chained_class_seq_rule_set_count_byte_range();
3290        self.data.read_at(range.start).ok().unwrap()
3291    }
3292
3293    /// Array of offsets to ChainedClassSequenceRuleSet tables, from
3294    /// beginning of ChainedSequenceContextFormat2 table (may be NULL)
3295    pub fn chained_class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3296        let range = self.chained_class_seq_rule_set_offsets_byte_range();
3297        self.data.read_array(range).ok().unwrap_or_default()
3298    }
3299
3300    /// A dynamically resolving wrapper for [`chained_class_seq_rule_set_offsets`][Self::chained_class_seq_rule_set_offsets].
3301    pub fn chained_class_seq_rule_sets(
3302        &self,
3303    ) -> ArrayOfNullableOffsets<'a, ChainedClassSequenceRuleSet<'a>, Offset16> {
3304        let data = self.data;
3305        let offsets = self.chained_class_seq_rule_set_offsets();
3306        ArrayOfNullableOffsets::new(offsets, data, ())
3307    }
3308
3309    pub fn format_byte_range(&self) -> Range<usize> {
3310        let start = 0;
3311        start..start + u16::RAW_BYTE_LEN
3312    }
3313
3314    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
3315        let start = self.format_byte_range().end;
3316        start..start + Offset16::RAW_BYTE_LEN
3317    }
3318
3319    pub fn backtrack_class_def_offset_byte_range(&self) -> Range<usize> {
3320        let start = self.coverage_offset_byte_range().end;
3321        start..start + Offset16::RAW_BYTE_LEN
3322    }
3323
3324    pub fn input_class_def_offset_byte_range(&self) -> Range<usize> {
3325        let start = self.backtrack_class_def_offset_byte_range().end;
3326        start..start + Offset16::RAW_BYTE_LEN
3327    }
3328
3329    pub fn lookahead_class_def_offset_byte_range(&self) -> Range<usize> {
3330        let start = self.input_class_def_offset_byte_range().end;
3331        start..start + Offset16::RAW_BYTE_LEN
3332    }
3333
3334    pub fn chained_class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
3335        let start = self.lookahead_class_def_offset_byte_range().end;
3336        start..start + u16::RAW_BYTE_LEN
3337    }
3338
3339    pub fn chained_class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
3340        let chained_class_seq_rule_set_count = self.chained_class_seq_rule_set_count();
3341        let start = self.chained_class_seq_rule_set_count_byte_range().end;
3342        start
3343            ..start
3344                + (transforms::to_usize(chained_class_seq_rule_set_count))
3345                    .saturating_mul(Offset16::RAW_BYTE_LEN)
3346    }
3347}
3348
3349#[cfg(feature = "experimental_traverse")]
3350impl<'a> SomeTable<'a> for ChainedSequenceContextFormat2<'a> {
3351    fn type_name(&self) -> &str {
3352        "ChainedSequenceContextFormat2"
3353    }
3354    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3355        match idx {
3356            0usize => Some(Field::new("format", self.format())),
3357            1usize => Some(Field::new(
3358                "coverage_offset",
3359                FieldType::offset(self.coverage_offset(), self.coverage()),
3360            )),
3361            2usize => Some(Field::new(
3362                "backtrack_class_def_offset",
3363                FieldType::offset(
3364                    self.backtrack_class_def_offset(),
3365                    self.backtrack_class_def(),
3366                ),
3367            )),
3368            3usize => Some(Field::new(
3369                "input_class_def_offset",
3370                FieldType::offset(self.input_class_def_offset(), self.input_class_def()),
3371            )),
3372            4usize => Some(Field::new(
3373                "lookahead_class_def_offset",
3374                FieldType::offset(
3375                    self.lookahead_class_def_offset(),
3376                    self.lookahead_class_def(),
3377                ),
3378            )),
3379            5usize => Some(Field::new(
3380                "chained_class_seq_rule_set_count",
3381                self.chained_class_seq_rule_set_count(),
3382            )),
3383            6usize => Some(Field::new(
3384                "chained_class_seq_rule_set_offsets",
3385                FieldType::from(self.chained_class_seq_rule_sets()),
3386            )),
3387            _ => None,
3388        }
3389    }
3390}
3391
3392#[cfg(feature = "experimental_traverse")]
3393#[allow(clippy::needless_lifetimes)]
3394impl<'a> std::fmt::Debug for ChainedSequenceContextFormat2<'a> {
3395    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3396        (self as &dyn SomeTable<'a>).fmt(f)
3397    }
3398}
3399
3400impl<'a> MinByteRange<'a> for ChainedClassSequenceRuleSet<'a> {
3401    fn min_byte_range(&self) -> Range<usize> {
3402        0..self.chained_class_seq_rule_offsets_byte_range().end
3403    }
3404    fn min_table_bytes(&self) -> &'a [u8] {
3405        let range = self.min_byte_range();
3406        self.data.as_bytes().get(range).unwrap_or_default()
3407    }
3408}
3409
3410impl<'a> FontRead<'a> for ChainedClassSequenceRuleSet<'a> {
3411    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3412        #[allow(clippy::absurd_extreme_comparisons)]
3413        if data.len() < Self::MIN_SIZE {
3414            return Err(ReadError::OutOfBounds);
3415        }
3416        Ok(Self { data })
3417    }
3418}
3419
3420/// Part of [ChainedSequenceContextFormat2]
3421#[derive(Clone)]
3422pub struct ChainedClassSequenceRuleSet<'a> {
3423    data: FontData<'a>,
3424}
3425
3426#[allow(clippy::needless_lifetimes)]
3427impl<'a> ChainedClassSequenceRuleSet<'a> {
3428    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3429    basic_table_impls!(impl_the_methods);
3430
3431    /// Number of ChainedClassSequenceRule tables
3432    pub fn chained_class_seq_rule_count(&self) -> u16 {
3433        let range = self.chained_class_seq_rule_count_byte_range();
3434        self.data.read_at(range.start).ok().unwrap()
3435    }
3436
3437    /// Array of offsets to ChainedClassSequenceRule tables, from
3438    /// beginning of ChainedClassSequenceRuleSet
3439    pub fn chained_class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
3440        let range = self.chained_class_seq_rule_offsets_byte_range();
3441        self.data.read_array(range).ok().unwrap_or_default()
3442    }
3443
3444    /// A dynamically resolving wrapper for [`chained_class_seq_rule_offsets`][Self::chained_class_seq_rule_offsets].
3445    pub fn chained_class_seq_rules(
3446        &self,
3447    ) -> ArrayOfOffsets<'a, ChainedClassSequenceRule<'a>, Offset16> {
3448        let data = self.data;
3449        let offsets = self.chained_class_seq_rule_offsets();
3450        ArrayOfOffsets::new(offsets, data, ())
3451    }
3452
3453    pub fn chained_class_seq_rule_count_byte_range(&self) -> Range<usize> {
3454        let start = 0;
3455        start..start + u16::RAW_BYTE_LEN
3456    }
3457
3458    pub fn chained_class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
3459        let chained_class_seq_rule_count = self.chained_class_seq_rule_count();
3460        let start = self.chained_class_seq_rule_count_byte_range().end;
3461        start
3462            ..start
3463                + (transforms::to_usize(chained_class_seq_rule_count))
3464                    .saturating_mul(Offset16::RAW_BYTE_LEN)
3465    }
3466}
3467
3468const _: () = assert!(FontData::default_data_long_enough(
3469    ChainedClassSequenceRuleSet::MIN_SIZE
3470));
3471
3472impl Default for ChainedClassSequenceRuleSet<'_> {
3473    fn default() -> Self {
3474        Self {
3475            data: FontData::default_table_data(),
3476        }
3477    }
3478}
3479
3480#[cfg(feature = "experimental_traverse")]
3481impl<'a> SomeTable<'a> for ChainedClassSequenceRuleSet<'a> {
3482    fn type_name(&self) -> &str {
3483        "ChainedClassSequenceRuleSet"
3484    }
3485    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3486        match idx {
3487            0usize => Some(Field::new(
3488                "chained_class_seq_rule_count",
3489                self.chained_class_seq_rule_count(),
3490            )),
3491            1usize => Some(Field::new(
3492                "chained_class_seq_rule_offsets",
3493                FieldType::from(self.chained_class_seq_rules()),
3494            )),
3495            _ => None,
3496        }
3497    }
3498}
3499
3500#[cfg(feature = "experimental_traverse")]
3501#[allow(clippy::needless_lifetimes)]
3502impl<'a> std::fmt::Debug for ChainedClassSequenceRuleSet<'a> {
3503    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3504        (self as &dyn SomeTable<'a>).fmt(f)
3505    }
3506}
3507
3508impl<'a> MinByteRange<'a> for ChainedClassSequenceRule<'a> {
3509    fn min_byte_range(&self) -> Range<usize> {
3510        0..self.seq_lookup_records_byte_range().end
3511    }
3512    fn min_table_bytes(&self) -> &'a [u8] {
3513        let range = self.min_byte_range();
3514        self.data.as_bytes().get(range).unwrap_or_default()
3515    }
3516}
3517
3518impl<'a> FontRead<'a> for ChainedClassSequenceRule<'a> {
3519    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3520        #[allow(clippy::absurd_extreme_comparisons)]
3521        if data.len() < Self::MIN_SIZE {
3522            return Err(ReadError::OutOfBounds);
3523        }
3524        Ok(Self { data })
3525    }
3526}
3527
3528/// Part of [ChainedSequenceContextFormat2]
3529#[derive(Clone)]
3530pub struct ChainedClassSequenceRule<'a> {
3531    data: FontData<'a>,
3532}
3533
3534#[allow(clippy::needless_lifetimes)]
3535impl<'a> ChainedClassSequenceRule<'a> {
3536    pub const MIN_SIZE: usize =
3537        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
3538    basic_table_impls!(impl_the_methods);
3539
3540    /// Number of glyphs in the backtrack sequence
3541    pub fn backtrack_glyph_count(&self) -> u16 {
3542        let range = self.backtrack_glyph_count_byte_range();
3543        self.data.read_at(range.start).ok().unwrap()
3544    }
3545
3546    /// Array of backtrack-sequence classes
3547    pub fn backtrack_sequence(&self) -> &'a [BigEndian<u16>] {
3548        let range = self.backtrack_sequence_byte_range();
3549        self.data.read_array(range).ok().unwrap_or_default()
3550    }
3551
3552    /// Total number of glyphs in the input sequence
3553    pub fn input_glyph_count(&self) -> u16 {
3554        let range = self.input_glyph_count_byte_range();
3555        self.data.read_at(range.start).ok().unwrap_or_default()
3556    }
3557
3558    /// Array of input sequence classes, beginning with the second
3559    /// glyph position
3560    pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
3561        let range = self.input_sequence_byte_range();
3562        self.data.read_array(range).ok().unwrap_or_default()
3563    }
3564
3565    /// Number of glyphs in the lookahead sequence
3566    pub fn lookahead_glyph_count(&self) -> u16 {
3567        let range = self.lookahead_glyph_count_byte_range();
3568        self.data.read_at(range.start).ok().unwrap_or_default()
3569    }
3570
3571    /// Array of lookahead-sequence classes
3572    pub fn lookahead_sequence(&self) -> &'a [BigEndian<u16>] {
3573        let range = self.lookahead_sequence_byte_range();
3574        self.data.read_array(range).ok().unwrap_or_default()
3575    }
3576
3577    /// Number of SequenceLookupRecords
3578    pub fn seq_lookup_count(&self) -> u16 {
3579        let range = self.seq_lookup_count_byte_range();
3580        self.data.read_at(range.start).ok().unwrap_or_default()
3581    }
3582
3583    /// Array of SequenceLookupRecords
3584    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3585        let range = self.seq_lookup_records_byte_range();
3586        self.data.read_array(range).ok().unwrap_or_default()
3587    }
3588
3589    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3590        let start = 0;
3591        start..start + u16::RAW_BYTE_LEN
3592    }
3593
3594    pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
3595        let backtrack_glyph_count = self.backtrack_glyph_count();
3596        let start = self.backtrack_glyph_count_byte_range().end;
3597        start
3598            ..start
3599                + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(u16::RAW_BYTE_LEN)
3600    }
3601
3602    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3603        let start = self.backtrack_sequence_byte_range().end;
3604        start..start + u16::RAW_BYTE_LEN
3605    }
3606
3607    pub fn input_sequence_byte_range(&self) -> Range<usize> {
3608        let input_glyph_count = self.input_glyph_count();
3609        let start = self.input_glyph_count_byte_range().end;
3610        start
3611            ..start
3612                + (transforms::subtract(input_glyph_count, 1_usize))
3613                    .saturating_mul(u16::RAW_BYTE_LEN)
3614    }
3615
3616    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3617        let start = self.input_sequence_byte_range().end;
3618        start..start + u16::RAW_BYTE_LEN
3619    }
3620
3621    pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
3622        let lookahead_glyph_count = self.lookahead_glyph_count();
3623        let start = self.lookahead_glyph_count_byte_range().end;
3624        start
3625            ..start
3626                + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(u16::RAW_BYTE_LEN)
3627    }
3628
3629    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3630        let start = self.lookahead_sequence_byte_range().end;
3631        start..start + u16::RAW_BYTE_LEN
3632    }
3633
3634    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3635        let seq_lookup_count = self.seq_lookup_count();
3636        let start = self.seq_lookup_count_byte_range().end;
3637        start
3638            ..start
3639                + (transforms::to_usize(seq_lookup_count))
3640                    .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
3641    }
3642}
3643
3644const _: () = assert!(FontData::default_data_long_enough(
3645    ChainedClassSequenceRule::MIN_SIZE
3646));
3647
3648impl Default for ChainedClassSequenceRule<'_> {
3649    fn default() -> Self {
3650        Self {
3651            data: FontData::default_table_data(),
3652        }
3653    }
3654}
3655
3656#[cfg(feature = "experimental_traverse")]
3657impl<'a> SomeTable<'a> for ChainedClassSequenceRule<'a> {
3658    fn type_name(&self) -> &str {
3659        "ChainedClassSequenceRule"
3660    }
3661    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3662        match idx {
3663            0usize => Some(Field::new(
3664                "backtrack_glyph_count",
3665                self.backtrack_glyph_count(),
3666            )),
3667            1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())),
3668            2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
3669            3usize => Some(Field::new("input_sequence", self.input_sequence())),
3670            4usize => Some(Field::new(
3671                "lookahead_glyph_count",
3672                self.lookahead_glyph_count(),
3673            )),
3674            5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())),
3675            6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
3676            7usize => Some(Field::new(
3677                "seq_lookup_records",
3678                traversal::FieldType::array_of_records(
3679                    stringify!(SequenceLookupRecord),
3680                    self.seq_lookup_records(),
3681                    self.offset_data(),
3682                ),
3683            )),
3684            _ => None,
3685        }
3686    }
3687}
3688
3689#[cfg(feature = "experimental_traverse")]
3690#[allow(clippy::needless_lifetimes)]
3691impl<'a> std::fmt::Debug for ChainedClassSequenceRule<'a> {
3692    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3693        (self as &dyn SomeTable<'a>).fmt(f)
3694    }
3695}
3696
3697impl Format<u16> for ChainedSequenceContextFormat3<'_> {
3698    const FORMAT: u16 = 3;
3699}
3700
3701impl<'a> MinByteRange<'a> for ChainedSequenceContextFormat3<'a> {
3702    fn min_byte_range(&self) -> Range<usize> {
3703        0..self.seq_lookup_records_byte_range().end
3704    }
3705    fn min_table_bytes(&self) -> &'a [u8] {
3706        let range = self.min_byte_range();
3707        self.data.as_bytes().get(range).unwrap_or_default()
3708    }
3709}
3710
3711impl<'a> FontRead<'a> for ChainedSequenceContextFormat3<'a> {
3712    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3713        #[allow(clippy::absurd_extreme_comparisons)]
3714        if data.len() < Self::MIN_SIZE {
3715            return Err(ReadError::OutOfBounds);
3716        }
3717        Ok(Self { data })
3718    }
3719}
3720
3721/// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts)
3722#[derive(Clone)]
3723pub struct ChainedSequenceContextFormat3<'a> {
3724    data: FontData<'a>,
3725}
3726
3727#[allow(clippy::needless_lifetimes)]
3728impl<'a> ChainedSequenceContextFormat3<'a> {
3729    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3730        + u16::RAW_BYTE_LEN
3731        + u16::RAW_BYTE_LEN
3732        + u16::RAW_BYTE_LEN
3733        + u16::RAW_BYTE_LEN);
3734    basic_table_impls!(impl_the_methods);
3735
3736    /// Format identifier: format = 3
3737    pub fn format(&self) -> u16 {
3738        let range = self.format_byte_range();
3739        self.data.read_at(range.start).ok().unwrap()
3740    }
3741
3742    /// Number of glyphs in the backtrack sequence
3743    pub fn backtrack_glyph_count(&self) -> u16 {
3744        let range = self.backtrack_glyph_count_byte_range();
3745        self.data.read_at(range.start).ok().unwrap()
3746    }
3747
3748    /// Array of offsets to coverage tables for the backtrack sequence
3749    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3750        let range = self.backtrack_coverage_offsets_byte_range();
3751        self.data.read_array(range).ok().unwrap_or_default()
3752    }
3753
3754    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
3755    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3756        let data = self.data;
3757        let offsets = self.backtrack_coverage_offsets();
3758        ArrayOfOffsets::new(offsets, data, ())
3759    }
3760
3761    /// Number of glyphs in the input sequence
3762    pub fn input_glyph_count(&self) -> u16 {
3763        let range = self.input_glyph_count_byte_range();
3764        self.data.read_at(range.start).ok().unwrap_or_default()
3765    }
3766
3767    /// Array of offsets to coverage tables for the input sequence
3768    pub fn input_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3769        let range = self.input_coverage_offsets_byte_range();
3770        self.data.read_array(range).ok().unwrap_or_default()
3771    }
3772
3773    /// A dynamically resolving wrapper for [`input_coverage_offsets`][Self::input_coverage_offsets].
3774    pub fn input_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3775        let data = self.data;
3776        let offsets = self.input_coverage_offsets();
3777        ArrayOfOffsets::new(offsets, data, ())
3778    }
3779
3780    /// Number of glyphs in the lookahead sequence
3781    pub fn lookahead_glyph_count(&self) -> u16 {
3782        let range = self.lookahead_glyph_count_byte_range();
3783        self.data.read_at(range.start).ok().unwrap_or_default()
3784    }
3785
3786    /// Array of offsets to coverage tables for the lookahead sequence
3787    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
3788        let range = self.lookahead_coverage_offsets_byte_range();
3789        self.data.read_array(range).ok().unwrap_or_default()
3790    }
3791
3792    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
3793    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
3794        let data = self.data;
3795        let offsets = self.lookahead_coverage_offsets();
3796        ArrayOfOffsets::new(offsets, data, ())
3797    }
3798
3799    /// Number of SequenceLookupRecords
3800    pub fn seq_lookup_count(&self) -> u16 {
3801        let range = self.seq_lookup_count_byte_range();
3802        self.data.read_at(range.start).ok().unwrap_or_default()
3803    }
3804
3805    /// Array of SequenceLookupRecords
3806    pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
3807        let range = self.seq_lookup_records_byte_range();
3808        self.data.read_array(range).ok().unwrap_or_default()
3809    }
3810
3811    pub fn format_byte_range(&self) -> Range<usize> {
3812        let start = 0;
3813        start..start + u16::RAW_BYTE_LEN
3814    }
3815
3816    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
3817        let start = self.format_byte_range().end;
3818        start..start + u16::RAW_BYTE_LEN
3819    }
3820
3821    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
3822        let backtrack_glyph_count = self.backtrack_glyph_count();
3823        let start = self.backtrack_glyph_count_byte_range().end;
3824        start
3825            ..start
3826                + (transforms::to_usize(backtrack_glyph_count))
3827                    .saturating_mul(Offset16::RAW_BYTE_LEN)
3828    }
3829
3830    pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
3831        let start = self.backtrack_coverage_offsets_byte_range().end;
3832        start..start + u16::RAW_BYTE_LEN
3833    }
3834
3835    pub fn input_coverage_offsets_byte_range(&self) -> Range<usize> {
3836        let input_glyph_count = self.input_glyph_count();
3837        let start = self.input_glyph_count_byte_range().end;
3838        start
3839            ..start
3840                + (transforms::to_usize(input_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
3841    }
3842
3843    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
3844        let start = self.input_coverage_offsets_byte_range().end;
3845        start..start + u16::RAW_BYTE_LEN
3846    }
3847
3848    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
3849        let lookahead_glyph_count = self.lookahead_glyph_count();
3850        let start = self.lookahead_glyph_count_byte_range().end;
3851        start
3852            ..start
3853                + (transforms::to_usize(lookahead_glyph_count))
3854                    .saturating_mul(Offset16::RAW_BYTE_LEN)
3855    }
3856
3857    pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
3858        let start = self.lookahead_coverage_offsets_byte_range().end;
3859        start..start + u16::RAW_BYTE_LEN
3860    }
3861
3862    pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
3863        let seq_lookup_count = self.seq_lookup_count();
3864        let start = self.seq_lookup_count_byte_range().end;
3865        start
3866            ..start
3867                + (transforms::to_usize(seq_lookup_count))
3868                    .saturating_mul(SequenceLookupRecord::RAW_BYTE_LEN)
3869    }
3870}
3871
3872#[cfg(feature = "experimental_traverse")]
3873impl<'a> SomeTable<'a> for ChainedSequenceContextFormat3<'a> {
3874    fn type_name(&self) -> &str {
3875        "ChainedSequenceContextFormat3"
3876    }
3877    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3878        match idx {
3879            0usize => Some(Field::new("format", self.format())),
3880            1usize => Some(Field::new(
3881                "backtrack_glyph_count",
3882                self.backtrack_glyph_count(),
3883            )),
3884            2usize => Some(Field::new(
3885                "backtrack_coverage_offsets",
3886                FieldType::from(self.backtrack_coverages()),
3887            )),
3888            3usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
3889            4usize => Some(Field::new(
3890                "input_coverage_offsets",
3891                FieldType::from(self.input_coverages()),
3892            )),
3893            5usize => Some(Field::new(
3894                "lookahead_glyph_count",
3895                self.lookahead_glyph_count(),
3896            )),
3897            6usize => Some(Field::new(
3898                "lookahead_coverage_offsets",
3899                FieldType::from(self.lookahead_coverages()),
3900            )),
3901            7usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
3902            8usize => Some(Field::new(
3903                "seq_lookup_records",
3904                traversal::FieldType::array_of_records(
3905                    stringify!(SequenceLookupRecord),
3906                    self.seq_lookup_records(),
3907                    self.offset_data(),
3908                ),
3909            )),
3910            _ => None,
3911        }
3912    }
3913}
3914
3915#[cfg(feature = "experimental_traverse")]
3916#[allow(clippy::needless_lifetimes)]
3917impl<'a> std::fmt::Debug for ChainedSequenceContextFormat3<'a> {
3918    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3919        (self as &dyn SomeTable<'a>).fmt(f)
3920    }
3921}
3922
3923#[derive(Clone)]
3924pub enum ChainedSequenceContext<'a> {
3925    Format1(ChainedSequenceContextFormat1<'a>),
3926    Format2(ChainedSequenceContextFormat2<'a>),
3927    Format3(ChainedSequenceContextFormat3<'a>),
3928}
3929
3930impl Default for ChainedSequenceContext<'_> {
3931    fn default() -> Self {
3932        Self::Format1(Default::default())
3933    }
3934}
3935
3936impl<'a> ChainedSequenceContext<'a> {
3937    ///Return the `FontData` used to resolve offsets for this table.
3938    pub fn offset_data(&self) -> FontData<'a> {
3939        match self {
3940            Self::Format1(item) => item.offset_data(),
3941            Self::Format2(item) => item.offset_data(),
3942            Self::Format3(item) => item.offset_data(),
3943        }
3944    }
3945
3946    /// Format identifier: format = 1
3947    pub fn format(&self) -> u16 {
3948        match self {
3949            Self::Format1(item) => item.format(),
3950            Self::Format2(item) => item.format(),
3951            Self::Format3(item) => item.format(),
3952        }
3953    }
3954}
3955
3956impl<'a> FontRead<'a> for ChainedSequenceContext<'a> {
3957    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3958        let format: u16 = data.read_at(0usize)?;
3959        match format {
3960            ChainedSequenceContextFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
3961            ChainedSequenceContextFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
3962            ChainedSequenceContextFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
3963            other => Err(ReadError::InvalidFormat(other.into())),
3964        }
3965    }
3966}
3967
3968impl<'a> MinByteRange<'a> for ChainedSequenceContext<'a> {
3969    fn min_byte_range(&self) -> Range<usize> {
3970        match self {
3971            Self::Format1(item) => item.min_byte_range(),
3972            Self::Format2(item) => item.min_byte_range(),
3973            Self::Format3(item) => item.min_byte_range(),
3974        }
3975    }
3976    fn min_table_bytes(&self) -> &'a [u8] {
3977        match self {
3978            Self::Format1(item) => item.min_table_bytes(),
3979            Self::Format2(item) => item.min_table_bytes(),
3980            Self::Format3(item) => item.min_table_bytes(),
3981        }
3982    }
3983}
3984
3985#[cfg(feature = "experimental_traverse")]
3986impl<'a> ChainedSequenceContext<'a> {
3987    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
3988        match self {
3989            Self::Format1(table) => table,
3990            Self::Format2(table) => table,
3991            Self::Format3(table) => table,
3992        }
3993    }
3994}
3995
3996#[cfg(feature = "experimental_traverse")]
3997impl std::fmt::Debug for ChainedSequenceContext<'_> {
3998    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3999        self.dyn_inner().fmt(f)
4000    }
4001}
4002
4003#[cfg(feature = "experimental_traverse")]
4004impl<'a> SomeTable<'a> for ChainedSequenceContext<'a> {
4005    fn type_name(&self) -> &str {
4006        self.dyn_inner().type_name()
4007    }
4008    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4009        self.dyn_inner().get_field(idx)
4010    }
4011}
4012
4013/// [Device](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
4014/// delta formats
4015#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
4016#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4017#[repr(u16)]
4018#[allow(clippy::manual_non_exhaustive)]
4019pub enum DeltaFormat {
4020    /// Signed 2-bit value, 8 values per uint16
4021    #[default]
4022    Local2BitDeltas = 0x0001,
4023    /// Signed 4-bit value, 4 values per uint16
4024    Local4BitDeltas = 0x0002,
4025    /// Signed 8-bit value, 2 values per uint16
4026    Local8BitDeltas = 0x0003,
4027    /// VariationIndex table, contains a delta-set index pair.
4028    VariationIndex = 0x8000,
4029    #[doc(hidden)]
4030    /// If font data is malformed we will map unknown values to this variant
4031    Unknown,
4032}
4033
4034impl DeltaFormat {
4035    /// Create from a raw scalar.
4036    ///
4037    /// This will never fail; unknown values will be mapped to the `Unknown` variant
4038    pub fn new(raw: u16) -> Self {
4039        match raw {
4040            0x0001 => Self::Local2BitDeltas,
4041            0x0002 => Self::Local4BitDeltas,
4042            0x0003 => Self::Local8BitDeltas,
4043            0x8000 => Self::VariationIndex,
4044            _ => Self::Unknown,
4045        }
4046    }
4047}
4048
4049impl font_types::Scalar for DeltaFormat {
4050    type Raw = <u16 as font_types::Scalar>::Raw;
4051    fn to_raw(self) -> Self::Raw {
4052        (self as u16).to_raw()
4053    }
4054    fn from_raw(raw: Self::Raw) -> Self {
4055        let t = <u16>::from_raw(raw);
4056        Self::new(t)
4057    }
4058}
4059
4060#[cfg(feature = "experimental_traverse")]
4061impl<'a> From<DeltaFormat> for FieldType<'a> {
4062    fn from(src: DeltaFormat) -> FieldType<'a> {
4063        (src as u16).into()
4064    }
4065}
4066
4067impl<'a> MinByteRange<'a> for Device<'a> {
4068    fn min_byte_range(&self) -> Range<usize> {
4069        0..self.delta_value_byte_range().end
4070    }
4071    fn min_table_bytes(&self) -> &'a [u8] {
4072        let range = self.min_byte_range();
4073        self.data.as_bytes().get(range).unwrap_or_default()
4074    }
4075}
4076
4077impl<'a> FontRead<'a> for Device<'a> {
4078    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4079        #[allow(clippy::absurd_extreme_comparisons)]
4080        if data.len() < Self::MIN_SIZE {
4081            return Err(ReadError::OutOfBounds);
4082        }
4083        Ok(Self { data })
4084    }
4085}
4086
4087/// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
4088#[derive(Clone)]
4089pub struct Device<'a> {
4090    data: FontData<'a>,
4091}
4092
4093#[allow(clippy::needless_lifetimes)]
4094impl<'a> Device<'a> {
4095    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + DeltaFormat::RAW_BYTE_LEN);
4096    basic_table_impls!(impl_the_methods);
4097
4098    /// Smallest size to correct, in ppem
4099    pub fn start_size(&self) -> u16 {
4100        let range = self.start_size_byte_range();
4101        self.data.read_at(range.start).ok().unwrap()
4102    }
4103
4104    /// Largest size to correct, in ppem
4105    pub fn end_size(&self) -> u16 {
4106        let range = self.end_size_byte_range();
4107        self.data.read_at(range.start).ok().unwrap()
4108    }
4109
4110    /// Format of deltaValue array data: 0x0001, 0x0002, or 0x0003
4111    pub fn delta_format(&self) -> DeltaFormat {
4112        let range = self.delta_format_byte_range();
4113        self.data.read_at(range.start).ok().unwrap()
4114    }
4115
4116    /// Array of compressed data
4117    pub fn delta_value(&self) -> &'a [BigEndian<u16>] {
4118        let range = self.delta_value_byte_range();
4119        self.data.read_array(range).ok().unwrap_or_default()
4120    }
4121
4122    pub fn start_size_byte_range(&self) -> Range<usize> {
4123        let start = 0;
4124        start..start + u16::RAW_BYTE_LEN
4125    }
4126
4127    pub fn end_size_byte_range(&self) -> Range<usize> {
4128        let start = self.start_size_byte_range().end;
4129        start..start + u16::RAW_BYTE_LEN
4130    }
4131
4132    pub fn delta_format_byte_range(&self) -> Range<usize> {
4133        let start = self.end_size_byte_range().end;
4134        start..start + DeltaFormat::RAW_BYTE_LEN
4135    }
4136
4137    pub fn delta_value_byte_range(&self) -> Range<usize> {
4138        let delta_format = self.delta_format();
4139        let start_size = self.start_size();
4140        let end_size = self.end_size();
4141        let start = self.delta_format_byte_range().end;
4142        start
4143            ..start
4144                + (DeltaFormat::value_count(delta_format, start_size, end_size))
4145                    .saturating_mul(u16::RAW_BYTE_LEN)
4146    }
4147}
4148
4149const _: () = assert!(FontData::default_data_long_enough(Device::MIN_SIZE));
4150
4151impl Default for Device<'_> {
4152    fn default() -> Self {
4153        Self {
4154            data: FontData::default_table_data(),
4155        }
4156    }
4157}
4158
4159#[cfg(feature = "experimental_traverse")]
4160impl<'a> SomeTable<'a> for Device<'a> {
4161    fn type_name(&self) -> &str {
4162        "Device"
4163    }
4164    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4165        match idx {
4166            0usize => Some(Field::new("start_size", self.start_size())),
4167            1usize => Some(Field::new("end_size", self.end_size())),
4168            2usize => Some(Field::new("delta_format", self.delta_format())),
4169            3usize => Some(Field::new("delta_value", self.delta_value())),
4170            _ => None,
4171        }
4172    }
4173}
4174
4175#[cfg(feature = "experimental_traverse")]
4176#[allow(clippy::needless_lifetimes)]
4177impl<'a> std::fmt::Debug for Device<'a> {
4178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4179        (self as &dyn SomeTable<'a>).fmt(f)
4180    }
4181}
4182
4183impl<'a> MinByteRange<'a> for VariationIndex<'a> {
4184    fn min_byte_range(&self) -> Range<usize> {
4185        0..self.delta_format_byte_range().end
4186    }
4187    fn min_table_bytes(&self) -> &'a [u8] {
4188        let range = self.min_byte_range();
4189        self.data.as_bytes().get(range).unwrap_or_default()
4190    }
4191}
4192
4193impl<'a> FontRead<'a> for VariationIndex<'a> {
4194    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4195        #[allow(clippy::absurd_extreme_comparisons)]
4196        if data.len() < Self::MIN_SIZE {
4197            return Err(ReadError::OutOfBounds);
4198        }
4199        Ok(Self { data })
4200    }
4201}
4202
4203/// Variation index table
4204#[derive(Clone)]
4205pub struct VariationIndex<'a> {
4206    data: FontData<'a>,
4207}
4208
4209#[allow(clippy::needless_lifetimes)]
4210impl<'a> VariationIndex<'a> {
4211    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + DeltaFormat::RAW_BYTE_LEN);
4212    basic_table_impls!(impl_the_methods);
4213
4214    /// A delta-set outer index — used to select an item variation
4215    /// data subtable within the item variation store.
4216    pub fn delta_set_outer_index(&self) -> u16 {
4217        let range = self.delta_set_outer_index_byte_range();
4218        self.data.read_at(range.start).ok().unwrap()
4219    }
4220
4221    /// A delta-set inner index — used to select a delta-set row
4222    /// within an item variation data subtable.
4223    pub fn delta_set_inner_index(&self) -> u16 {
4224        let range = self.delta_set_inner_index_byte_range();
4225        self.data.read_at(range.start).ok().unwrap()
4226    }
4227
4228    /// Format, = 0x8000
4229    pub fn delta_format(&self) -> DeltaFormat {
4230        let range = self.delta_format_byte_range();
4231        self.data.read_at(range.start).ok().unwrap()
4232    }
4233
4234    pub fn delta_set_outer_index_byte_range(&self) -> Range<usize> {
4235        let start = 0;
4236        start..start + u16::RAW_BYTE_LEN
4237    }
4238
4239    pub fn delta_set_inner_index_byte_range(&self) -> Range<usize> {
4240        let start = self.delta_set_outer_index_byte_range().end;
4241        start..start + u16::RAW_BYTE_LEN
4242    }
4243
4244    pub fn delta_format_byte_range(&self) -> Range<usize> {
4245        let start = self.delta_set_inner_index_byte_range().end;
4246        start..start + DeltaFormat::RAW_BYTE_LEN
4247    }
4248}
4249
4250const _: () = assert!(FontData::default_data_long_enough(VariationIndex::MIN_SIZE));
4251
4252impl Default for VariationIndex<'_> {
4253    fn default() -> Self {
4254        Self {
4255            data: FontData::default_table_data(),
4256        }
4257    }
4258}
4259
4260#[cfg(feature = "experimental_traverse")]
4261impl<'a> SomeTable<'a> for VariationIndex<'a> {
4262    fn type_name(&self) -> &str {
4263        "VariationIndex"
4264    }
4265    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4266        match idx {
4267            0usize => Some(Field::new(
4268                "delta_set_outer_index",
4269                self.delta_set_outer_index(),
4270            )),
4271            1usize => Some(Field::new(
4272                "delta_set_inner_index",
4273                self.delta_set_inner_index(),
4274            )),
4275            2usize => Some(Field::new("delta_format", self.delta_format())),
4276            _ => None,
4277        }
4278    }
4279}
4280
4281#[cfg(feature = "experimental_traverse")]
4282#[allow(clippy::needless_lifetimes)]
4283impl<'a> std::fmt::Debug for VariationIndex<'a> {
4284    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4285        (self as &dyn SomeTable<'a>).fmt(f)
4286    }
4287}
4288
4289/// Either a [Device] table (in a non-variable font) or a [VariationIndex] table (in a variable font)
4290#[derive(Clone)]
4291pub enum DeviceOrVariationIndex<'a> {
4292    Device(Device<'a>),
4293    VariationIndex(VariationIndex<'a>),
4294}
4295
4296impl Default for DeviceOrVariationIndex<'_> {
4297    fn default() -> Self {
4298        Self::Device(Default::default())
4299    }
4300}
4301
4302impl<'a> DeviceOrVariationIndex<'a> {
4303    ///Return the `FontData` used to resolve offsets for this table.
4304    pub fn offset_data(&self) -> FontData<'a> {
4305        match self {
4306            Self::Device(item) => item.offset_data(),
4307            Self::VariationIndex(item) => item.offset_data(),
4308        }
4309    }
4310}
4311
4312impl<'a> FontRead<'a> for DeviceOrVariationIndex<'a> {
4313    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4314        let format: DeltaFormat = data.read_at(4usize)?;
4315
4316        #[allow(clippy::redundant_guards)]
4317        match format {
4318            format if format != DeltaFormat::VariationIndex => {
4319                Ok(Self::Device(FontRead::read(data)?))
4320            }
4321            format if format == DeltaFormat::VariationIndex => {
4322                Ok(Self::VariationIndex(FontRead::read(data)?))
4323            }
4324            other => Err(ReadError::InvalidFormat(other.into())),
4325        }
4326    }
4327}
4328
4329impl<'a> MinByteRange<'a> for DeviceOrVariationIndex<'a> {
4330    fn min_byte_range(&self) -> Range<usize> {
4331        match self {
4332            Self::Device(item) => item.min_byte_range(),
4333            Self::VariationIndex(item) => item.min_byte_range(),
4334        }
4335    }
4336    fn min_table_bytes(&self) -> &'a [u8] {
4337        match self {
4338            Self::Device(item) => item.min_table_bytes(),
4339            Self::VariationIndex(item) => item.min_table_bytes(),
4340        }
4341    }
4342}
4343
4344#[cfg(feature = "experimental_traverse")]
4345impl<'a> DeviceOrVariationIndex<'a> {
4346    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
4347        match self {
4348            Self::Device(table) => table,
4349            Self::VariationIndex(table) => table,
4350        }
4351    }
4352}
4353
4354#[cfg(feature = "experimental_traverse")]
4355impl std::fmt::Debug for DeviceOrVariationIndex<'_> {
4356    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4357        self.dyn_inner().fmt(f)
4358    }
4359}
4360
4361#[cfg(feature = "experimental_traverse")]
4362impl<'a> SomeTable<'a> for DeviceOrVariationIndex<'a> {
4363    fn type_name(&self) -> &str {
4364        self.dyn_inner().type_name()
4365    }
4366    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4367        self.dyn_inner().get_field(idx)
4368    }
4369}
4370
4371impl<'a> MinByteRange<'a> for FeatureVariations<'a> {
4372    fn min_byte_range(&self) -> Range<usize> {
4373        0..self.feature_variation_records_byte_range().end
4374    }
4375    fn min_table_bytes(&self) -> &'a [u8] {
4376        let range = self.min_byte_range();
4377        self.data.as_bytes().get(range).unwrap_or_default()
4378    }
4379}
4380
4381impl<'a> FontRead<'a> for FeatureVariations<'a> {
4382    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4383        #[allow(clippy::absurd_extreme_comparisons)]
4384        if data.len() < Self::MIN_SIZE {
4385            return Err(ReadError::OutOfBounds);
4386        }
4387        Ok(Self { data })
4388    }
4389}
4390
4391/// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table)
4392#[derive(Clone)]
4393pub struct FeatureVariations<'a> {
4394    data: FontData<'a>,
4395}
4396
4397#[allow(clippy::needless_lifetimes)]
4398impl<'a> FeatureVariations<'a> {
4399    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
4400    basic_table_impls!(impl_the_methods);
4401
4402    pub fn version(&self) -> MajorMinor {
4403        let range = self.version_byte_range();
4404        self.data.read_at(range.start).ok().unwrap()
4405    }
4406
4407    /// Number of feature variation records.
4408    pub fn feature_variation_record_count(&self) -> u32 {
4409        let range = self.feature_variation_record_count_byte_range();
4410        self.data.read_at(range.start).ok().unwrap()
4411    }
4412
4413    /// Array of feature variation records.
4414    pub fn feature_variation_records(&self) -> &'a [FeatureVariationRecord] {
4415        let range = self.feature_variation_records_byte_range();
4416        self.data.read_array(range).ok().unwrap_or_default()
4417    }
4418
4419    pub fn version_byte_range(&self) -> Range<usize> {
4420        let start = 0;
4421        start..start + MajorMinor::RAW_BYTE_LEN
4422    }
4423
4424    pub fn feature_variation_record_count_byte_range(&self) -> Range<usize> {
4425        let start = self.version_byte_range().end;
4426        start..start + u32::RAW_BYTE_LEN
4427    }
4428
4429    pub fn feature_variation_records_byte_range(&self) -> Range<usize> {
4430        let feature_variation_record_count = self.feature_variation_record_count();
4431        let start = self.feature_variation_record_count_byte_range().end;
4432        start
4433            ..start
4434                + (transforms::to_usize(feature_variation_record_count))
4435                    .saturating_mul(FeatureVariationRecord::RAW_BYTE_LEN)
4436    }
4437}
4438
4439const _: () = assert!(FontData::default_data_long_enough(
4440    FeatureVariations::MIN_SIZE
4441));
4442
4443impl Default for FeatureVariations<'_> {
4444    fn default() -> Self {
4445        Self {
4446            data: FontData::default_table_data(),
4447        }
4448    }
4449}
4450
4451#[cfg(feature = "experimental_traverse")]
4452impl<'a> SomeTable<'a> for FeatureVariations<'a> {
4453    fn type_name(&self) -> &str {
4454        "FeatureVariations"
4455    }
4456    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4457        match idx {
4458            0usize => Some(Field::new("version", self.version())),
4459            1usize => Some(Field::new(
4460                "feature_variation_record_count",
4461                self.feature_variation_record_count(),
4462            )),
4463            2usize => Some(Field::new(
4464                "feature_variation_records",
4465                traversal::FieldType::array_of_records(
4466                    stringify!(FeatureVariationRecord),
4467                    self.feature_variation_records(),
4468                    self.offset_data(),
4469                ),
4470            )),
4471            _ => None,
4472        }
4473    }
4474}
4475
4476#[cfg(feature = "experimental_traverse")]
4477#[allow(clippy::needless_lifetimes)]
4478impl<'a> std::fmt::Debug for FeatureVariations<'a> {
4479    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4480        (self as &dyn SomeTable<'a>).fmt(f)
4481    }
4482}
4483
4484/// Part of [FeatureVariations]
4485#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
4486#[repr(C)]
4487#[repr(packed)]
4488pub struct FeatureVariationRecord {
4489    /// Offset to a condition set table, from beginning of
4490    /// FeatureVariations table.
4491    pub condition_set_offset: BigEndian<Nullable<Offset32>>,
4492    /// Offset to a feature table substitution table, from beginning of
4493    /// the FeatureVariations table.
4494    pub feature_table_substitution_offset: BigEndian<Nullable<Offset32>>,
4495}
4496
4497impl FeatureVariationRecord {
4498    /// Offset to a condition set table, from beginning of
4499    /// FeatureVariations table.
4500    pub fn condition_set_offset(&self) -> Nullable<Offset32> {
4501        self.condition_set_offset.get()
4502    }
4503
4504    /// Offset to a condition set table, from beginning of
4505    /// FeatureVariations table.
4506    ///
4507    /// The `data` argument should be retrieved from the parent table
4508    /// By calling its `offset_data` method.
4509    pub fn condition_set<'a>(
4510        &self,
4511        data: FontData<'a>,
4512    ) -> Option<Result<ConditionSet<'a>, ReadError>> {
4513        self.condition_set_offset().resolve(data)
4514    }
4515
4516    /// Offset to a feature table substitution table, from beginning of
4517    /// the FeatureVariations table.
4518    pub fn feature_table_substitution_offset(&self) -> Nullable<Offset32> {
4519        self.feature_table_substitution_offset.get()
4520    }
4521
4522    /// Offset to a feature table substitution table, from beginning of
4523    /// the FeatureVariations table.
4524    ///
4525    /// The `data` argument should be retrieved from the parent table
4526    /// By calling its `offset_data` method.
4527    pub fn feature_table_substitution<'a>(
4528        &self,
4529        data: FontData<'a>,
4530    ) -> Option<Result<FeatureTableSubstitution<'a>, ReadError>> {
4531        self.feature_table_substitution_offset().resolve(data)
4532    }
4533}
4534
4535impl FixedSize for FeatureVariationRecord {
4536    const RAW_BYTE_LEN: usize = Offset32::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
4537}
4538
4539#[cfg(feature = "experimental_traverse")]
4540impl<'a> SomeRecord<'a> for FeatureVariationRecord {
4541    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
4542        RecordResolver {
4543            name: "FeatureVariationRecord",
4544            get_field: Box::new(move |idx, _data| match idx {
4545                0usize => Some(Field::new(
4546                    "condition_set_offset",
4547                    FieldType::offset(self.condition_set_offset(), self.condition_set(_data)),
4548                )),
4549                1usize => Some(Field::new(
4550                    "feature_table_substitution_offset",
4551                    FieldType::offset(
4552                        self.feature_table_substitution_offset(),
4553                        self.feature_table_substitution(_data),
4554                    ),
4555                )),
4556                _ => None,
4557            }),
4558            data,
4559        }
4560    }
4561}
4562
4563impl<'a> MinByteRange<'a> for ConditionSet<'a> {
4564    fn min_byte_range(&self) -> Range<usize> {
4565        0..self.condition_offsets_byte_range().end
4566    }
4567    fn min_table_bytes(&self) -> &'a [u8] {
4568        let range = self.min_byte_range();
4569        self.data.as_bytes().get(range).unwrap_or_default()
4570    }
4571}
4572
4573impl<'a> FontRead<'a> for ConditionSet<'a> {
4574    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4575        #[allow(clippy::absurd_extreme_comparisons)]
4576        if data.len() < Self::MIN_SIZE {
4577            return Err(ReadError::OutOfBounds);
4578        }
4579        Ok(Self { data })
4580    }
4581}
4582
4583/// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table)
4584#[derive(Clone)]
4585pub struct ConditionSet<'a> {
4586    data: FontData<'a>,
4587}
4588
4589#[allow(clippy::needless_lifetimes)]
4590impl<'a> ConditionSet<'a> {
4591    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
4592    basic_table_impls!(impl_the_methods);
4593
4594    /// Number of conditions for this condition set.
4595    pub fn condition_count(&self) -> u16 {
4596        let range = self.condition_count_byte_range();
4597        self.data.read_at(range.start).ok().unwrap()
4598    }
4599
4600    /// Array of offsets to condition tables, from beginning of the
4601    /// ConditionSet table.
4602    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset32>] {
4603        let range = self.condition_offsets_byte_range();
4604        self.data.read_array(range).ok().unwrap_or_default()
4605    }
4606
4607    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
4608    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset32> {
4609        let data = self.data;
4610        let offsets = self.condition_offsets();
4611        ArrayOfOffsets::new(offsets, data, ())
4612    }
4613
4614    pub fn condition_count_byte_range(&self) -> Range<usize> {
4615        let start = 0;
4616        start..start + u16::RAW_BYTE_LEN
4617    }
4618
4619    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
4620        let condition_count = self.condition_count();
4621        let start = self.condition_count_byte_range().end;
4622        start
4623            ..start + (transforms::to_usize(condition_count)).saturating_mul(Offset32::RAW_BYTE_LEN)
4624    }
4625}
4626
4627const _: () = assert!(FontData::default_data_long_enough(ConditionSet::MIN_SIZE));
4628
4629impl Default for ConditionSet<'_> {
4630    fn default() -> Self {
4631        Self {
4632            data: FontData::default_table_data(),
4633        }
4634    }
4635}
4636
4637#[cfg(feature = "experimental_traverse")]
4638impl<'a> SomeTable<'a> for ConditionSet<'a> {
4639    fn type_name(&self) -> &str {
4640        "ConditionSet"
4641    }
4642    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4643        match idx {
4644            0usize => Some(Field::new("condition_count", self.condition_count())),
4645            1usize => Some(Field::new(
4646                "condition_offsets",
4647                FieldType::from(self.conditions()),
4648            )),
4649            _ => None,
4650        }
4651    }
4652}
4653
4654#[cfg(feature = "experimental_traverse")]
4655#[allow(clippy::needless_lifetimes)]
4656impl<'a> std::fmt::Debug for ConditionSet<'a> {
4657    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4658        (self as &dyn SomeTable<'a>).fmt(f)
4659    }
4660}
4661
4662/// [Condition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table)
4663///
4664/// Formats 2..5 are implementations of specification changes currently under debate at ISO for an OFF
4665/// update. For the time being the specification is <https://github.com/harfbuzz/boring-expansion-spec/blob/main/ConditionTree.md>.
4666#[derive(Clone)]
4667pub enum Condition<'a> {
4668    Format1AxisRange(ConditionFormat1<'a>),
4669    Format2VariableValue(ConditionFormat2<'a>),
4670    Format3And(ConditionFormat3<'a>),
4671    Format4Or(ConditionFormat4<'a>),
4672    Format5Negate(ConditionFormat5<'a>),
4673}
4674
4675impl Default for Condition<'_> {
4676    fn default() -> Self {
4677        Self::Format1AxisRange(Default::default())
4678    }
4679}
4680
4681impl<'a> Condition<'a> {
4682    ///Return the `FontData` used to resolve offsets for this table.
4683    pub fn offset_data(&self) -> FontData<'a> {
4684        match self {
4685            Self::Format1AxisRange(item) => item.offset_data(),
4686            Self::Format2VariableValue(item) => item.offset_data(),
4687            Self::Format3And(item) => item.offset_data(),
4688            Self::Format4Or(item) => item.offset_data(),
4689            Self::Format5Negate(item) => item.offset_data(),
4690        }
4691    }
4692
4693    /// Format, = 1
4694    pub fn format(&self) -> u16 {
4695        match self {
4696            Self::Format1AxisRange(item) => item.format(),
4697            Self::Format2VariableValue(item) => item.format(),
4698            Self::Format3And(item) => item.format(),
4699            Self::Format4Or(item) => item.format(),
4700            Self::Format5Negate(item) => item.format(),
4701        }
4702    }
4703}
4704
4705impl<'a> FontRead<'a> for Condition<'a> {
4706    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4707        let format: u16 = data.read_at(0usize)?;
4708        match format {
4709            ConditionFormat1::FORMAT => Ok(Self::Format1AxisRange(FontRead::read(data)?)),
4710            ConditionFormat2::FORMAT => Ok(Self::Format2VariableValue(FontRead::read(data)?)),
4711            ConditionFormat3::FORMAT => Ok(Self::Format3And(FontRead::read(data)?)),
4712            ConditionFormat4::FORMAT => Ok(Self::Format4Or(FontRead::read(data)?)),
4713            ConditionFormat5::FORMAT => Ok(Self::Format5Negate(FontRead::read(data)?)),
4714            other => Err(ReadError::InvalidFormat(other.into())),
4715        }
4716    }
4717}
4718
4719impl<'a> MinByteRange<'a> for Condition<'a> {
4720    fn min_byte_range(&self) -> Range<usize> {
4721        match self {
4722            Self::Format1AxisRange(item) => item.min_byte_range(),
4723            Self::Format2VariableValue(item) => item.min_byte_range(),
4724            Self::Format3And(item) => item.min_byte_range(),
4725            Self::Format4Or(item) => item.min_byte_range(),
4726            Self::Format5Negate(item) => item.min_byte_range(),
4727        }
4728    }
4729    fn min_table_bytes(&self) -> &'a [u8] {
4730        match self {
4731            Self::Format1AxisRange(item) => item.min_table_bytes(),
4732            Self::Format2VariableValue(item) => item.min_table_bytes(),
4733            Self::Format3And(item) => item.min_table_bytes(),
4734            Self::Format4Or(item) => item.min_table_bytes(),
4735            Self::Format5Negate(item) => item.min_table_bytes(),
4736        }
4737    }
4738}
4739
4740#[cfg(feature = "experimental_traverse")]
4741impl<'a> Condition<'a> {
4742    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
4743        match self {
4744            Self::Format1AxisRange(table) => table,
4745            Self::Format2VariableValue(table) => table,
4746            Self::Format3And(table) => table,
4747            Self::Format4Or(table) => table,
4748            Self::Format5Negate(table) => table,
4749        }
4750    }
4751}
4752
4753#[cfg(feature = "experimental_traverse")]
4754impl std::fmt::Debug for Condition<'_> {
4755    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4756        self.dyn_inner().fmt(f)
4757    }
4758}
4759
4760#[cfg(feature = "experimental_traverse")]
4761impl<'a> SomeTable<'a> for Condition<'a> {
4762    fn type_name(&self) -> &str {
4763        self.dyn_inner().type_name()
4764    }
4765    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4766        self.dyn_inner().get_field(idx)
4767    }
4768}
4769
4770impl Format<u16> for ConditionFormat1<'_> {
4771    const FORMAT: u16 = 1;
4772}
4773
4774impl<'a> MinByteRange<'a> for ConditionFormat1<'a> {
4775    fn min_byte_range(&self) -> Range<usize> {
4776        0..self.filter_range_max_value_byte_range().end
4777    }
4778    fn min_table_bytes(&self) -> &'a [u8] {
4779        let range = self.min_byte_range();
4780        self.data.as_bytes().get(range).unwrap_or_default()
4781    }
4782}
4783
4784impl<'a> FontRead<'a> for ConditionFormat1<'a> {
4785    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4786        #[allow(clippy::absurd_extreme_comparisons)]
4787        if data.len() < Self::MIN_SIZE {
4788            return Err(ReadError::OutOfBounds);
4789        }
4790        Ok(Self { data })
4791    }
4792}
4793
4794/// [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
4795#[derive(Clone)]
4796pub struct ConditionFormat1<'a> {
4797    data: FontData<'a>,
4798}
4799
4800#[allow(clippy::needless_lifetimes)]
4801impl<'a> ConditionFormat1<'a> {
4802    pub const MIN_SIZE: usize =
4803        (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + F2Dot14::RAW_BYTE_LEN + F2Dot14::RAW_BYTE_LEN);
4804    basic_table_impls!(impl_the_methods);
4805
4806    /// Format, = 1
4807    pub fn format(&self) -> u16 {
4808        let range = self.format_byte_range();
4809        self.data.read_at(range.start).ok().unwrap()
4810    }
4811
4812    /// Index (zero-based) for the variation axis within the 'fvar'
4813    /// table.
4814    pub fn axis_index(&self) -> u16 {
4815        let range = self.axis_index_byte_range();
4816        self.data.read_at(range.start).ok().unwrap()
4817    }
4818
4819    /// Minimum value of the font variation instances that satisfy this
4820    /// condition.
4821    pub fn filter_range_min_value(&self) -> F2Dot14 {
4822        let range = self.filter_range_min_value_byte_range();
4823        self.data.read_at(range.start).ok().unwrap()
4824    }
4825
4826    /// Maximum value of the font variation instances that satisfy this
4827    /// condition.
4828    pub fn filter_range_max_value(&self) -> F2Dot14 {
4829        let range = self.filter_range_max_value_byte_range();
4830        self.data.read_at(range.start).ok().unwrap()
4831    }
4832
4833    pub fn format_byte_range(&self) -> Range<usize> {
4834        let start = 0;
4835        start..start + u16::RAW_BYTE_LEN
4836    }
4837
4838    pub fn axis_index_byte_range(&self) -> Range<usize> {
4839        let start = self.format_byte_range().end;
4840        start..start + u16::RAW_BYTE_LEN
4841    }
4842
4843    pub fn filter_range_min_value_byte_range(&self) -> Range<usize> {
4844        let start = self.axis_index_byte_range().end;
4845        start..start + F2Dot14::RAW_BYTE_LEN
4846    }
4847
4848    pub fn filter_range_max_value_byte_range(&self) -> Range<usize> {
4849        let start = self.filter_range_min_value_byte_range().end;
4850        start..start + F2Dot14::RAW_BYTE_LEN
4851    }
4852}
4853
4854const _: () = assert!(FontData::default_data_long_enough(
4855    ConditionFormat1::MIN_SIZE
4856));
4857
4858impl Default for ConditionFormat1<'_> {
4859    fn default() -> Self {
4860        Self {
4861            data: FontData::default_format_1_u16_table_data(),
4862        }
4863    }
4864}
4865
4866#[cfg(feature = "experimental_traverse")]
4867impl<'a> SomeTable<'a> for ConditionFormat1<'a> {
4868    fn type_name(&self) -> &str {
4869        "ConditionFormat1"
4870    }
4871    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4872        match idx {
4873            0usize => Some(Field::new("format", self.format())),
4874            1usize => Some(Field::new("axis_index", self.axis_index())),
4875            2usize => Some(Field::new(
4876                "filter_range_min_value",
4877                self.filter_range_min_value(),
4878            )),
4879            3usize => Some(Field::new(
4880                "filter_range_max_value",
4881                self.filter_range_max_value(),
4882            )),
4883            _ => None,
4884        }
4885    }
4886}
4887
4888#[cfg(feature = "experimental_traverse")]
4889#[allow(clippy::needless_lifetimes)]
4890impl<'a> std::fmt::Debug for ConditionFormat1<'a> {
4891    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4892        (self as &dyn SomeTable<'a>).fmt(f)
4893    }
4894}
4895
4896impl Format<u16> for ConditionFormat2<'_> {
4897    const FORMAT: u16 = 2;
4898}
4899
4900impl<'a> MinByteRange<'a> for ConditionFormat2<'a> {
4901    fn min_byte_range(&self) -> Range<usize> {
4902        0..self.var_index_byte_range().end
4903    }
4904    fn min_table_bytes(&self) -> &'a [u8] {
4905        let range = self.min_byte_range();
4906        self.data.as_bytes().get(range).unwrap_or_default()
4907    }
4908}
4909
4910impl<'a> FontRead<'a> for ConditionFormat2<'a> {
4911    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4912        #[allow(clippy::absurd_extreme_comparisons)]
4913        if data.len() < Self::MIN_SIZE {
4914            return Err(ReadError::OutOfBounds);
4915        }
4916        Ok(Self { data })
4917    }
4918}
4919
4920/// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index
4921#[derive(Clone)]
4922pub struct ConditionFormat2<'a> {
4923    data: FontData<'a>,
4924}
4925
4926#[allow(clippy::needless_lifetimes)]
4927impl<'a> ConditionFormat2<'a> {
4928    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
4929    basic_table_impls!(impl_the_methods);
4930
4931    /// Format, = 2
4932    pub fn format(&self) -> u16 {
4933        let range = self.format_byte_range();
4934        self.data.read_at(range.start).ok().unwrap()
4935    }
4936
4937    /// Value at default instance.
4938    pub fn default_value(&self) -> i16 {
4939        let range = self.default_value_byte_range();
4940        self.data.read_at(range.start).ok().unwrap()
4941    }
4942
4943    /// Variation index to vary the value based on current designspace location.
4944    pub fn var_index(&self) -> u32 {
4945        let range = self.var_index_byte_range();
4946        self.data.read_at(range.start).ok().unwrap()
4947    }
4948
4949    pub fn format_byte_range(&self) -> Range<usize> {
4950        let start = 0;
4951        start..start + u16::RAW_BYTE_LEN
4952    }
4953
4954    pub fn default_value_byte_range(&self) -> Range<usize> {
4955        let start = self.format_byte_range().end;
4956        start..start + i16::RAW_BYTE_LEN
4957    }
4958
4959    pub fn var_index_byte_range(&self) -> Range<usize> {
4960        let start = self.default_value_byte_range().end;
4961        start..start + u32::RAW_BYTE_LEN
4962    }
4963}
4964
4965#[cfg(feature = "experimental_traverse")]
4966impl<'a> SomeTable<'a> for ConditionFormat2<'a> {
4967    fn type_name(&self) -> &str {
4968        "ConditionFormat2"
4969    }
4970    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4971        match idx {
4972            0usize => Some(Field::new("format", self.format())),
4973            1usize => Some(Field::new("default_value", self.default_value())),
4974            2usize => Some(Field::new("var_index", self.var_index())),
4975            _ => None,
4976        }
4977    }
4978}
4979
4980#[cfg(feature = "experimental_traverse")]
4981#[allow(clippy::needless_lifetimes)]
4982impl<'a> std::fmt::Debug for ConditionFormat2<'a> {
4983    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4984        (self as &dyn SomeTable<'a>).fmt(f)
4985    }
4986}
4987
4988impl Format<u16> for ConditionFormat3<'_> {
4989    const FORMAT: u16 = 3;
4990}
4991
4992impl<'a> MinByteRange<'a> for ConditionFormat3<'a> {
4993    fn min_byte_range(&self) -> Range<usize> {
4994        0..self.condition_offsets_byte_range().end
4995    }
4996    fn min_table_bytes(&self) -> &'a [u8] {
4997        let range = self.min_byte_range();
4998        self.data.as_bytes().get(range).unwrap_or_default()
4999    }
5000}
5001
5002impl<'a> FontRead<'a> for ConditionFormat3<'a> {
5003    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5004        #[allow(clippy::absurd_extreme_comparisons)]
5005        if data.len() < Self::MIN_SIZE {
5006            return Err(ReadError::OutOfBounds);
5007        }
5008        Ok(Self { data })
5009    }
5010}
5011
5012/// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND
5013#[derive(Clone)]
5014pub struct ConditionFormat3<'a> {
5015    data: FontData<'a>,
5016}
5017
5018#[allow(clippy::needless_lifetimes)]
5019impl<'a> ConditionFormat3<'a> {
5020    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
5021    basic_table_impls!(impl_the_methods);
5022
5023    /// Format, = 3
5024    pub fn format(&self) -> u16 {
5025        let range = self.format_byte_range();
5026        self.data.read_at(range.start).ok().unwrap()
5027    }
5028
5029    /// Number of conditions.
5030    pub fn condition_count(&self) -> u8 {
5031        let range = self.condition_count_byte_range();
5032        self.data.read_at(range.start).ok().unwrap()
5033    }
5034
5035    /// Array of condition tables for this conjunction (AND) expression.
5036    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
5037        let range = self.condition_offsets_byte_range();
5038        self.data.read_array(range).ok().unwrap_or_default()
5039    }
5040
5041    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
5042    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
5043        let data = self.data;
5044        let offsets = self.condition_offsets();
5045        ArrayOfOffsets::new(offsets, data, ())
5046    }
5047
5048    pub fn format_byte_range(&self) -> Range<usize> {
5049        let start = 0;
5050        start..start + u16::RAW_BYTE_LEN
5051    }
5052
5053    pub fn condition_count_byte_range(&self) -> Range<usize> {
5054        let start = self.format_byte_range().end;
5055        start..start + u8::RAW_BYTE_LEN
5056    }
5057
5058    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
5059        let condition_count = self.condition_count();
5060        let start = self.condition_count_byte_range().end;
5061        start
5062            ..start + (transforms::to_usize(condition_count)).saturating_mul(Offset24::RAW_BYTE_LEN)
5063    }
5064}
5065
5066#[cfg(feature = "experimental_traverse")]
5067impl<'a> SomeTable<'a> for ConditionFormat3<'a> {
5068    fn type_name(&self) -> &str {
5069        "ConditionFormat3"
5070    }
5071    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5072        match idx {
5073            0usize => Some(Field::new("format", self.format())),
5074            1usize => Some(Field::new("condition_count", self.condition_count())),
5075            2usize => Some(Field::new(
5076                "condition_offsets",
5077                FieldType::from(self.conditions()),
5078            )),
5079            _ => None,
5080        }
5081    }
5082}
5083
5084#[cfg(feature = "experimental_traverse")]
5085#[allow(clippy::needless_lifetimes)]
5086impl<'a> std::fmt::Debug for ConditionFormat3<'a> {
5087    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5088        (self as &dyn SomeTable<'a>).fmt(f)
5089    }
5090}
5091
5092impl Format<u16> for ConditionFormat4<'_> {
5093    const FORMAT: u16 = 4;
5094}
5095
5096impl<'a> MinByteRange<'a> for ConditionFormat4<'a> {
5097    fn min_byte_range(&self) -> Range<usize> {
5098        0..self.condition_offsets_byte_range().end
5099    }
5100    fn min_table_bytes(&self) -> &'a [u8] {
5101        let range = self.min_byte_range();
5102        self.data.as_bytes().get(range).unwrap_or_default()
5103    }
5104}
5105
5106impl<'a> FontRead<'a> for ConditionFormat4<'a> {
5107    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5108        #[allow(clippy::absurd_extreme_comparisons)]
5109        if data.len() < Self::MIN_SIZE {
5110            return Err(ReadError::OutOfBounds);
5111        }
5112        Ok(Self { data })
5113    }
5114}
5115
5116/// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR
5117#[derive(Clone)]
5118pub struct ConditionFormat4<'a> {
5119    data: FontData<'a>,
5120}
5121
5122#[allow(clippy::needless_lifetimes)]
5123impl<'a> ConditionFormat4<'a> {
5124    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u8::RAW_BYTE_LEN);
5125    basic_table_impls!(impl_the_methods);
5126
5127    /// Format, = 4
5128    pub fn format(&self) -> u16 {
5129        let range = self.format_byte_range();
5130        self.data.read_at(range.start).ok().unwrap()
5131    }
5132
5133    /// Number of conditions.
5134    pub fn condition_count(&self) -> u8 {
5135        let range = self.condition_count_byte_range();
5136        self.data.read_at(range.start).ok().unwrap()
5137    }
5138
5139    /// Array of condition tables for this disjunction (OR) expression.
5140    pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
5141        let range = self.condition_offsets_byte_range();
5142        self.data.read_array(range).ok().unwrap_or_default()
5143    }
5144
5145    /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
5146    pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
5147        let data = self.data;
5148        let offsets = self.condition_offsets();
5149        ArrayOfOffsets::new(offsets, data, ())
5150    }
5151
5152    pub fn format_byte_range(&self) -> Range<usize> {
5153        let start = 0;
5154        start..start + u16::RAW_BYTE_LEN
5155    }
5156
5157    pub fn condition_count_byte_range(&self) -> Range<usize> {
5158        let start = self.format_byte_range().end;
5159        start..start + u8::RAW_BYTE_LEN
5160    }
5161
5162    pub fn condition_offsets_byte_range(&self) -> Range<usize> {
5163        let condition_count = self.condition_count();
5164        let start = self.condition_count_byte_range().end;
5165        start
5166            ..start + (transforms::to_usize(condition_count)).saturating_mul(Offset24::RAW_BYTE_LEN)
5167    }
5168}
5169
5170#[cfg(feature = "experimental_traverse")]
5171impl<'a> SomeTable<'a> for ConditionFormat4<'a> {
5172    fn type_name(&self) -> &str {
5173        "ConditionFormat4"
5174    }
5175    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5176        match idx {
5177            0usize => Some(Field::new("format", self.format())),
5178            1usize => Some(Field::new("condition_count", self.condition_count())),
5179            2usize => Some(Field::new(
5180                "condition_offsets",
5181                FieldType::from(self.conditions()),
5182            )),
5183            _ => None,
5184        }
5185    }
5186}
5187
5188#[cfg(feature = "experimental_traverse")]
5189#[allow(clippy::needless_lifetimes)]
5190impl<'a> std::fmt::Debug for ConditionFormat4<'a> {
5191    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5192        (self as &dyn SomeTable<'a>).fmt(f)
5193    }
5194}
5195
5196impl Format<u16> for ConditionFormat5<'_> {
5197    const FORMAT: u16 = 5;
5198}
5199
5200impl<'a> MinByteRange<'a> for ConditionFormat5<'a> {
5201    fn min_byte_range(&self) -> Range<usize> {
5202        0..self.condition_offset_byte_range().end
5203    }
5204    fn min_table_bytes(&self) -> &'a [u8] {
5205        let range = self.min_byte_range();
5206        self.data.as_bytes().get(range).unwrap_or_default()
5207    }
5208}
5209
5210impl<'a> FontRead<'a> for ConditionFormat5<'a> {
5211    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5212        #[allow(clippy::absurd_extreme_comparisons)]
5213        if data.len() < Self::MIN_SIZE {
5214            return Err(ReadError::OutOfBounds);
5215        }
5216        Ok(Self { data })
5217    }
5218}
5219
5220/// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT
5221#[derive(Clone)]
5222pub struct ConditionFormat5<'a> {
5223    data: FontData<'a>,
5224}
5225
5226#[allow(clippy::needless_lifetimes)]
5227impl<'a> ConditionFormat5<'a> {
5228    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset24::RAW_BYTE_LEN);
5229    basic_table_impls!(impl_the_methods);
5230
5231    /// Format, = 5
5232    pub fn format(&self) -> u16 {
5233        let range = self.format_byte_range();
5234        self.data.read_at(range.start).ok().unwrap()
5235    }
5236
5237    /// Condition to negate.
5238    pub fn condition_offset(&self) -> Offset24 {
5239        let range = self.condition_offset_byte_range();
5240        self.data.read_at(range.start).ok().unwrap()
5241    }
5242
5243    /// Attempt to resolve [`condition_offset`][Self::condition_offset].
5244    pub fn condition(&self) -> Result<Condition<'a>, ReadError> {
5245        let data = self.data;
5246        self.condition_offset().resolve(data)
5247    }
5248
5249    pub fn format_byte_range(&self) -> Range<usize> {
5250        let start = 0;
5251        start..start + u16::RAW_BYTE_LEN
5252    }
5253
5254    pub fn condition_offset_byte_range(&self) -> Range<usize> {
5255        let start = self.format_byte_range().end;
5256        start..start + Offset24::RAW_BYTE_LEN
5257    }
5258}
5259
5260#[cfg(feature = "experimental_traverse")]
5261impl<'a> SomeTable<'a> for ConditionFormat5<'a> {
5262    fn type_name(&self) -> &str {
5263        "ConditionFormat5"
5264    }
5265    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5266        match idx {
5267            0usize => Some(Field::new("format", self.format())),
5268            1usize => Some(Field::new(
5269                "condition_offset",
5270                FieldType::offset(self.condition_offset(), self.condition()),
5271            )),
5272            _ => None,
5273        }
5274    }
5275}
5276
5277#[cfg(feature = "experimental_traverse")]
5278#[allow(clippy::needless_lifetimes)]
5279impl<'a> std::fmt::Debug for ConditionFormat5<'a> {
5280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5281        (self as &dyn SomeTable<'a>).fmt(f)
5282    }
5283}
5284
5285impl<'a> MinByteRange<'a> for FeatureTableSubstitution<'a> {
5286    fn min_byte_range(&self) -> Range<usize> {
5287        0..self.substitutions_byte_range().end
5288    }
5289    fn min_table_bytes(&self) -> &'a [u8] {
5290        let range = self.min_byte_range();
5291        self.data.as_bytes().get(range).unwrap_or_default()
5292    }
5293}
5294
5295impl<'a> FontRead<'a> for FeatureTableSubstitution<'a> {
5296    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5297        #[allow(clippy::absurd_extreme_comparisons)]
5298        if data.len() < Self::MIN_SIZE {
5299            return Err(ReadError::OutOfBounds);
5300        }
5301        Ok(Self { data })
5302    }
5303}
5304
5305/// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table)
5306#[derive(Clone)]
5307pub struct FeatureTableSubstitution<'a> {
5308    data: FontData<'a>,
5309}
5310
5311#[allow(clippy::needless_lifetimes)]
5312impl<'a> FeatureTableSubstitution<'a> {
5313    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
5314    basic_table_impls!(impl_the_methods);
5315
5316    /// Major & minor version of the table: (1, 0)
5317    pub fn version(&self) -> MajorMinor {
5318        let range = self.version_byte_range();
5319        self.data.read_at(range.start).ok().unwrap()
5320    }
5321
5322    /// Number of feature table substitution records.
5323    pub fn substitution_count(&self) -> u16 {
5324        let range = self.substitution_count_byte_range();
5325        self.data.read_at(range.start).ok().unwrap()
5326    }
5327
5328    /// Array of feature table substitution records.
5329    pub fn substitutions(&self) -> &'a [FeatureTableSubstitutionRecord] {
5330        let range = self.substitutions_byte_range();
5331        self.data.read_array(range).ok().unwrap_or_default()
5332    }
5333
5334    pub fn version_byte_range(&self) -> Range<usize> {
5335        let start = 0;
5336        start..start + MajorMinor::RAW_BYTE_LEN
5337    }
5338
5339    pub fn substitution_count_byte_range(&self) -> Range<usize> {
5340        let start = self.version_byte_range().end;
5341        start..start + u16::RAW_BYTE_LEN
5342    }
5343
5344    pub fn substitutions_byte_range(&self) -> Range<usize> {
5345        let substitution_count = self.substitution_count();
5346        let start = self.substitution_count_byte_range().end;
5347        start
5348            ..start
5349                + (transforms::to_usize(substitution_count))
5350                    .saturating_mul(FeatureTableSubstitutionRecord::RAW_BYTE_LEN)
5351    }
5352}
5353
5354const _: () = assert!(FontData::default_data_long_enough(
5355    FeatureTableSubstitution::MIN_SIZE
5356));
5357
5358impl Default for FeatureTableSubstitution<'_> {
5359    fn default() -> Self {
5360        Self {
5361            data: FontData::default_table_data(),
5362        }
5363    }
5364}
5365
5366#[cfg(feature = "experimental_traverse")]
5367impl<'a> SomeTable<'a> for FeatureTableSubstitution<'a> {
5368    fn type_name(&self) -> &str {
5369        "FeatureTableSubstitution"
5370    }
5371    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5372        match idx {
5373            0usize => Some(Field::new("version", self.version())),
5374            1usize => Some(Field::new("substitution_count", self.substitution_count())),
5375            2usize => Some(Field::new(
5376                "substitutions",
5377                traversal::FieldType::array_of_records(
5378                    stringify!(FeatureTableSubstitutionRecord),
5379                    self.substitutions(),
5380                    self.offset_data(),
5381                ),
5382            )),
5383            _ => None,
5384        }
5385    }
5386}
5387
5388#[cfg(feature = "experimental_traverse")]
5389#[allow(clippy::needless_lifetimes)]
5390impl<'a> std::fmt::Debug for FeatureTableSubstitution<'a> {
5391    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5392        (self as &dyn SomeTable<'a>).fmt(f)
5393    }
5394}
5395
5396/// Used in [FeatureTableSubstitution]
5397#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
5398#[repr(C)]
5399#[repr(packed)]
5400pub struct FeatureTableSubstitutionRecord {
5401    /// The feature table index to match.
5402    pub feature_index: BigEndian<u16>,
5403    /// Offset to an alternate feature table, from start of the
5404    /// FeatureTableSubstitution table.
5405    pub alternate_feature_offset: BigEndian<Offset32>,
5406}
5407
5408impl FeatureTableSubstitutionRecord {
5409    /// The feature table index to match.
5410    pub fn feature_index(&self) -> u16 {
5411        self.feature_index.get()
5412    }
5413
5414    /// Offset to an alternate feature table, from start of the
5415    /// FeatureTableSubstitution table.
5416    pub fn alternate_feature_offset(&self) -> Offset32 {
5417        self.alternate_feature_offset.get()
5418    }
5419}
5420
5421impl FixedSize for FeatureTableSubstitutionRecord {
5422    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
5423}
5424
5425#[cfg(feature = "experimental_traverse")]
5426impl<'a> SomeRecord<'a> for FeatureTableSubstitutionRecord {
5427    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
5428        RecordResolver {
5429            name: "FeatureTableSubstitutionRecord",
5430            get_field: Box::new(move |idx, _data| match idx {
5431                0usize => Some(Field::new("feature_index", self.feature_index())),
5432                1usize => Some(Field::new(
5433                    "alternate_feature_offset",
5434                    FieldType::offset(
5435                        self.alternate_feature_offset(),
5436                        self.alternate_feature(_data),
5437                    ),
5438                )),
5439                _ => None,
5440            }),
5441            data,
5442        }
5443    }
5444}
5445
5446impl<'a> MinByteRange<'a> for SizeParams<'a> {
5447    fn min_byte_range(&self) -> Range<usize> {
5448        0..self.range_end_byte_range().end
5449    }
5450    fn min_table_bytes(&self) -> &'a [u8] {
5451        let range = self.min_byte_range();
5452        self.data.as_bytes().get(range).unwrap_or_default()
5453    }
5454}
5455
5456impl<'a> FontRead<'a> for SizeParams<'a> {
5457    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5458        #[allow(clippy::absurd_extreme_comparisons)]
5459        if data.len() < Self::MIN_SIZE {
5460            return Err(ReadError::OutOfBounds);
5461        }
5462        Ok(Self { data })
5463    }
5464}
5465
5466#[derive(Clone)]
5467pub struct SizeParams<'a> {
5468    data: FontData<'a>,
5469}
5470
5471#[allow(clippy::needless_lifetimes)]
5472impl<'a> SizeParams<'a> {
5473    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5474        + u16::RAW_BYTE_LEN
5475        + u16::RAW_BYTE_LEN
5476        + u16::RAW_BYTE_LEN
5477        + u16::RAW_BYTE_LEN);
5478    basic_table_impls!(impl_the_methods);
5479
5480    /// The first value represents the design size in 720/inch units (decipoints).
5481    ///
5482    /// The design size entry must be non-zero. When there is a design size but
5483    /// no recommended size range, the rest of the array will consist of zeros.
5484    pub fn design_size(&self) -> u16 {
5485        let range = self.design_size_byte_range();
5486        self.data.read_at(range.start).ok().unwrap()
5487    }
5488
5489    /// The second value has no independent meaning, but serves as an identifier that associates fonts in a subfamily.
5490    ///
5491    /// All fonts which share a Typographic or Font Family name and which differ
5492    /// only by size range shall have the same subfamily value, and no fonts
5493    /// which differ in weight or style shall have the same subfamily value.
5494    /// If this value is zero, the remaining fields in the array will be ignored.
5495    pub fn identifier(&self) -> u16 {
5496        let range = self.identifier_byte_range();
5497        self.data.read_at(range.start).ok().unwrap()
5498    }
5499
5500    /// The third value enables applications to use a single name for the subfamily identified by the second value.
5501    ///
5502    /// If the preceding value is non-zero, this value must be set in the range
5503    /// 256 – 32767 (inclusive). It records the value of a field in the 'name'
5504    /// table, which must contain English-language strings encoded in Windows
5505    /// Unicode and Macintosh Roman, and may contain additional strings localized
5506    /// to other scripts and languages. Each of these strings is the name
5507    /// an application should use, in combination with the family name, to
5508    /// represent the subfamily in a menu. Applications will choose the
5509    /// appropriate version based on their selection criteria.
5510    pub fn name_entry(&self) -> u16 {
5511        let range = self.name_entry_byte_range();
5512        self.data.read_at(range.start).ok().unwrap()
5513    }
5514
5515    /// The fourth and fifth values represent the small end of the recommended
5516    /// usage range (exclusive) and the large end of the recommended usage range
5517    /// (inclusive), stored in 720/inch units (decipoints).
5518    ///
5519    /// Ranges must not overlap, and should generally be contiguous.
5520    pub fn range_start(&self) -> u16 {
5521        let range = self.range_start_byte_range();
5522        self.data.read_at(range.start).ok().unwrap()
5523    }
5524
5525    pub fn range_end(&self) -> u16 {
5526        let range = self.range_end_byte_range();
5527        self.data.read_at(range.start).ok().unwrap()
5528    }
5529
5530    pub fn design_size_byte_range(&self) -> Range<usize> {
5531        let start = 0;
5532        start..start + u16::RAW_BYTE_LEN
5533    }
5534
5535    pub fn identifier_byte_range(&self) -> Range<usize> {
5536        let start = self.design_size_byte_range().end;
5537        start..start + u16::RAW_BYTE_LEN
5538    }
5539
5540    pub fn name_entry_byte_range(&self) -> Range<usize> {
5541        let start = self.identifier_byte_range().end;
5542        start..start + u16::RAW_BYTE_LEN
5543    }
5544
5545    pub fn range_start_byte_range(&self) -> Range<usize> {
5546        let start = self.name_entry_byte_range().end;
5547        start..start + u16::RAW_BYTE_LEN
5548    }
5549
5550    pub fn range_end_byte_range(&self) -> Range<usize> {
5551        let start = self.range_start_byte_range().end;
5552        start..start + u16::RAW_BYTE_LEN
5553    }
5554}
5555
5556const _: () = assert!(FontData::default_data_long_enough(SizeParams::MIN_SIZE));
5557
5558impl Default for SizeParams<'_> {
5559    fn default() -> Self {
5560        Self {
5561            data: FontData::default_table_data(),
5562        }
5563    }
5564}
5565
5566#[cfg(feature = "experimental_traverse")]
5567impl<'a> SomeTable<'a> for SizeParams<'a> {
5568    fn type_name(&self) -> &str {
5569        "SizeParams"
5570    }
5571    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5572        match idx {
5573            0usize => Some(Field::new("design_size", self.design_size())),
5574            1usize => Some(Field::new("identifier", self.identifier())),
5575            2usize => Some(Field::new("name_entry", self.name_entry())),
5576            3usize => Some(Field::new("range_start", self.range_start())),
5577            4usize => Some(Field::new("range_end", self.range_end())),
5578            _ => None,
5579        }
5580    }
5581}
5582
5583#[cfg(feature = "experimental_traverse")]
5584#[allow(clippy::needless_lifetimes)]
5585impl<'a> std::fmt::Debug for SizeParams<'a> {
5586    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5587        (self as &dyn SomeTable<'a>).fmt(f)
5588    }
5589}
5590
5591impl<'a> MinByteRange<'a> for StylisticSetParams<'a> {
5592    fn min_byte_range(&self) -> Range<usize> {
5593        0..self.ui_name_id_byte_range().end
5594    }
5595    fn min_table_bytes(&self) -> &'a [u8] {
5596        let range = self.min_byte_range();
5597        self.data.as_bytes().get(range).unwrap_or_default()
5598    }
5599}
5600
5601impl<'a> FontRead<'a> for StylisticSetParams<'a> {
5602    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5603        #[allow(clippy::absurd_extreme_comparisons)]
5604        if data.len() < Self::MIN_SIZE {
5605            return Err(ReadError::OutOfBounds);
5606        }
5607        Ok(Self { data })
5608    }
5609}
5610
5611#[derive(Clone)]
5612pub struct StylisticSetParams<'a> {
5613    data: FontData<'a>,
5614}
5615
5616#[allow(clippy::needless_lifetimes)]
5617impl<'a> StylisticSetParams<'a> {
5618    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + NameId::RAW_BYTE_LEN);
5619    basic_table_impls!(impl_the_methods);
5620
5621    pub fn version(&self) -> u16 {
5622        let range = self.version_byte_range();
5623        self.data.read_at(range.start).ok().unwrap()
5624    }
5625
5626    /// The 'name' table name ID that specifies a string (or strings, for
5627    /// multiple languages) for a user-interface label for this feature.
5628    ///
5629    /// The value of uiLabelNameId is expected to be in the font-specific name
5630    /// ID range (256-32767), though that is not a requirement in this Feature
5631    /// Parameters specification. The user-interface label for the feature can
5632    /// be provided in multiple languages. An English string should be included
5633    /// as a fallback. The string should be kept to a minimal length to fit
5634    /// comfortably with different application interfaces.
5635    pub fn ui_name_id(&self) -> NameId {
5636        let range = self.ui_name_id_byte_range();
5637        self.data.read_at(range.start).ok().unwrap()
5638    }
5639
5640    pub fn version_byte_range(&self) -> Range<usize> {
5641        let start = 0;
5642        start..start + u16::RAW_BYTE_LEN
5643    }
5644
5645    pub fn ui_name_id_byte_range(&self) -> Range<usize> {
5646        let start = self.version_byte_range().end;
5647        start..start + NameId::RAW_BYTE_LEN
5648    }
5649}
5650
5651const _: () = assert!(FontData::default_data_long_enough(
5652    StylisticSetParams::MIN_SIZE
5653));
5654
5655impl Default for StylisticSetParams<'_> {
5656    fn default() -> Self {
5657        Self {
5658            data: FontData::default_table_data(),
5659        }
5660    }
5661}
5662
5663#[cfg(feature = "experimental_traverse")]
5664impl<'a> SomeTable<'a> for StylisticSetParams<'a> {
5665    fn type_name(&self) -> &str {
5666        "StylisticSetParams"
5667    }
5668    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5669        match idx {
5670            0usize => Some(Field::new("version", self.version())),
5671            1usize => Some(Field::new("ui_name_id", self.ui_name_id())),
5672            _ => None,
5673        }
5674    }
5675}
5676
5677#[cfg(feature = "experimental_traverse")]
5678#[allow(clippy::needless_lifetimes)]
5679impl<'a> std::fmt::Debug for StylisticSetParams<'a> {
5680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5681        (self as &dyn SomeTable<'a>).fmt(f)
5682    }
5683}
5684
5685impl Format<u16> for CharacterVariantParams<'_> {
5686    const FORMAT: u16 = 0;
5687}
5688
5689impl<'a> MinByteRange<'a> for CharacterVariantParams<'a> {
5690    fn min_byte_range(&self) -> Range<usize> {
5691        0..self.character_byte_range().end
5692    }
5693    fn min_table_bytes(&self) -> &'a [u8] {
5694        let range = self.min_byte_range();
5695        self.data.as_bytes().get(range).unwrap_or_default()
5696    }
5697}
5698
5699impl<'a> FontRead<'a> for CharacterVariantParams<'a> {
5700    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
5701        #[allow(clippy::absurd_extreme_comparisons)]
5702        if data.len() < Self::MIN_SIZE {
5703            return Err(ReadError::OutOfBounds);
5704        }
5705        Ok(Self { data })
5706    }
5707}
5708
5709/// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99)
5710#[derive(Clone)]
5711pub struct CharacterVariantParams<'a> {
5712    data: FontData<'a>,
5713}
5714
5715#[allow(clippy::needless_lifetimes)]
5716impl<'a> CharacterVariantParams<'a> {
5717    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5718        + NameId::RAW_BYTE_LEN
5719        + NameId::RAW_BYTE_LEN
5720        + NameId::RAW_BYTE_LEN
5721        + u16::RAW_BYTE_LEN
5722        + NameId::RAW_BYTE_LEN
5723        + u16::RAW_BYTE_LEN);
5724    basic_table_impls!(impl_the_methods);
5725
5726    /// Format number is set to 0.
5727    pub fn format(&self) -> u16 {
5728        let range = self.format_byte_range();
5729        self.data.read_at(range.start).ok().unwrap()
5730    }
5731
5732    /// The 'name' table name ID that specifies a string (or strings,
5733    /// for multiple languages) for a user-interface label for this
5734    /// feature. (May be NULL.)
5735    pub fn feat_ui_label_name_id(&self) -> NameId {
5736        let range = self.feat_ui_label_name_id_byte_range();
5737        self.data.read_at(range.start).ok().unwrap()
5738    }
5739
5740    /// The 'name' table name ID that specifies a string (or strings,
5741    /// for multiple languages) that an application can use for tooltip
5742    /// text for this feature. (May be NULL.)
5743    pub fn feat_ui_tooltip_text_name_id(&self) -> NameId {
5744        let range = self.feat_ui_tooltip_text_name_id_byte_range();
5745        self.data.read_at(range.start).ok().unwrap()
5746    }
5747
5748    /// The 'name' table name ID that specifies sample text that
5749    /// illustrates the effect of this feature. (May be NULL.)
5750    pub fn sample_text_name_id(&self) -> NameId {
5751        let range = self.sample_text_name_id_byte_range();
5752        self.data.read_at(range.start).ok().unwrap()
5753    }
5754
5755    /// Number of named parameters. (May be zero.)
5756    pub fn num_named_parameters(&self) -> u16 {
5757        let range = self.num_named_parameters_byte_range();
5758        self.data.read_at(range.start).ok().unwrap()
5759    }
5760
5761    /// The first 'name' table name ID used to specify strings for
5762    /// user-interface labels for the feature parameters. (Must be zero
5763    /// if numParameters is zero.)
5764    pub fn first_param_ui_label_name_id(&self) -> NameId {
5765        let range = self.first_param_ui_label_name_id_byte_range();
5766        self.data.read_at(range.start).ok().unwrap()
5767    }
5768
5769    /// The count of characters for which this feature provides glyph
5770    /// variants. (May be zero.)
5771    pub fn char_count(&self) -> u16 {
5772        let range = self.char_count_byte_range();
5773        self.data.read_at(range.start).ok().unwrap()
5774    }
5775
5776    /// The Unicode Scalar Value of the characters for which this
5777    /// feature provides glyph variants.
5778    pub fn character(&self) -> &'a [BigEndian<Uint24>] {
5779        let range = self.character_byte_range();
5780        self.data.read_array(range).ok().unwrap_or_default()
5781    }
5782
5783    pub fn format_byte_range(&self) -> Range<usize> {
5784        let start = 0;
5785        start..start + u16::RAW_BYTE_LEN
5786    }
5787
5788    pub fn feat_ui_label_name_id_byte_range(&self) -> Range<usize> {
5789        let start = self.format_byte_range().end;
5790        start..start + NameId::RAW_BYTE_LEN
5791    }
5792
5793    pub fn feat_ui_tooltip_text_name_id_byte_range(&self) -> Range<usize> {
5794        let start = self.feat_ui_label_name_id_byte_range().end;
5795        start..start + NameId::RAW_BYTE_LEN
5796    }
5797
5798    pub fn sample_text_name_id_byte_range(&self) -> Range<usize> {
5799        let start = self.feat_ui_tooltip_text_name_id_byte_range().end;
5800        start..start + NameId::RAW_BYTE_LEN
5801    }
5802
5803    pub fn num_named_parameters_byte_range(&self) -> Range<usize> {
5804        let start = self.sample_text_name_id_byte_range().end;
5805        start..start + u16::RAW_BYTE_LEN
5806    }
5807
5808    pub fn first_param_ui_label_name_id_byte_range(&self) -> Range<usize> {
5809        let start = self.num_named_parameters_byte_range().end;
5810        start..start + NameId::RAW_BYTE_LEN
5811    }
5812
5813    pub fn char_count_byte_range(&self) -> Range<usize> {
5814        let start = self.first_param_ui_label_name_id_byte_range().end;
5815        start..start + u16::RAW_BYTE_LEN
5816    }
5817
5818    pub fn character_byte_range(&self) -> Range<usize> {
5819        let char_count = self.char_count();
5820        let start = self.char_count_byte_range().end;
5821        start..start + (transforms::to_usize(char_count)).saturating_mul(Uint24::RAW_BYTE_LEN)
5822    }
5823}
5824
5825const _: () = assert!(FontData::default_data_long_enough(
5826    CharacterVariantParams::MIN_SIZE
5827));
5828
5829impl Default for CharacterVariantParams<'_> {
5830    fn default() -> Self {
5831        Self {
5832            data: FontData::default_table_data(),
5833        }
5834    }
5835}
5836
5837#[cfg(feature = "experimental_traverse")]
5838impl<'a> SomeTable<'a> for CharacterVariantParams<'a> {
5839    fn type_name(&self) -> &str {
5840        "CharacterVariantParams"
5841    }
5842    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
5843        match idx {
5844            0usize => Some(Field::new("format", self.format())),
5845            1usize => Some(Field::new(
5846                "feat_ui_label_name_id",
5847                self.feat_ui_label_name_id(),
5848            )),
5849            2usize => Some(Field::new(
5850                "feat_ui_tooltip_text_name_id",
5851                self.feat_ui_tooltip_text_name_id(),
5852            )),
5853            3usize => Some(Field::new(
5854                "sample_text_name_id",
5855                self.sample_text_name_id(),
5856            )),
5857            4usize => Some(Field::new(
5858                "num_named_parameters",
5859                self.num_named_parameters(),
5860            )),
5861            5usize => Some(Field::new(
5862                "first_param_ui_label_name_id",
5863                self.first_param_ui_label_name_id(),
5864            )),
5865            6usize => Some(Field::new("char_count", self.char_count())),
5866            7usize => Some(Field::new("character", self.character())),
5867            _ => None,
5868        }
5869    }
5870}
5871
5872#[cfg(feature = "experimental_traverse")]
5873#[allow(clippy::needless_lifetimes)]
5874impl<'a> std::fmt::Debug for CharacterVariantParams<'a> {
5875    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5876        (self as &dyn SomeTable<'a>).fmt(f)
5877    }
5878}