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