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