Skip to main content

read_fonts/generated/
generated_gsub.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 Gsub<'a> {
9    fn min_byte_range(&self) -> Range<usize> {
10        0..self.lookup_list_offset_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 TopLevelTable for Gsub<'_> {
19    /// `GSUB`
20    const TAG: Tag = Tag::new(b"GSUB");
21}
22
23impl<'a> FontRead<'a> for Gsub<'a> {
24    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
25        #[allow(clippy::absurd_extreme_comparisons)]
26        if data.len() < Self::MIN_SIZE {
27            return Err(ReadError::OutOfBounds);
28        }
29        Ok(Self { data })
30    }
31}
32
33/// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header)
34#[derive(Clone)]
35pub struct Gsub<'a> {
36    data: FontData<'a>,
37}
38
39#[allow(clippy::needless_lifetimes)]
40impl<'a> Gsub<'a> {
41    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN
42        + Offset16::RAW_BYTE_LEN
43        + Offset16::RAW_BYTE_LEN
44        + Offset16::RAW_BYTE_LEN);
45    basic_table_impls!(impl_the_methods);
46
47    /// The major and minor version of the GSUB table, as a tuple (u16, u16)
48    pub fn version(&self) -> MajorMinor {
49        let range = self.version_byte_range();
50        self.data.read_at(range.start).ok().unwrap()
51    }
52
53    /// Offset to ScriptList table, from beginning of GSUB table
54    pub fn script_list_offset(&self) -> Offset16 {
55        let range = self.script_list_offset_byte_range();
56        self.data.read_at(range.start).ok().unwrap()
57    }
58
59    /// Attempt to resolve [`script_list_offset`][Self::script_list_offset].
60    pub fn script_list(&self) -> Result<ScriptList<'a>, ReadError> {
61        let data = self.data;
62        self.script_list_offset().resolve(data)
63    }
64
65    /// Offset to FeatureList table, from beginning of GSUB table
66    pub fn feature_list_offset(&self) -> Offset16 {
67        let range = self.feature_list_offset_byte_range();
68        self.data.read_at(range.start).ok().unwrap()
69    }
70
71    /// Attempt to resolve [`feature_list_offset`][Self::feature_list_offset].
72    pub fn feature_list(&self) -> Result<FeatureList<'a>, ReadError> {
73        let data = self.data;
74        self.feature_list_offset().resolve(data)
75    }
76
77    /// Offset to LookupList table, from beginning of GSUB table
78    pub fn lookup_list_offset(&self) -> Offset16 {
79        let range = self.lookup_list_offset_byte_range();
80        self.data.read_at(range.start).ok().unwrap()
81    }
82
83    /// Attempt to resolve [`lookup_list_offset`][Self::lookup_list_offset].
84    pub fn lookup_list(&self) -> Result<SubstitutionLookupList<'a>, ReadError> {
85        let data = self.data;
86        self.lookup_list_offset().resolve(data)
87    }
88
89    /// Offset to FeatureVariations table, from beginning of the GSUB
90    /// table (may be NULL)
91    pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
92        let range = self.feature_variations_offset_byte_range();
93        (!range.is_empty())
94            .then(|| self.data.read_at(range.start).ok())
95            .flatten()
96    }
97
98    /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset].
99    pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
100        let data = self.data;
101        self.feature_variations_offset().map(|x| x.resolve(data))?
102    }
103
104    pub fn version_byte_range(&self) -> Range<usize> {
105        let start = 0;
106        start..start + MajorMinor::RAW_BYTE_LEN
107    }
108
109    pub fn script_list_offset_byte_range(&self) -> Range<usize> {
110        let start = self.version_byte_range().end;
111        start..start + Offset16::RAW_BYTE_LEN
112    }
113
114    pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
115        let start = self.script_list_offset_byte_range().end;
116        start..start + Offset16::RAW_BYTE_LEN
117    }
118
119    pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
120        let start = self.feature_list_offset_byte_range().end;
121        start..start + Offset16::RAW_BYTE_LEN
122    }
123
124    pub fn feature_variations_offset_byte_range(&self) -> Range<usize> {
125        let start = self.lookup_list_offset_byte_range().end;
126        start
127            ..(self.version().compatible((1u16, 1u16)))
128                .then(|| start + Offset32::RAW_BYTE_LEN)
129                .unwrap_or(start)
130    }
131}
132
133const _: () = assert!(FontData::default_data_long_enough(Gsub::MIN_SIZE));
134
135impl Default for Gsub<'_> {
136    fn default() -> Self {
137        Self {
138            data: FontData::default_table_data(),
139        }
140    }
141}
142
143#[cfg(feature = "experimental_traverse")]
144impl<'a> SomeTable<'a> for Gsub<'a> {
145    fn type_name(&self) -> &str {
146        "Gsub"
147    }
148    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
149        match idx {
150            0usize => Some(Field::new("version", self.version())),
151            1usize => Some(Field::new(
152                "script_list_offset",
153                FieldType::offset(self.script_list_offset(), self.script_list()),
154            )),
155            2usize => Some(Field::new(
156                "feature_list_offset",
157                FieldType::offset(self.feature_list_offset(), self.feature_list()),
158            )),
159            3usize => Some(Field::new(
160                "lookup_list_offset",
161                FieldType::offset(self.lookup_list_offset(), self.lookup_list()),
162            )),
163            4usize if self.version().compatible((1u16, 1u16)) => Some(Field::new(
164                "feature_variations_offset",
165                FieldType::offset(
166                    self.feature_variations_offset().unwrap(),
167                    self.feature_variations(),
168                ),
169            )),
170            _ => None,
171        }
172    }
173}
174
175#[cfg(feature = "experimental_traverse")]
176#[allow(clippy::needless_lifetimes)]
177impl<'a> std::fmt::Debug for Gsub<'a> {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        (self as &dyn SomeTable<'a>).fmt(f)
180    }
181}
182
183/// A [GSUB Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsubLookupTypeEnum) subtable.
184pub enum SubstitutionLookup<'a> {
185    Single(Lookup<'a, SingleSubst<'a>>),
186    Multiple(Lookup<'a, MultipleSubstFormat1<'a>>),
187    Alternate(Lookup<'a, AlternateSubstFormat1<'a>>),
188    Ligature(Lookup<'a, LigatureSubstFormat1<'a>>),
189    Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>),
190    ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>),
191    Extension(Lookup<'a, ExtensionSubtable<'a>>),
192    Reverse(Lookup<'a, ReverseChainSingleSubstFormat1<'a>>),
193}
194
195impl Default for SubstitutionLookup<'_> {
196    fn default() -> Self {
197        Self::Single(Default::default())
198    }
199}
200
201impl<'a> FontRead<'a> for SubstitutionLookup<'a> {
202    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
203        let discriminant = Lookup::read_discriminant(bytes)?;
204        match discriminant {
205            1 => Ok(SubstitutionLookup::Single(FontRead::read(bytes)?)),
206            2 => Ok(SubstitutionLookup::Multiple(FontRead::read(bytes)?)),
207            3 => Ok(SubstitutionLookup::Alternate(FontRead::read(bytes)?)),
208            4 => Ok(SubstitutionLookup::Ligature(FontRead::read(bytes)?)),
209            5 => Ok(SubstitutionLookup::Contextual(FontRead::read(bytes)?)),
210            6 => Ok(SubstitutionLookup::ChainContextual(FontRead::read(bytes)?)),
211            7 => Ok(SubstitutionLookup::Extension(FontRead::read(bytes)?)),
212            8 => Ok(SubstitutionLookup::Reverse(FontRead::read(bytes)?)),
213            other => Err(ReadError::InvalidFormat(other.into())),
214        }
215    }
216}
217
218impl<'a> SubstitutionLookup<'a> {
219    #[allow(dead_code)]
220    /// Return the inner table, removing the specific generics.
221    ///
222    /// This lets us return a single concrete type we can call methods on.
223    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
224        match self {
225            SubstitutionLookup::Single(inner) => inner.of_unit_type(),
226            SubstitutionLookup::Multiple(inner) => inner.of_unit_type(),
227            SubstitutionLookup::Alternate(inner) => inner.of_unit_type(),
228            SubstitutionLookup::Ligature(inner) => inner.of_unit_type(),
229            SubstitutionLookup::Contextual(inner) => inner.of_unit_type(),
230            SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(),
231            SubstitutionLookup::Extension(inner) => inner.of_unit_type(),
232            SubstitutionLookup::Reverse(inner) => inner.of_unit_type(),
233        }
234    }
235}
236
237#[cfg(feature = "experimental_traverse")]
238impl<'a> SubstitutionLookup<'a> {
239    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
240        match self {
241            SubstitutionLookup::Single(table) => table,
242            SubstitutionLookup::Multiple(table) => table,
243            SubstitutionLookup::Alternate(table) => table,
244            SubstitutionLookup::Ligature(table) => table,
245            SubstitutionLookup::Contextual(table) => table,
246            SubstitutionLookup::ChainContextual(table) => table,
247            SubstitutionLookup::Extension(table) => table,
248            SubstitutionLookup::Reverse(table) => table,
249        }
250    }
251}
252
253#[cfg(feature = "experimental_traverse")]
254impl<'a> SomeTable<'a> for SubstitutionLookup<'a> {
255    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
256        self.dyn_inner().get_field(idx)
257    }
258    fn type_name(&self) -> &str {
259        self.dyn_inner().type_name()
260    }
261}
262
263#[cfg(feature = "experimental_traverse")]
264impl std::fmt::Debug for SubstitutionLookup<'_> {
265    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266        self.dyn_inner().fmt(f)
267    }
268}
269
270/// LookupType 1: [Single Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable) Subtable
271#[derive(Clone)]
272pub enum SingleSubst<'a> {
273    Format1(SingleSubstFormat1<'a>),
274    Format2(SingleSubstFormat2<'a>),
275}
276
277impl Default for SingleSubst<'_> {
278    fn default() -> Self {
279        Self::Format1(Default::default())
280    }
281}
282
283impl<'a> SingleSubst<'a> {
284    ///Return the `FontData` used to resolve offsets for this table.
285    pub fn offset_data(&self) -> FontData<'a> {
286        match self {
287            Self::Format1(item) => item.offset_data(),
288            Self::Format2(item) => item.offset_data(),
289        }
290    }
291
292    /// Format identifier: format = 1
293    pub fn subst_format(&self) -> u16 {
294        match self {
295            Self::Format1(item) => item.subst_format(),
296            Self::Format2(item) => item.subst_format(),
297        }
298    }
299
300    /// Offset to Coverage table, from beginning of substitution
301    /// subtable
302    pub fn coverage_offset(&self) -> Offset16 {
303        match self {
304            Self::Format1(item) => item.coverage_offset(),
305            Self::Format2(item) => item.coverage_offset(),
306        }
307    }
308}
309
310impl<'a> FontRead<'a> for SingleSubst<'a> {
311    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
312        let format: u16 = data.read_at(0usize)?;
313        match format {
314            SingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
315            SingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
316            other => Err(ReadError::InvalidFormat(other.into())),
317        }
318    }
319}
320
321impl<'a> MinByteRange<'a> for SingleSubst<'a> {
322    fn min_byte_range(&self) -> Range<usize> {
323        match self {
324            Self::Format1(item) => item.min_byte_range(),
325            Self::Format2(item) => item.min_byte_range(),
326        }
327    }
328    fn min_table_bytes(&self) -> &'a [u8] {
329        match self {
330            Self::Format1(item) => item.min_table_bytes(),
331            Self::Format2(item) => item.min_table_bytes(),
332        }
333    }
334}
335
336#[cfg(feature = "experimental_traverse")]
337impl<'a> SingleSubst<'a> {
338    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
339        match self {
340            Self::Format1(table) => table,
341            Self::Format2(table) => table,
342        }
343    }
344}
345
346#[cfg(feature = "experimental_traverse")]
347impl std::fmt::Debug for SingleSubst<'_> {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        self.dyn_inner().fmt(f)
350    }
351}
352
353#[cfg(feature = "experimental_traverse")]
354impl<'a> SomeTable<'a> for SingleSubst<'a> {
355    fn type_name(&self) -> &str {
356        self.dyn_inner().type_name()
357    }
358    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
359        self.dyn_inner().get_field(idx)
360    }
361}
362
363impl Format<u16> for SingleSubstFormat1<'_> {
364    const FORMAT: u16 = 1;
365}
366
367impl<'a> MinByteRange<'a> for SingleSubstFormat1<'a> {
368    fn min_byte_range(&self) -> Range<usize> {
369        0..self.delta_glyph_id_byte_range().end
370    }
371    fn min_table_bytes(&self) -> &'a [u8] {
372        let range = self.min_byte_range();
373        self.data.as_bytes().get(range).unwrap_or_default()
374    }
375}
376
377impl<'a> FontRead<'a> for SingleSubstFormat1<'a> {
378    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
379        #[allow(clippy::absurd_extreme_comparisons)]
380        if data.len() < Self::MIN_SIZE {
381            return Err(ReadError::OutOfBounds);
382        }
383        Ok(Self { data })
384    }
385}
386
387/// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1)
388#[derive(Clone)]
389pub struct SingleSubstFormat1<'a> {
390    data: FontData<'a>,
391}
392
393#[allow(clippy::needless_lifetimes)]
394impl<'a> SingleSubstFormat1<'a> {
395    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
396    basic_table_impls!(impl_the_methods);
397
398    /// Format identifier: format = 1
399    pub fn subst_format(&self) -> u16 {
400        let range = self.subst_format_byte_range();
401        self.data.read_at(range.start).ok().unwrap()
402    }
403
404    /// Offset to Coverage table, from beginning of substitution
405    /// subtable
406    pub fn coverage_offset(&self) -> Offset16 {
407        let range = self.coverage_offset_byte_range();
408        self.data.read_at(range.start).ok().unwrap()
409    }
410
411    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
412    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
413        let data = self.data;
414        self.coverage_offset().resolve(data)
415    }
416
417    /// Add to original glyph ID to get substitute glyph ID
418    pub fn delta_glyph_id(&self) -> i16 {
419        let range = self.delta_glyph_id_byte_range();
420        self.data.read_at(range.start).ok().unwrap()
421    }
422
423    pub fn subst_format_byte_range(&self) -> Range<usize> {
424        let start = 0;
425        start..start + u16::RAW_BYTE_LEN
426    }
427
428    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
429        let start = self.subst_format_byte_range().end;
430        start..start + Offset16::RAW_BYTE_LEN
431    }
432
433    pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
434        let start = self.coverage_offset_byte_range().end;
435        start..start + i16::RAW_BYTE_LEN
436    }
437}
438
439const _: () = assert!(FontData::default_data_long_enough(
440    SingleSubstFormat1::MIN_SIZE
441));
442
443impl Default for SingleSubstFormat1<'_> {
444    fn default() -> Self {
445        Self {
446            data: FontData::default_format_1_u16_table_data(),
447        }
448    }
449}
450
451#[cfg(feature = "experimental_traverse")]
452impl<'a> SomeTable<'a> for SingleSubstFormat1<'a> {
453    fn type_name(&self) -> &str {
454        "SingleSubstFormat1"
455    }
456    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
457        match idx {
458            0usize => Some(Field::new("subst_format", self.subst_format())),
459            1usize => Some(Field::new(
460                "coverage_offset",
461                FieldType::offset(self.coverage_offset(), self.coverage()),
462            )),
463            2usize => Some(Field::new("delta_glyph_id", self.delta_glyph_id())),
464            _ => None,
465        }
466    }
467}
468
469#[cfg(feature = "experimental_traverse")]
470#[allow(clippy::needless_lifetimes)]
471impl<'a> std::fmt::Debug for SingleSubstFormat1<'a> {
472    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473        (self as &dyn SomeTable<'a>).fmt(f)
474    }
475}
476
477impl Format<u16> for SingleSubstFormat2<'_> {
478    const FORMAT: u16 = 2;
479}
480
481impl<'a> MinByteRange<'a> for SingleSubstFormat2<'a> {
482    fn min_byte_range(&self) -> Range<usize> {
483        0..self.substitute_glyph_ids_byte_range().end
484    }
485    fn min_table_bytes(&self) -> &'a [u8] {
486        let range = self.min_byte_range();
487        self.data.as_bytes().get(range).unwrap_or_default()
488    }
489}
490
491impl<'a> FontRead<'a> for SingleSubstFormat2<'a> {
492    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
493        #[allow(clippy::absurd_extreme_comparisons)]
494        if data.len() < Self::MIN_SIZE {
495            return Err(ReadError::OutOfBounds);
496        }
497        Ok(Self { data })
498    }
499}
500
501/// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2)
502#[derive(Clone)]
503pub struct SingleSubstFormat2<'a> {
504    data: FontData<'a>,
505}
506
507#[allow(clippy::needless_lifetimes)]
508impl<'a> SingleSubstFormat2<'a> {
509    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
510    basic_table_impls!(impl_the_methods);
511
512    /// Format identifier: format = 2
513    pub fn subst_format(&self) -> u16 {
514        let range = self.subst_format_byte_range();
515        self.data.read_at(range.start).ok().unwrap()
516    }
517
518    /// Offset to Coverage table, from beginning of substitution
519    /// subtable
520    pub fn coverage_offset(&self) -> Offset16 {
521        let range = self.coverage_offset_byte_range();
522        self.data.read_at(range.start).ok().unwrap()
523    }
524
525    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
526    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
527        let data = self.data;
528        self.coverage_offset().resolve(data)
529    }
530
531    /// Number of glyph IDs in the substituteGlyphIDs array
532    pub fn glyph_count(&self) -> u16 {
533        let range = self.glyph_count_byte_range();
534        self.data.read_at(range.start).ok().unwrap()
535    }
536
537    /// Array of substitute glyph IDs — ordered by Coverage index
538    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
539        let range = self.substitute_glyph_ids_byte_range();
540        self.data.read_array(range).ok().unwrap_or_default()
541    }
542
543    pub fn subst_format_byte_range(&self) -> Range<usize> {
544        let start = 0;
545        start..start + u16::RAW_BYTE_LEN
546    }
547
548    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
549        let start = self.subst_format_byte_range().end;
550        start..start + Offset16::RAW_BYTE_LEN
551    }
552
553    pub fn glyph_count_byte_range(&self) -> Range<usize> {
554        let start = self.coverage_offset_byte_range().end;
555        start..start + u16::RAW_BYTE_LEN
556    }
557
558    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
559        let glyph_count = self.glyph_count();
560        let start = self.glyph_count_byte_range().end;
561        start..start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN)
562    }
563}
564
565#[cfg(feature = "experimental_traverse")]
566impl<'a> SomeTable<'a> for SingleSubstFormat2<'a> {
567    fn type_name(&self) -> &str {
568        "SingleSubstFormat2"
569    }
570    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
571        match idx {
572            0usize => Some(Field::new("subst_format", self.subst_format())),
573            1usize => Some(Field::new(
574                "coverage_offset",
575                FieldType::offset(self.coverage_offset(), self.coverage()),
576            )),
577            2usize => Some(Field::new("glyph_count", self.glyph_count())),
578            3usize => Some(Field::new(
579                "substitute_glyph_ids",
580                self.substitute_glyph_ids(),
581            )),
582            _ => None,
583        }
584    }
585}
586
587#[cfg(feature = "experimental_traverse")]
588#[allow(clippy::needless_lifetimes)]
589impl<'a> std::fmt::Debug for SingleSubstFormat2<'a> {
590    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
591        (self as &dyn SomeTable<'a>).fmt(f)
592    }
593}
594
595impl Format<u16> for MultipleSubstFormat1<'_> {
596    const FORMAT: u16 = 1;
597}
598
599impl<'a> MinByteRange<'a> for MultipleSubstFormat1<'a> {
600    fn min_byte_range(&self) -> Range<usize> {
601        0..self.sequence_offsets_byte_range().end
602    }
603    fn min_table_bytes(&self) -> &'a [u8] {
604        let range = self.min_byte_range();
605        self.data.as_bytes().get(range).unwrap_or_default()
606    }
607}
608
609impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> {
610    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
611        #[allow(clippy::absurd_extreme_comparisons)]
612        if data.len() < Self::MIN_SIZE {
613            return Err(ReadError::OutOfBounds);
614        }
615        Ok(Self { data })
616    }
617}
618
619/// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1)
620#[derive(Clone)]
621pub struct MultipleSubstFormat1<'a> {
622    data: FontData<'a>,
623}
624
625#[allow(clippy::needless_lifetimes)]
626impl<'a> MultipleSubstFormat1<'a> {
627    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
628    basic_table_impls!(impl_the_methods);
629
630    /// Format identifier: format = 1
631    pub fn subst_format(&self) -> u16 {
632        let range = self.subst_format_byte_range();
633        self.data.read_at(range.start).ok().unwrap()
634    }
635
636    /// Offset to Coverage table, from beginning of substitution
637    /// subtable
638    pub fn coverage_offset(&self) -> Offset16 {
639        let range = self.coverage_offset_byte_range();
640        self.data.read_at(range.start).ok().unwrap()
641    }
642
643    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
644    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
645        let data = self.data;
646        self.coverage_offset().resolve(data)
647    }
648
649    /// Number of Sequence table offsets in the sequenceOffsets array
650    pub fn sequence_count(&self) -> u16 {
651        let range = self.sequence_count_byte_range();
652        self.data.read_at(range.start).ok().unwrap()
653    }
654
655    /// Array of offsets to Sequence tables. Offsets are from beginning
656    /// of substitution subtable, ordered by Coverage index
657    pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset16>] {
658        let range = self.sequence_offsets_byte_range();
659        self.data.read_array(range).ok().unwrap_or_default()
660    }
661
662    /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets].
663    pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> {
664        let data = self.data;
665        let offsets = self.sequence_offsets();
666        ArrayOfOffsets::new(offsets, data, ())
667    }
668
669    pub fn subst_format_byte_range(&self) -> Range<usize> {
670        let start = 0;
671        start..start + u16::RAW_BYTE_LEN
672    }
673
674    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
675        let start = self.subst_format_byte_range().end;
676        start..start + Offset16::RAW_BYTE_LEN
677    }
678
679    pub fn sequence_count_byte_range(&self) -> Range<usize> {
680        let start = self.coverage_offset_byte_range().end;
681        start..start + u16::RAW_BYTE_LEN
682    }
683
684    pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
685        let sequence_count = self.sequence_count();
686        let start = self.sequence_count_byte_range().end;
687        start..start + (transforms::to_usize(sequence_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
688    }
689}
690
691const _: () = assert!(FontData::default_data_long_enough(
692    MultipleSubstFormat1::MIN_SIZE
693));
694
695impl Default for MultipleSubstFormat1<'_> {
696    fn default() -> Self {
697        Self {
698            data: FontData::default_format_1_u16_table_data(),
699        }
700    }
701}
702
703#[cfg(feature = "experimental_traverse")]
704impl<'a> SomeTable<'a> for MultipleSubstFormat1<'a> {
705    fn type_name(&self) -> &str {
706        "MultipleSubstFormat1"
707    }
708    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
709        match idx {
710            0usize => Some(Field::new("subst_format", self.subst_format())),
711            1usize => Some(Field::new(
712                "coverage_offset",
713                FieldType::offset(self.coverage_offset(), self.coverage()),
714            )),
715            2usize => Some(Field::new("sequence_count", self.sequence_count())),
716            3usize => Some(Field::new(
717                "sequence_offsets",
718                FieldType::from(self.sequences()),
719            )),
720            _ => None,
721        }
722    }
723}
724
725#[cfg(feature = "experimental_traverse")]
726#[allow(clippy::needless_lifetimes)]
727impl<'a> std::fmt::Debug for MultipleSubstFormat1<'a> {
728    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
729        (self as &dyn SomeTable<'a>).fmt(f)
730    }
731}
732
733impl<'a> MinByteRange<'a> for Sequence<'a> {
734    fn min_byte_range(&self) -> Range<usize> {
735        0..self.substitute_glyph_ids_byte_range().end
736    }
737    fn min_table_bytes(&self) -> &'a [u8] {
738        let range = self.min_byte_range();
739        self.data.as_bytes().get(range).unwrap_or_default()
740    }
741}
742
743impl<'a> FontRead<'a> for Sequence<'a> {
744    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
745        #[allow(clippy::absurd_extreme_comparisons)]
746        if data.len() < Self::MIN_SIZE {
747            return Err(ReadError::OutOfBounds);
748        }
749        Ok(Self { data })
750    }
751}
752
753/// Part of [MultipleSubstFormat1]
754#[derive(Clone)]
755pub struct Sequence<'a> {
756    data: FontData<'a>,
757}
758
759#[allow(clippy::needless_lifetimes)]
760impl<'a> Sequence<'a> {
761    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
762    basic_table_impls!(impl_the_methods);
763
764    /// Number of glyph IDs in the substituteGlyphIDs array. This must
765    /// always be greater than 0.
766    pub fn glyph_count(&self) -> u16 {
767        let range = self.glyph_count_byte_range();
768        self.data.read_at(range.start).ok().unwrap()
769    }
770
771    /// String of glyph IDs to substitute
772    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
773        let range = self.substitute_glyph_ids_byte_range();
774        self.data.read_array(range).ok().unwrap_or_default()
775    }
776
777    pub fn glyph_count_byte_range(&self) -> Range<usize> {
778        let start = 0;
779        start..start + u16::RAW_BYTE_LEN
780    }
781
782    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
783        let glyph_count = self.glyph_count();
784        let start = self.glyph_count_byte_range().end;
785        start..start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN)
786    }
787}
788
789const _: () = assert!(FontData::default_data_long_enough(Sequence::MIN_SIZE));
790
791impl Default for Sequence<'_> {
792    fn default() -> Self {
793        Self {
794            data: FontData::default_table_data(),
795        }
796    }
797}
798
799#[cfg(feature = "experimental_traverse")]
800impl<'a> SomeTable<'a> for Sequence<'a> {
801    fn type_name(&self) -> &str {
802        "Sequence"
803    }
804    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
805        match idx {
806            0usize => Some(Field::new("glyph_count", self.glyph_count())),
807            1usize => Some(Field::new(
808                "substitute_glyph_ids",
809                self.substitute_glyph_ids(),
810            )),
811            _ => None,
812        }
813    }
814}
815
816#[cfg(feature = "experimental_traverse")]
817#[allow(clippy::needless_lifetimes)]
818impl<'a> std::fmt::Debug for Sequence<'a> {
819    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
820        (self as &dyn SomeTable<'a>).fmt(f)
821    }
822}
823
824impl Format<u16> for AlternateSubstFormat1<'_> {
825    const FORMAT: u16 = 1;
826}
827
828impl<'a> MinByteRange<'a> for AlternateSubstFormat1<'a> {
829    fn min_byte_range(&self) -> Range<usize> {
830        0..self.alternate_set_offsets_byte_range().end
831    }
832    fn min_table_bytes(&self) -> &'a [u8] {
833        let range = self.min_byte_range();
834        self.data.as_bytes().get(range).unwrap_or_default()
835    }
836}
837
838impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> {
839    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
840        #[allow(clippy::absurd_extreme_comparisons)]
841        if data.len() < Self::MIN_SIZE {
842            return Err(ReadError::OutOfBounds);
843        }
844        Ok(Self { data })
845    }
846}
847
848/// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1)
849#[derive(Clone)]
850pub struct AlternateSubstFormat1<'a> {
851    data: FontData<'a>,
852}
853
854#[allow(clippy::needless_lifetimes)]
855impl<'a> AlternateSubstFormat1<'a> {
856    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
857    basic_table_impls!(impl_the_methods);
858
859    /// Format identifier: format = 1
860    pub fn subst_format(&self) -> u16 {
861        let range = self.subst_format_byte_range();
862        self.data.read_at(range.start).ok().unwrap()
863    }
864
865    /// Offset to Coverage table, from beginning of substitution
866    /// subtable
867    pub fn coverage_offset(&self) -> Offset16 {
868        let range = self.coverage_offset_byte_range();
869        self.data.read_at(range.start).ok().unwrap()
870    }
871
872    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
873    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
874        let data = self.data;
875        self.coverage_offset().resolve(data)
876    }
877
878    /// Number of AlternateSet tables
879    pub fn alternate_set_count(&self) -> u16 {
880        let range = self.alternate_set_count_byte_range();
881        self.data.read_at(range.start).ok().unwrap()
882    }
883
884    /// Array of offsets to AlternateSet tables. Offsets are from
885    /// beginning of substitution subtable, ordered by Coverage index
886    pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
887        let range = self.alternate_set_offsets_byte_range();
888        self.data.read_array(range).ok().unwrap_or_default()
889    }
890
891    /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets].
892    pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> {
893        let data = self.data;
894        let offsets = self.alternate_set_offsets();
895        ArrayOfOffsets::new(offsets, data, ())
896    }
897
898    pub fn subst_format_byte_range(&self) -> Range<usize> {
899        let start = 0;
900        start..start + u16::RAW_BYTE_LEN
901    }
902
903    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
904        let start = self.subst_format_byte_range().end;
905        start..start + Offset16::RAW_BYTE_LEN
906    }
907
908    pub fn alternate_set_count_byte_range(&self) -> Range<usize> {
909        let start = self.coverage_offset_byte_range().end;
910        start..start + u16::RAW_BYTE_LEN
911    }
912
913    pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
914        let alternate_set_count = self.alternate_set_count();
915        let start = self.alternate_set_count_byte_range().end;
916        start
917            ..start
918                + (transforms::to_usize(alternate_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
919    }
920}
921
922const _: () = assert!(FontData::default_data_long_enough(
923    AlternateSubstFormat1::MIN_SIZE
924));
925
926impl Default for AlternateSubstFormat1<'_> {
927    fn default() -> Self {
928        Self {
929            data: FontData::default_format_1_u16_table_data(),
930        }
931    }
932}
933
934#[cfg(feature = "experimental_traverse")]
935impl<'a> SomeTable<'a> for AlternateSubstFormat1<'a> {
936    fn type_name(&self) -> &str {
937        "AlternateSubstFormat1"
938    }
939    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
940        match idx {
941            0usize => Some(Field::new("subst_format", self.subst_format())),
942            1usize => Some(Field::new(
943                "coverage_offset",
944                FieldType::offset(self.coverage_offset(), self.coverage()),
945            )),
946            2usize => Some(Field::new(
947                "alternate_set_count",
948                self.alternate_set_count(),
949            )),
950            3usize => Some(Field::new(
951                "alternate_set_offsets",
952                FieldType::from(self.alternate_sets()),
953            )),
954            _ => None,
955        }
956    }
957}
958
959#[cfg(feature = "experimental_traverse")]
960#[allow(clippy::needless_lifetimes)]
961impl<'a> std::fmt::Debug for AlternateSubstFormat1<'a> {
962    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
963        (self as &dyn SomeTable<'a>).fmt(f)
964    }
965}
966
967impl<'a> MinByteRange<'a> for AlternateSet<'a> {
968    fn min_byte_range(&self) -> Range<usize> {
969        0..self.alternate_glyph_ids_byte_range().end
970    }
971    fn min_table_bytes(&self) -> &'a [u8] {
972        let range = self.min_byte_range();
973        self.data.as_bytes().get(range).unwrap_or_default()
974    }
975}
976
977impl<'a> FontRead<'a> for AlternateSet<'a> {
978    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
979        #[allow(clippy::absurd_extreme_comparisons)]
980        if data.len() < Self::MIN_SIZE {
981            return Err(ReadError::OutOfBounds);
982        }
983        Ok(Self { data })
984    }
985}
986
987/// Part of [AlternateSubstFormat1]
988#[derive(Clone)]
989pub struct AlternateSet<'a> {
990    data: FontData<'a>,
991}
992
993#[allow(clippy::needless_lifetimes)]
994impl<'a> AlternateSet<'a> {
995    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
996    basic_table_impls!(impl_the_methods);
997
998    /// Number of glyph IDs in the alternateGlyphIDs array
999    pub fn glyph_count(&self) -> u16 {
1000        let range = self.glyph_count_byte_range();
1001        self.data.read_at(range.start).ok().unwrap()
1002    }
1003
1004    /// Array of alternate glyph IDs, in arbitrary order
1005    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1006        let range = self.alternate_glyph_ids_byte_range();
1007        self.data.read_array(range).ok().unwrap_or_default()
1008    }
1009
1010    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1011        let start = 0;
1012        start..start + u16::RAW_BYTE_LEN
1013    }
1014
1015    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
1016        let glyph_count = self.glyph_count();
1017        let start = self.glyph_count_byte_range().end;
1018        start..start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN)
1019    }
1020}
1021
1022const _: () = assert!(FontData::default_data_long_enough(AlternateSet::MIN_SIZE));
1023
1024impl Default for AlternateSet<'_> {
1025    fn default() -> Self {
1026        Self {
1027            data: FontData::default_table_data(),
1028        }
1029    }
1030}
1031
1032#[cfg(feature = "experimental_traverse")]
1033impl<'a> SomeTable<'a> for AlternateSet<'a> {
1034    fn type_name(&self) -> &str {
1035        "AlternateSet"
1036    }
1037    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1038        match idx {
1039            0usize => Some(Field::new("glyph_count", self.glyph_count())),
1040            1usize => Some(Field::new(
1041                "alternate_glyph_ids",
1042                self.alternate_glyph_ids(),
1043            )),
1044            _ => None,
1045        }
1046    }
1047}
1048
1049#[cfg(feature = "experimental_traverse")]
1050#[allow(clippy::needless_lifetimes)]
1051impl<'a> std::fmt::Debug for AlternateSet<'a> {
1052    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1053        (self as &dyn SomeTable<'a>).fmt(f)
1054    }
1055}
1056
1057impl Format<u16> for LigatureSubstFormat1<'_> {
1058    const FORMAT: u16 = 1;
1059}
1060
1061impl<'a> MinByteRange<'a> for LigatureSubstFormat1<'a> {
1062    fn min_byte_range(&self) -> Range<usize> {
1063        0..self.ligature_set_offsets_byte_range().end
1064    }
1065    fn min_table_bytes(&self) -> &'a [u8] {
1066        let range = self.min_byte_range();
1067        self.data.as_bytes().get(range).unwrap_or_default()
1068    }
1069}
1070
1071impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
1072    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1073        #[allow(clippy::absurd_extreme_comparisons)]
1074        if data.len() < Self::MIN_SIZE {
1075            return Err(ReadError::OutOfBounds);
1076        }
1077        Ok(Self { data })
1078    }
1079}
1080
1081/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
1082#[derive(Clone)]
1083pub struct LigatureSubstFormat1<'a> {
1084    data: FontData<'a>,
1085}
1086
1087#[allow(clippy::needless_lifetimes)]
1088impl<'a> LigatureSubstFormat1<'a> {
1089    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1090    basic_table_impls!(impl_the_methods);
1091
1092    /// Format identifier: format = 1
1093    pub fn subst_format(&self) -> u16 {
1094        let range = self.subst_format_byte_range();
1095        self.data.read_at(range.start).ok().unwrap()
1096    }
1097
1098    /// Offset to Coverage table, from beginning of substitution
1099    /// subtable
1100    pub fn coverage_offset(&self) -> Offset16 {
1101        let range = self.coverage_offset_byte_range();
1102        self.data.read_at(range.start).ok().unwrap()
1103    }
1104
1105    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1106    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1107        let data = self.data;
1108        self.coverage_offset().resolve(data)
1109    }
1110
1111    /// Number of LigatureSet tables
1112    pub fn ligature_set_count(&self) -> u16 {
1113        let range = self.ligature_set_count_byte_range();
1114        self.data.read_at(range.start).ok().unwrap()
1115    }
1116
1117    /// Array of offsets to LigatureSet tables. Offsets are from
1118    /// beginning of substitution subtable, ordered by Coverage index
1119    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
1120        let range = self.ligature_set_offsets_byte_range();
1121        self.data.read_array(range).ok().unwrap_or_default()
1122    }
1123
1124    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
1125    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
1126        let data = self.data;
1127        let offsets = self.ligature_set_offsets();
1128        ArrayOfOffsets::new(offsets, data, ())
1129    }
1130
1131    pub fn subst_format_byte_range(&self) -> Range<usize> {
1132        let start = 0;
1133        start..start + u16::RAW_BYTE_LEN
1134    }
1135
1136    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1137        let start = self.subst_format_byte_range().end;
1138        start..start + Offset16::RAW_BYTE_LEN
1139    }
1140
1141    pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
1142        let start = self.coverage_offset_byte_range().end;
1143        start..start + u16::RAW_BYTE_LEN
1144    }
1145
1146    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
1147        let ligature_set_count = self.ligature_set_count();
1148        let start = self.ligature_set_count_byte_range().end;
1149        start
1150            ..start
1151                + (transforms::to_usize(ligature_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
1152    }
1153}
1154
1155const _: () = assert!(FontData::default_data_long_enough(
1156    LigatureSubstFormat1::MIN_SIZE
1157));
1158
1159impl Default for LigatureSubstFormat1<'_> {
1160    fn default() -> Self {
1161        Self {
1162            data: FontData::default_format_1_u16_table_data(),
1163        }
1164    }
1165}
1166
1167#[cfg(feature = "experimental_traverse")]
1168impl<'a> SomeTable<'a> for LigatureSubstFormat1<'a> {
1169    fn type_name(&self) -> &str {
1170        "LigatureSubstFormat1"
1171    }
1172    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1173        match idx {
1174            0usize => Some(Field::new("subst_format", self.subst_format())),
1175            1usize => Some(Field::new(
1176                "coverage_offset",
1177                FieldType::offset(self.coverage_offset(), self.coverage()),
1178            )),
1179            2usize => Some(Field::new("ligature_set_count", self.ligature_set_count())),
1180            3usize => Some(Field::new(
1181                "ligature_set_offsets",
1182                FieldType::from(self.ligature_sets()),
1183            )),
1184            _ => None,
1185        }
1186    }
1187}
1188
1189#[cfg(feature = "experimental_traverse")]
1190#[allow(clippy::needless_lifetimes)]
1191impl<'a> std::fmt::Debug for LigatureSubstFormat1<'a> {
1192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1193        (self as &dyn SomeTable<'a>).fmt(f)
1194    }
1195}
1196
1197impl<'a> MinByteRange<'a> for LigatureSet<'a> {
1198    fn min_byte_range(&self) -> Range<usize> {
1199        0..self.ligature_offsets_byte_range().end
1200    }
1201    fn min_table_bytes(&self) -> &'a [u8] {
1202        let range = self.min_byte_range();
1203        self.data.as_bytes().get(range).unwrap_or_default()
1204    }
1205}
1206
1207impl<'a> FontRead<'a> for LigatureSet<'a> {
1208    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1209        #[allow(clippy::absurd_extreme_comparisons)]
1210        if data.len() < Self::MIN_SIZE {
1211            return Err(ReadError::OutOfBounds);
1212        }
1213        Ok(Self { data })
1214    }
1215}
1216
1217/// Part of [LigatureSubstFormat1]
1218#[derive(Clone)]
1219pub struct LigatureSet<'a> {
1220    data: FontData<'a>,
1221}
1222
1223#[allow(clippy::needless_lifetimes)]
1224impl<'a> LigatureSet<'a> {
1225    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1226    basic_table_impls!(impl_the_methods);
1227
1228    /// Number of Ligature tables
1229    pub fn ligature_count(&self) -> u16 {
1230        let range = self.ligature_count_byte_range();
1231        self.data.read_at(range.start).ok().unwrap()
1232    }
1233
1234    /// Array of offsets to Ligature tables. Offsets are from beginning
1235    /// of LigatureSet table, ordered by preference.
1236    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
1237        let range = self.ligature_offsets_byte_range();
1238        self.data.read_array(range).ok().unwrap_or_default()
1239    }
1240
1241    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
1242    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
1243        let data = self.data;
1244        let offsets = self.ligature_offsets();
1245        ArrayOfOffsets::new(offsets, data, ())
1246    }
1247
1248    pub fn ligature_count_byte_range(&self) -> Range<usize> {
1249        let start = 0;
1250        start..start + u16::RAW_BYTE_LEN
1251    }
1252
1253    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
1254        let ligature_count = self.ligature_count();
1255        let start = self.ligature_count_byte_range().end;
1256        start..start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
1257    }
1258}
1259
1260const _: () = assert!(FontData::default_data_long_enough(LigatureSet::MIN_SIZE));
1261
1262impl Default for LigatureSet<'_> {
1263    fn default() -> Self {
1264        Self {
1265            data: FontData::default_table_data(),
1266        }
1267    }
1268}
1269
1270#[cfg(feature = "experimental_traverse")]
1271impl<'a> SomeTable<'a> for LigatureSet<'a> {
1272    fn type_name(&self) -> &str {
1273        "LigatureSet"
1274    }
1275    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1276        match idx {
1277            0usize => Some(Field::new("ligature_count", self.ligature_count())),
1278            1usize => Some(Field::new(
1279                "ligature_offsets",
1280                FieldType::from(self.ligatures()),
1281            )),
1282            _ => None,
1283        }
1284    }
1285}
1286
1287#[cfg(feature = "experimental_traverse")]
1288#[allow(clippy::needless_lifetimes)]
1289impl<'a> std::fmt::Debug for LigatureSet<'a> {
1290    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1291        (self as &dyn SomeTable<'a>).fmt(f)
1292    }
1293}
1294
1295impl<'a> MinByteRange<'a> for Ligature<'a> {
1296    fn min_byte_range(&self) -> Range<usize> {
1297        0..self.component_glyph_ids_byte_range().end
1298    }
1299    fn min_table_bytes(&self) -> &'a [u8] {
1300        let range = self.min_byte_range();
1301        self.data.as_bytes().get(range).unwrap_or_default()
1302    }
1303}
1304
1305impl<'a> FontRead<'a> for Ligature<'a> {
1306    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1307        #[allow(clippy::absurd_extreme_comparisons)]
1308        if data.len() < Self::MIN_SIZE {
1309            return Err(ReadError::OutOfBounds);
1310        }
1311        Ok(Self { data })
1312    }
1313}
1314
1315/// Part of [LigatureSubstFormat1]
1316#[derive(Clone)]
1317pub struct Ligature<'a> {
1318    data: FontData<'a>,
1319}
1320
1321#[allow(clippy::needless_lifetimes)]
1322impl<'a> Ligature<'a> {
1323    pub const MIN_SIZE: usize = (GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1324    basic_table_impls!(impl_the_methods);
1325
1326    /// glyph ID of ligature to substitute
1327    pub fn ligature_glyph(&self) -> GlyphId16 {
1328        let range = self.ligature_glyph_byte_range();
1329        self.data.read_at(range.start).ok().unwrap()
1330    }
1331
1332    /// Number of components in the ligature
1333    pub fn component_count(&self) -> u16 {
1334        let range = self.component_count_byte_range();
1335        self.data.read_at(range.start).ok().unwrap()
1336    }
1337
1338    /// Array of component glyph IDs — start with the second
1339    /// component, ordered in writing direction
1340    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1341        let range = self.component_glyph_ids_byte_range();
1342        self.data.read_array(range).ok().unwrap_or_default()
1343    }
1344
1345    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
1346        let start = 0;
1347        start..start + GlyphId16::RAW_BYTE_LEN
1348    }
1349
1350    pub fn component_count_byte_range(&self) -> Range<usize> {
1351        let start = self.ligature_glyph_byte_range().end;
1352        start..start + u16::RAW_BYTE_LEN
1353    }
1354
1355    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
1356        let component_count = self.component_count();
1357        let start = self.component_count_byte_range().end;
1358        start
1359            ..start
1360                + (transforms::subtract(component_count, 1_usize))
1361                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
1362    }
1363}
1364
1365const _: () = assert!(FontData::default_data_long_enough(Ligature::MIN_SIZE));
1366
1367impl Default for Ligature<'_> {
1368    fn default() -> Self {
1369        Self {
1370            data: FontData::default_table_data(),
1371        }
1372    }
1373}
1374
1375#[cfg(feature = "experimental_traverse")]
1376impl<'a> SomeTable<'a> for Ligature<'a> {
1377    fn type_name(&self) -> &str {
1378        "Ligature"
1379    }
1380    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1381        match idx {
1382            0usize => Some(Field::new("ligature_glyph", self.ligature_glyph())),
1383            1usize => Some(Field::new("component_count", self.component_count())),
1384            2usize => Some(Field::new(
1385                "component_glyph_ids",
1386                self.component_glyph_ids(),
1387            )),
1388            _ => None,
1389        }
1390    }
1391}
1392
1393#[cfg(feature = "experimental_traverse")]
1394#[allow(clippy::needless_lifetimes)]
1395impl<'a> std::fmt::Debug for Ligature<'a> {
1396    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1397        (self as &dyn SomeTable<'a>).fmt(f)
1398    }
1399}
1400
1401impl Format<u16> for ExtensionSubstFormat1<'_> {
1402    const FORMAT: u16 = 1;
1403}
1404
1405impl Discriminant for ExtensionSubstFormat1<'_, ()> {
1406    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
1407        data.read_at(u16::RAW_BYTE_LEN)
1408    }
1409}
1410
1411impl<'a, T> MinByteRange<'a> for ExtensionSubstFormat1<'a, T> {
1412    fn min_byte_range(&self) -> Range<usize> {
1413        0..self.extension_offset_byte_range().end
1414    }
1415    fn min_table_bytes(&self) -> &'a [u8] {
1416        let range = self.min_byte_range();
1417        self.data.as_bytes().get(range).unwrap_or_default()
1418    }
1419}
1420
1421impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
1422    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1423        #[allow(clippy::absurd_extreme_comparisons)]
1424        if data.len() < Self::MIN_SIZE {
1425            return Err(ReadError::OutOfBounds);
1426        }
1427        Ok(Self {
1428            data,
1429            offset_type: std::marker::PhantomData,
1430        })
1431    }
1432}
1433
1434impl<'a, T> ExtensionSubstFormat1<'a, T> {
1435    #[allow(dead_code)]
1436    /// Replace the specific generic type on this implementation with `()`
1437    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1438        ExtensionSubstFormat1 {
1439            data: self.data,
1440            offset_type: std::marker::PhantomData,
1441        }
1442    }
1443}
1444
1445/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
1446#[derive(Clone)]
1447pub struct ExtensionSubstFormat1<'a, T = ()> {
1448    data: FontData<'a>,
1449    offset_type: std::marker::PhantomData<*const T>,
1450}
1451
1452#[allow(clippy::needless_lifetimes)]
1453impl<'a, T> ExtensionSubstFormat1<'a, T> {
1454    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
1455    basic_table_impls!(impl_the_methods);
1456
1457    /// Format identifier. Set to 1.
1458    pub fn subst_format(&self) -> u16 {
1459        let range = self.subst_format_byte_range();
1460        self.data.read_at(range.start).ok().unwrap()
1461    }
1462
1463    /// Lookup type of subtable referenced by extensionOffset (that is,
1464    /// the extension subtable).
1465    pub fn extension_lookup_type(&self) -> u16 {
1466        let range = self.extension_lookup_type_byte_range();
1467        self.data.read_at(range.start).ok().unwrap()
1468    }
1469
1470    /// Offset to the extension subtable, of lookup type
1471    /// extensionLookupType, relative to the start of the
1472    /// ExtensionSubstFormat1 subtable.
1473    pub fn extension_offset(&self) -> Offset32 {
1474        let range = self.extension_offset_byte_range();
1475        self.data.read_at(range.start).ok().unwrap()
1476    }
1477
1478    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
1479    pub fn extension(&self) -> Result<T, ReadError>
1480    where
1481        T: FontRead<'a>,
1482    {
1483        let data = self.data;
1484        self.extension_offset().resolve(data)
1485    }
1486
1487    pub fn subst_format_byte_range(&self) -> Range<usize> {
1488        let start = 0;
1489        start..start + u16::RAW_BYTE_LEN
1490    }
1491
1492    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
1493        let start = self.subst_format_byte_range().end;
1494        start..start + u16::RAW_BYTE_LEN
1495    }
1496
1497    pub fn extension_offset_byte_range(&self) -> Range<usize> {
1498        let start = self.extension_lookup_type_byte_range().end;
1499        start..start + Offset32::RAW_BYTE_LEN
1500    }
1501}
1502
1503const _: () = assert!(FontData::default_data_long_enough(
1504    ExtensionSubstFormat1::<()>::MIN_SIZE
1505));
1506
1507impl<T> Default for ExtensionSubstFormat1<'_, T> {
1508    fn default() -> Self {
1509        Self {
1510            data: FontData::default_format_1_u16_table_data(),
1511            offset_type: std::marker::PhantomData,
1512        }
1513    }
1514}
1515
1516#[cfg(feature = "experimental_traverse")]
1517impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionSubstFormat1<'a, T> {
1518    fn type_name(&self) -> &str {
1519        "ExtensionSubstFormat1"
1520    }
1521    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1522        match idx {
1523            0usize => Some(Field::new("subst_format", self.subst_format())),
1524            1usize => Some(Field::new(
1525                "extension_lookup_type",
1526                self.extension_lookup_type(),
1527            )),
1528            2usize => Some(Field::new(
1529                "extension_offset",
1530                FieldType::offset(self.extension_offset(), self.extension()),
1531            )),
1532            _ => None,
1533        }
1534    }
1535}
1536
1537#[cfg(feature = "experimental_traverse")]
1538#[allow(clippy::needless_lifetimes)]
1539impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for ExtensionSubstFormat1<'a, T> {
1540    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1541        (self as &dyn SomeTable<'a>).fmt(f)
1542    }
1543}
1544
1545/// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable
1546pub enum ExtensionSubtable<'a> {
1547    Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
1548    Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>),
1549    Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>),
1550    Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>),
1551    Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
1552    ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
1553    Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>),
1554}
1555
1556impl Default for ExtensionSubtable<'_> {
1557    fn default() -> Self {
1558        Self::Single(Default::default())
1559    }
1560}
1561
1562impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
1563    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
1564        let discriminant = ExtensionSubstFormat1::read_discriminant(bytes)?;
1565        match discriminant {
1566            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
1567            2 => Ok(ExtensionSubtable::Multiple(FontRead::read(bytes)?)),
1568            3 => Ok(ExtensionSubtable::Alternate(FontRead::read(bytes)?)),
1569            4 => Ok(ExtensionSubtable::Ligature(FontRead::read(bytes)?)),
1570            5 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
1571            6 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
1572            8 => Ok(ExtensionSubtable::Reverse(FontRead::read(bytes)?)),
1573            other => Err(ReadError::InvalidFormat(other.into())),
1574        }
1575    }
1576}
1577
1578impl<'a> ExtensionSubtable<'a> {
1579    #[allow(dead_code)]
1580    /// Return the inner table, removing the specific generics.
1581    ///
1582    /// This lets us return a single concrete type we can call methods on.
1583    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1584        match self {
1585            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
1586            ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
1587            ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
1588            ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
1589            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
1590            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
1591            ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
1592        }
1593    }
1594}
1595
1596#[cfg(feature = "experimental_traverse")]
1597impl<'a> ExtensionSubtable<'a> {
1598    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
1599        match self {
1600            ExtensionSubtable::Single(table) => table,
1601            ExtensionSubtable::Multiple(table) => table,
1602            ExtensionSubtable::Alternate(table) => table,
1603            ExtensionSubtable::Ligature(table) => table,
1604            ExtensionSubtable::Contextual(table) => table,
1605            ExtensionSubtable::ChainContextual(table) => table,
1606            ExtensionSubtable::Reverse(table) => table,
1607        }
1608    }
1609}
1610
1611#[cfg(feature = "experimental_traverse")]
1612impl<'a> SomeTable<'a> for ExtensionSubtable<'a> {
1613    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1614        self.dyn_inner().get_field(idx)
1615    }
1616    fn type_name(&self) -> &str {
1617        self.dyn_inner().type_name()
1618    }
1619}
1620
1621#[cfg(feature = "experimental_traverse")]
1622impl std::fmt::Debug for ExtensionSubtable<'_> {
1623    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1624        self.dyn_inner().fmt(f)
1625    }
1626}
1627
1628impl Format<u16> for ReverseChainSingleSubstFormat1<'_> {
1629    const FORMAT: u16 = 1;
1630}
1631
1632impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat1<'a> {
1633    fn min_byte_range(&self) -> Range<usize> {
1634        0..self.substitute_glyph_ids_byte_range().end
1635    }
1636    fn min_table_bytes(&self) -> &'a [u8] {
1637        let range = self.min_byte_range();
1638        self.data.as_bytes().get(range).unwrap_or_default()
1639    }
1640}
1641
1642impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
1643    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1644        #[allow(clippy::absurd_extreme_comparisons)]
1645        if data.len() < Self::MIN_SIZE {
1646            return Err(ReadError::OutOfBounds);
1647        }
1648        Ok(Self { data })
1649    }
1650}
1651
1652/// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts)
1653#[derive(Clone)]
1654pub struct ReverseChainSingleSubstFormat1<'a> {
1655    data: FontData<'a>,
1656}
1657
1658#[allow(clippy::needless_lifetimes)]
1659impl<'a> ReverseChainSingleSubstFormat1<'a> {
1660    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1661        + Offset16::RAW_BYTE_LEN
1662        + u16::RAW_BYTE_LEN
1663        + u16::RAW_BYTE_LEN
1664        + u16::RAW_BYTE_LEN);
1665    basic_table_impls!(impl_the_methods);
1666
1667    /// Format identifier: format = 1
1668    pub fn subst_format(&self) -> u16 {
1669        let range = self.subst_format_byte_range();
1670        self.data.read_at(range.start).ok().unwrap()
1671    }
1672
1673    /// Offset to Coverage table, from beginning of substitution
1674    /// subtable.
1675    pub fn coverage_offset(&self) -> Offset16 {
1676        let range = self.coverage_offset_byte_range();
1677        self.data.read_at(range.start).ok().unwrap()
1678    }
1679
1680    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1681    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1682        let data = self.data;
1683        self.coverage_offset().resolve(data)
1684    }
1685
1686    /// Number of glyphs in the backtrack sequence.
1687    pub fn backtrack_glyph_count(&self) -> u16 {
1688        let range = self.backtrack_glyph_count_byte_range();
1689        self.data.read_at(range.start).ok().unwrap()
1690    }
1691
1692    /// Array of offsets to coverage tables in backtrack sequence, in
1693    /// glyph sequence order.
1694    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1695        let range = self.backtrack_coverage_offsets_byte_range();
1696        self.data.read_array(range).ok().unwrap_or_default()
1697    }
1698
1699    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
1700    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1701        let data = self.data;
1702        let offsets = self.backtrack_coverage_offsets();
1703        ArrayOfOffsets::new(offsets, data, ())
1704    }
1705
1706    /// Number of glyphs in lookahead sequence.
1707    pub fn lookahead_glyph_count(&self) -> u16 {
1708        let range = self.lookahead_glyph_count_byte_range();
1709        self.data.read_at(range.start).ok().unwrap_or_default()
1710    }
1711
1712    /// Array of offsets to coverage tables in lookahead sequence, in
1713    /// glyph sequence order.
1714    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1715        let range = self.lookahead_coverage_offsets_byte_range();
1716        self.data.read_array(range).ok().unwrap_or_default()
1717    }
1718
1719    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
1720    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1721        let data = self.data;
1722        let offsets = self.lookahead_coverage_offsets();
1723        ArrayOfOffsets::new(offsets, data, ())
1724    }
1725
1726    /// Number of glyph IDs in the substituteGlyphIDs array.
1727    pub fn glyph_count(&self) -> u16 {
1728        let range = self.glyph_count_byte_range();
1729        self.data.read_at(range.start).ok().unwrap_or_default()
1730    }
1731
1732    /// Array of substitute glyph IDs — ordered by Coverage index.
1733    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1734        let range = self.substitute_glyph_ids_byte_range();
1735        self.data.read_array(range).ok().unwrap_or_default()
1736    }
1737
1738    pub fn subst_format_byte_range(&self) -> Range<usize> {
1739        let start = 0;
1740        start..start + u16::RAW_BYTE_LEN
1741    }
1742
1743    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1744        let start = self.subst_format_byte_range().end;
1745        start..start + Offset16::RAW_BYTE_LEN
1746    }
1747
1748    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
1749        let start = self.coverage_offset_byte_range().end;
1750        start..start + u16::RAW_BYTE_LEN
1751    }
1752
1753    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
1754        let backtrack_glyph_count = self.backtrack_glyph_count();
1755        let start = self.backtrack_glyph_count_byte_range().end;
1756        start
1757            ..start
1758                + (transforms::to_usize(backtrack_glyph_count))
1759                    .saturating_mul(Offset16::RAW_BYTE_LEN)
1760    }
1761
1762    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
1763        let start = self.backtrack_coverage_offsets_byte_range().end;
1764        start..start + u16::RAW_BYTE_LEN
1765    }
1766
1767    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
1768        let lookahead_glyph_count = self.lookahead_glyph_count();
1769        let start = self.lookahead_glyph_count_byte_range().end;
1770        start
1771            ..start
1772                + (transforms::to_usize(lookahead_glyph_count))
1773                    .saturating_mul(Offset16::RAW_BYTE_LEN)
1774    }
1775
1776    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1777        let start = self.lookahead_coverage_offsets_byte_range().end;
1778        start..start + u16::RAW_BYTE_LEN
1779    }
1780
1781    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
1782        let glyph_count = self.glyph_count();
1783        let start = self.glyph_count_byte_range().end;
1784        start..start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN)
1785    }
1786}
1787
1788const _: () = assert!(FontData::default_data_long_enough(
1789    ReverseChainSingleSubstFormat1::MIN_SIZE
1790));
1791
1792impl Default for ReverseChainSingleSubstFormat1<'_> {
1793    fn default() -> Self {
1794        Self {
1795            data: FontData::default_format_1_u16_table_data(),
1796        }
1797    }
1798}
1799
1800#[cfg(feature = "experimental_traverse")]
1801impl<'a> SomeTable<'a> for ReverseChainSingleSubstFormat1<'a> {
1802    fn type_name(&self) -> &str {
1803        "ReverseChainSingleSubstFormat1"
1804    }
1805    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1806        match idx {
1807            0usize => Some(Field::new("subst_format", self.subst_format())),
1808            1usize => Some(Field::new(
1809                "coverage_offset",
1810                FieldType::offset(self.coverage_offset(), self.coverage()),
1811            )),
1812            2usize => Some(Field::new(
1813                "backtrack_glyph_count",
1814                self.backtrack_glyph_count(),
1815            )),
1816            3usize => Some(Field::new(
1817                "backtrack_coverage_offsets",
1818                FieldType::from(self.backtrack_coverages()),
1819            )),
1820            4usize => Some(Field::new(
1821                "lookahead_glyph_count",
1822                self.lookahead_glyph_count(),
1823            )),
1824            5usize => Some(Field::new(
1825                "lookahead_coverage_offsets",
1826                FieldType::from(self.lookahead_coverages()),
1827            )),
1828            6usize => Some(Field::new("glyph_count", self.glyph_count())),
1829            7usize => Some(Field::new(
1830                "substitute_glyph_ids",
1831                self.substitute_glyph_ids(),
1832            )),
1833            _ => None,
1834        }
1835    }
1836}
1837
1838#[cfg(feature = "experimental_traverse")]
1839#[allow(clippy::needless_lifetimes)]
1840impl<'a> std::fmt::Debug for ReverseChainSingleSubstFormat1<'a> {
1841    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1842        (self as &dyn SomeTable<'a>).fmt(f)
1843    }
1844}