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 + (glyph_count as usize).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 + (sequence_count as usize).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 + (glyph_count as usize).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..start + (alternate_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
917    }
918}
919
920const _: () = assert!(FontData::default_data_long_enough(
921    AlternateSubstFormat1::MIN_SIZE
922));
923
924impl Default for AlternateSubstFormat1<'_> {
925    fn default() -> Self {
926        Self {
927            data: FontData::default_format_1_u16_table_data(),
928        }
929    }
930}
931
932#[cfg(feature = "experimental_traverse")]
933impl<'a> SomeTable<'a> for AlternateSubstFormat1<'a> {
934    fn type_name(&self) -> &str {
935        "AlternateSubstFormat1"
936    }
937    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
938        match idx {
939            0usize => Some(Field::new("subst_format", self.subst_format())),
940            1usize => Some(Field::new(
941                "coverage_offset",
942                FieldType::offset(self.coverage_offset(), self.coverage()),
943            )),
944            2usize => Some(Field::new(
945                "alternate_set_count",
946                self.alternate_set_count(),
947            )),
948            3usize => Some(Field::new(
949                "alternate_set_offsets",
950                FieldType::from(self.alternate_sets()),
951            )),
952            _ => None,
953        }
954    }
955}
956
957#[cfg(feature = "experimental_traverse")]
958#[allow(clippy::needless_lifetimes)]
959impl<'a> std::fmt::Debug for AlternateSubstFormat1<'a> {
960    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961        (self as &dyn SomeTable<'a>).fmt(f)
962    }
963}
964
965impl<'a> MinByteRange<'a> for AlternateSet<'a> {
966    fn min_byte_range(&self) -> Range<usize> {
967        0..self.alternate_glyph_ids_byte_range().end
968    }
969    fn min_table_bytes(&self) -> &'a [u8] {
970        let range = self.min_byte_range();
971        self.data.as_bytes().get(range).unwrap_or_default()
972    }
973}
974
975impl<'a> FontRead<'a> for AlternateSet<'a> {
976    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
977        #[allow(clippy::absurd_extreme_comparisons)]
978        if data.len() < Self::MIN_SIZE {
979            return Err(ReadError::OutOfBounds);
980        }
981        Ok(Self { data })
982    }
983}
984
985/// Part of [AlternateSubstFormat1]
986#[derive(Clone)]
987pub struct AlternateSet<'a> {
988    data: FontData<'a>,
989}
990
991#[allow(clippy::needless_lifetimes)]
992impl<'a> AlternateSet<'a> {
993    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
994    basic_table_impls!(impl_the_methods);
995
996    /// Number of glyph IDs in the alternateGlyphIDs array
997    pub fn glyph_count(&self) -> u16 {
998        let range = self.glyph_count_byte_range();
999        self.data.read_at(range.start).ok().unwrap()
1000    }
1001
1002    /// Array of alternate glyph IDs, in arbitrary order
1003    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1004        let range = self.alternate_glyph_ids_byte_range();
1005        self.data.read_array(range).ok().unwrap_or_default()
1006    }
1007
1008    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1009        let start = 0;
1010        start..start + u16::RAW_BYTE_LEN
1011    }
1012
1013    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
1014        let glyph_count = self.glyph_count();
1015        let start = self.glyph_count_byte_range().end;
1016        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
1017    }
1018}
1019
1020const _: () = assert!(FontData::default_data_long_enough(AlternateSet::MIN_SIZE));
1021
1022impl Default for AlternateSet<'_> {
1023    fn default() -> Self {
1024        Self {
1025            data: FontData::default_table_data(),
1026        }
1027    }
1028}
1029
1030#[cfg(feature = "experimental_traverse")]
1031impl<'a> SomeTable<'a> for AlternateSet<'a> {
1032    fn type_name(&self) -> &str {
1033        "AlternateSet"
1034    }
1035    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1036        match idx {
1037            0usize => Some(Field::new("glyph_count", self.glyph_count())),
1038            1usize => Some(Field::new(
1039                "alternate_glyph_ids",
1040                self.alternate_glyph_ids(),
1041            )),
1042            _ => None,
1043        }
1044    }
1045}
1046
1047#[cfg(feature = "experimental_traverse")]
1048#[allow(clippy::needless_lifetimes)]
1049impl<'a> std::fmt::Debug for AlternateSet<'a> {
1050    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1051        (self as &dyn SomeTable<'a>).fmt(f)
1052    }
1053}
1054
1055impl Format<u16> for LigatureSubstFormat1<'_> {
1056    const FORMAT: u16 = 1;
1057}
1058
1059impl<'a> MinByteRange<'a> for LigatureSubstFormat1<'a> {
1060    fn min_byte_range(&self) -> Range<usize> {
1061        0..self.ligature_set_offsets_byte_range().end
1062    }
1063    fn min_table_bytes(&self) -> &'a [u8] {
1064        let range = self.min_byte_range();
1065        self.data.as_bytes().get(range).unwrap_or_default()
1066    }
1067}
1068
1069impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
1070    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1071        #[allow(clippy::absurd_extreme_comparisons)]
1072        if data.len() < Self::MIN_SIZE {
1073            return Err(ReadError::OutOfBounds);
1074        }
1075        Ok(Self { data })
1076    }
1077}
1078
1079/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
1080#[derive(Clone)]
1081pub struct LigatureSubstFormat1<'a> {
1082    data: FontData<'a>,
1083}
1084
1085#[allow(clippy::needless_lifetimes)]
1086impl<'a> LigatureSubstFormat1<'a> {
1087    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1088    basic_table_impls!(impl_the_methods);
1089
1090    /// Format identifier: format = 1
1091    pub fn subst_format(&self) -> u16 {
1092        let range = self.subst_format_byte_range();
1093        self.data.read_at(range.start).ok().unwrap()
1094    }
1095
1096    /// Offset to Coverage table, from beginning of substitution
1097    /// subtable
1098    pub fn coverage_offset(&self) -> Offset16 {
1099        let range = self.coverage_offset_byte_range();
1100        self.data.read_at(range.start).ok().unwrap()
1101    }
1102
1103    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1104    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1105        let data = self.data;
1106        self.coverage_offset().resolve(data)
1107    }
1108
1109    /// Number of LigatureSet tables
1110    pub fn ligature_set_count(&self) -> u16 {
1111        let range = self.ligature_set_count_byte_range();
1112        self.data.read_at(range.start).ok().unwrap()
1113    }
1114
1115    /// Array of offsets to LigatureSet tables. Offsets are from
1116    /// beginning of substitution subtable, ordered by Coverage index
1117    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
1118        let range = self.ligature_set_offsets_byte_range();
1119        self.data.read_array(range).ok().unwrap_or_default()
1120    }
1121
1122    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
1123    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
1124        let data = self.data;
1125        let offsets = self.ligature_set_offsets();
1126        ArrayOfOffsets::new(offsets, data, ())
1127    }
1128
1129    pub fn subst_format_byte_range(&self) -> Range<usize> {
1130        let start = 0;
1131        start..start + u16::RAW_BYTE_LEN
1132    }
1133
1134    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1135        let start = self.subst_format_byte_range().end;
1136        start..start + Offset16::RAW_BYTE_LEN
1137    }
1138
1139    pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
1140        let start = self.coverage_offset_byte_range().end;
1141        start..start + u16::RAW_BYTE_LEN
1142    }
1143
1144    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
1145        let ligature_set_count = self.ligature_set_count();
1146        let start = self.ligature_set_count_byte_range().end;
1147        start..start + (ligature_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
1148    }
1149}
1150
1151const _: () = assert!(FontData::default_data_long_enough(
1152    LigatureSubstFormat1::MIN_SIZE
1153));
1154
1155impl Default for LigatureSubstFormat1<'_> {
1156    fn default() -> Self {
1157        Self {
1158            data: FontData::default_format_1_u16_table_data(),
1159        }
1160    }
1161}
1162
1163#[cfg(feature = "experimental_traverse")]
1164impl<'a> SomeTable<'a> for LigatureSubstFormat1<'a> {
1165    fn type_name(&self) -> &str {
1166        "LigatureSubstFormat1"
1167    }
1168    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1169        match idx {
1170            0usize => Some(Field::new("subst_format", self.subst_format())),
1171            1usize => Some(Field::new(
1172                "coverage_offset",
1173                FieldType::offset(self.coverage_offset(), self.coverage()),
1174            )),
1175            2usize => Some(Field::new("ligature_set_count", self.ligature_set_count())),
1176            3usize => Some(Field::new(
1177                "ligature_set_offsets",
1178                FieldType::from(self.ligature_sets()),
1179            )),
1180            _ => None,
1181        }
1182    }
1183}
1184
1185#[cfg(feature = "experimental_traverse")]
1186#[allow(clippy::needless_lifetimes)]
1187impl<'a> std::fmt::Debug for LigatureSubstFormat1<'a> {
1188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1189        (self as &dyn SomeTable<'a>).fmt(f)
1190    }
1191}
1192
1193impl<'a> MinByteRange<'a> for LigatureSet<'a> {
1194    fn min_byte_range(&self) -> Range<usize> {
1195        0..self.ligature_offsets_byte_range().end
1196    }
1197    fn min_table_bytes(&self) -> &'a [u8] {
1198        let range = self.min_byte_range();
1199        self.data.as_bytes().get(range).unwrap_or_default()
1200    }
1201}
1202
1203impl<'a> FontRead<'a> for LigatureSet<'a> {
1204    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1205        #[allow(clippy::absurd_extreme_comparisons)]
1206        if data.len() < Self::MIN_SIZE {
1207            return Err(ReadError::OutOfBounds);
1208        }
1209        Ok(Self { data })
1210    }
1211}
1212
1213/// Part of [LigatureSubstFormat1]
1214#[derive(Clone)]
1215pub struct LigatureSet<'a> {
1216    data: FontData<'a>,
1217}
1218
1219#[allow(clippy::needless_lifetimes)]
1220impl<'a> LigatureSet<'a> {
1221    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1222    basic_table_impls!(impl_the_methods);
1223
1224    /// Number of Ligature tables
1225    pub fn ligature_count(&self) -> u16 {
1226        let range = self.ligature_count_byte_range();
1227        self.data.read_at(range.start).ok().unwrap()
1228    }
1229
1230    /// Array of offsets to Ligature tables. Offsets are from beginning
1231    /// of LigatureSet table, ordered by preference.
1232    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
1233        let range = self.ligature_offsets_byte_range();
1234        self.data.read_array(range).ok().unwrap_or_default()
1235    }
1236
1237    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
1238    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
1239        let data = self.data;
1240        let offsets = self.ligature_offsets();
1241        ArrayOfOffsets::new(offsets, data, ())
1242    }
1243
1244    pub fn ligature_count_byte_range(&self) -> Range<usize> {
1245        let start = 0;
1246        start..start + u16::RAW_BYTE_LEN
1247    }
1248
1249    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
1250        let ligature_count = self.ligature_count();
1251        let start = self.ligature_count_byte_range().end;
1252        start..start + (ligature_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
1253    }
1254}
1255
1256const _: () = assert!(FontData::default_data_long_enough(LigatureSet::MIN_SIZE));
1257
1258impl Default for LigatureSet<'_> {
1259    fn default() -> Self {
1260        Self {
1261            data: FontData::default_table_data(),
1262        }
1263    }
1264}
1265
1266#[cfg(feature = "experimental_traverse")]
1267impl<'a> SomeTable<'a> for LigatureSet<'a> {
1268    fn type_name(&self) -> &str {
1269        "LigatureSet"
1270    }
1271    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1272        match idx {
1273            0usize => Some(Field::new("ligature_count", self.ligature_count())),
1274            1usize => Some(Field::new(
1275                "ligature_offsets",
1276                FieldType::from(self.ligatures()),
1277            )),
1278            _ => None,
1279        }
1280    }
1281}
1282
1283#[cfg(feature = "experimental_traverse")]
1284#[allow(clippy::needless_lifetimes)]
1285impl<'a> std::fmt::Debug for LigatureSet<'a> {
1286    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1287        (self as &dyn SomeTable<'a>).fmt(f)
1288    }
1289}
1290
1291impl<'a> MinByteRange<'a> for Ligature<'a> {
1292    fn min_byte_range(&self) -> Range<usize> {
1293        0..self.component_glyph_ids_byte_range().end
1294    }
1295    fn min_table_bytes(&self) -> &'a [u8] {
1296        let range = self.min_byte_range();
1297        self.data.as_bytes().get(range).unwrap_or_default()
1298    }
1299}
1300
1301impl<'a> FontRead<'a> for Ligature<'a> {
1302    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1303        #[allow(clippy::absurd_extreme_comparisons)]
1304        if data.len() < Self::MIN_SIZE {
1305            return Err(ReadError::OutOfBounds);
1306        }
1307        Ok(Self { data })
1308    }
1309}
1310
1311/// Part of [LigatureSubstFormat1]
1312#[derive(Clone)]
1313pub struct Ligature<'a> {
1314    data: FontData<'a>,
1315}
1316
1317#[allow(clippy::needless_lifetimes)]
1318impl<'a> Ligature<'a> {
1319    pub const MIN_SIZE: usize = (GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1320    basic_table_impls!(impl_the_methods);
1321
1322    /// glyph ID of ligature to substitute
1323    pub fn ligature_glyph(&self) -> GlyphId16 {
1324        let range = self.ligature_glyph_byte_range();
1325        self.data.read_at(range.start).ok().unwrap()
1326    }
1327
1328    /// Number of components in the ligature
1329    pub fn component_count(&self) -> u16 {
1330        let range = self.component_count_byte_range();
1331        self.data.read_at(range.start).ok().unwrap()
1332    }
1333
1334    /// Array of component glyph IDs — start with the second
1335    /// component, ordered in writing direction
1336    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1337        let range = self.component_glyph_ids_byte_range();
1338        self.data.read_array(range).ok().unwrap_or_default()
1339    }
1340
1341    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
1342        let start = 0;
1343        start..start + GlyphId16::RAW_BYTE_LEN
1344    }
1345
1346    pub fn component_count_byte_range(&self) -> Range<usize> {
1347        let start = self.ligature_glyph_byte_range().end;
1348        start..start + u16::RAW_BYTE_LEN
1349    }
1350
1351    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
1352        let component_count = self.component_count();
1353        let start = self.component_count_byte_range().end;
1354        start
1355            ..start
1356                + (transforms::subtract(component_count, 1_usize))
1357                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
1358    }
1359}
1360
1361const _: () = assert!(FontData::default_data_long_enough(Ligature::MIN_SIZE));
1362
1363impl Default for Ligature<'_> {
1364    fn default() -> Self {
1365        Self {
1366            data: FontData::default_table_data(),
1367        }
1368    }
1369}
1370
1371#[cfg(feature = "experimental_traverse")]
1372impl<'a> SomeTable<'a> for Ligature<'a> {
1373    fn type_name(&self) -> &str {
1374        "Ligature"
1375    }
1376    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1377        match idx {
1378            0usize => Some(Field::new("ligature_glyph", self.ligature_glyph())),
1379            1usize => Some(Field::new("component_count", self.component_count())),
1380            2usize => Some(Field::new(
1381                "component_glyph_ids",
1382                self.component_glyph_ids(),
1383            )),
1384            _ => None,
1385        }
1386    }
1387}
1388
1389#[cfg(feature = "experimental_traverse")]
1390#[allow(clippy::needless_lifetimes)]
1391impl<'a> std::fmt::Debug for Ligature<'a> {
1392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1393        (self as &dyn SomeTable<'a>).fmt(f)
1394    }
1395}
1396
1397impl Format<u16> for ExtensionSubstFormat1<'_> {
1398    const FORMAT: u16 = 1;
1399}
1400
1401impl Discriminant for ExtensionSubstFormat1<'_, ()> {
1402    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
1403        data.read_at(u16::RAW_BYTE_LEN)
1404    }
1405}
1406
1407impl<'a, T> MinByteRange<'a> for ExtensionSubstFormat1<'a, T> {
1408    fn min_byte_range(&self) -> Range<usize> {
1409        0..self.extension_offset_byte_range().end
1410    }
1411    fn min_table_bytes(&self) -> &'a [u8] {
1412        let range = self.min_byte_range();
1413        self.data.as_bytes().get(range).unwrap_or_default()
1414    }
1415}
1416
1417impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
1418    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1419        #[allow(clippy::absurd_extreme_comparisons)]
1420        if data.len() < Self::MIN_SIZE {
1421            return Err(ReadError::OutOfBounds);
1422        }
1423        Ok(Self {
1424            data,
1425            offset_type: std::marker::PhantomData,
1426        })
1427    }
1428}
1429
1430impl<'a, T> ExtensionSubstFormat1<'a, T> {
1431    #[allow(dead_code)]
1432    /// Replace the specific generic type on this implementation with `()`
1433    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1434        ExtensionSubstFormat1 {
1435            data: self.data,
1436            offset_type: std::marker::PhantomData,
1437        }
1438    }
1439}
1440
1441/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
1442#[derive(Clone)]
1443pub struct ExtensionSubstFormat1<'a, T = ()> {
1444    data: FontData<'a>,
1445    offset_type: std::marker::PhantomData<*const T>,
1446}
1447
1448#[allow(clippy::needless_lifetimes)]
1449impl<'a, T> ExtensionSubstFormat1<'a, T> {
1450    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
1451    basic_table_impls!(impl_the_methods);
1452
1453    /// Format identifier. Set to 1.
1454    pub fn subst_format(&self) -> u16 {
1455        let range = self.subst_format_byte_range();
1456        self.data.read_at(range.start).ok().unwrap()
1457    }
1458
1459    /// Lookup type of subtable referenced by extensionOffset (that is,
1460    /// the extension subtable).
1461    pub fn extension_lookup_type(&self) -> u16 {
1462        let range = self.extension_lookup_type_byte_range();
1463        self.data.read_at(range.start).ok().unwrap()
1464    }
1465
1466    /// Offset to the extension subtable, of lookup type
1467    /// extensionLookupType, relative to the start of the
1468    /// ExtensionSubstFormat1 subtable.
1469    pub fn extension_offset(&self) -> Offset32 {
1470        let range = self.extension_offset_byte_range();
1471        self.data.read_at(range.start).ok().unwrap()
1472    }
1473
1474    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
1475    pub fn extension(&self) -> Result<T, ReadError>
1476    where
1477        T: FontRead<'a>,
1478    {
1479        let data = self.data;
1480        self.extension_offset().resolve(data)
1481    }
1482
1483    pub fn subst_format_byte_range(&self) -> Range<usize> {
1484        let start = 0;
1485        start..start + u16::RAW_BYTE_LEN
1486    }
1487
1488    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
1489        let start = self.subst_format_byte_range().end;
1490        start..start + u16::RAW_BYTE_LEN
1491    }
1492
1493    pub fn extension_offset_byte_range(&self) -> Range<usize> {
1494        let start = self.extension_lookup_type_byte_range().end;
1495        start..start + Offset32::RAW_BYTE_LEN
1496    }
1497}
1498
1499const _: () = assert!(FontData::default_data_long_enough(
1500    ExtensionSubstFormat1::<()>::MIN_SIZE
1501));
1502
1503impl<T> Default for ExtensionSubstFormat1<'_, T> {
1504    fn default() -> Self {
1505        Self {
1506            data: FontData::default_format_1_u16_table_data(),
1507            offset_type: std::marker::PhantomData,
1508        }
1509    }
1510}
1511
1512#[cfg(feature = "experimental_traverse")]
1513impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionSubstFormat1<'a, T> {
1514    fn type_name(&self) -> &str {
1515        "ExtensionSubstFormat1"
1516    }
1517    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1518        match idx {
1519            0usize => Some(Field::new("subst_format", self.subst_format())),
1520            1usize => Some(Field::new(
1521                "extension_lookup_type",
1522                self.extension_lookup_type(),
1523            )),
1524            2usize => Some(Field::new(
1525                "extension_offset",
1526                FieldType::offset(self.extension_offset(), self.extension()),
1527            )),
1528            _ => None,
1529        }
1530    }
1531}
1532
1533#[cfg(feature = "experimental_traverse")]
1534#[allow(clippy::needless_lifetimes)]
1535impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for ExtensionSubstFormat1<'a, T> {
1536    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1537        (self as &dyn SomeTable<'a>).fmt(f)
1538    }
1539}
1540
1541/// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable
1542pub enum ExtensionSubtable<'a> {
1543    Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
1544    Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>),
1545    Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>),
1546    Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>),
1547    Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
1548    ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
1549    Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>),
1550}
1551
1552impl Default for ExtensionSubtable<'_> {
1553    fn default() -> Self {
1554        Self::Single(Default::default())
1555    }
1556}
1557
1558impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
1559    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
1560        let discriminant = ExtensionSubstFormat1::read_discriminant(bytes)?;
1561        match discriminant {
1562            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
1563            2 => Ok(ExtensionSubtable::Multiple(FontRead::read(bytes)?)),
1564            3 => Ok(ExtensionSubtable::Alternate(FontRead::read(bytes)?)),
1565            4 => Ok(ExtensionSubtable::Ligature(FontRead::read(bytes)?)),
1566            5 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
1567            6 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
1568            8 => Ok(ExtensionSubtable::Reverse(FontRead::read(bytes)?)),
1569            other => Err(ReadError::InvalidFormat(other.into())),
1570        }
1571    }
1572}
1573
1574impl<'a> ExtensionSubtable<'a> {
1575    #[allow(dead_code)]
1576    /// Return the inner table, removing the specific generics.
1577    ///
1578    /// This lets us return a single concrete type we can call methods on.
1579    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1580        match self {
1581            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
1582            ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
1583            ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
1584            ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
1585            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
1586            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
1587            ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
1588        }
1589    }
1590}
1591
1592#[cfg(feature = "experimental_traverse")]
1593impl<'a> ExtensionSubtable<'a> {
1594    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
1595        match self {
1596            ExtensionSubtable::Single(table) => table,
1597            ExtensionSubtable::Multiple(table) => table,
1598            ExtensionSubtable::Alternate(table) => table,
1599            ExtensionSubtable::Ligature(table) => table,
1600            ExtensionSubtable::Contextual(table) => table,
1601            ExtensionSubtable::ChainContextual(table) => table,
1602            ExtensionSubtable::Reverse(table) => table,
1603        }
1604    }
1605}
1606
1607#[cfg(feature = "experimental_traverse")]
1608impl<'a> SomeTable<'a> for ExtensionSubtable<'a> {
1609    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1610        self.dyn_inner().get_field(idx)
1611    }
1612    fn type_name(&self) -> &str {
1613        self.dyn_inner().type_name()
1614    }
1615}
1616
1617#[cfg(feature = "experimental_traverse")]
1618impl std::fmt::Debug for ExtensionSubtable<'_> {
1619    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1620        self.dyn_inner().fmt(f)
1621    }
1622}
1623
1624impl Format<u16> for ReverseChainSingleSubstFormat1<'_> {
1625    const FORMAT: u16 = 1;
1626}
1627
1628impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat1<'a> {
1629    fn min_byte_range(&self) -> Range<usize> {
1630        0..self.substitute_glyph_ids_byte_range().end
1631    }
1632    fn min_table_bytes(&self) -> &'a [u8] {
1633        let range = self.min_byte_range();
1634        self.data.as_bytes().get(range).unwrap_or_default()
1635    }
1636}
1637
1638impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
1639    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1640        #[allow(clippy::absurd_extreme_comparisons)]
1641        if data.len() < Self::MIN_SIZE {
1642            return Err(ReadError::OutOfBounds);
1643        }
1644        Ok(Self { data })
1645    }
1646}
1647
1648/// [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)
1649#[derive(Clone)]
1650pub struct ReverseChainSingleSubstFormat1<'a> {
1651    data: FontData<'a>,
1652}
1653
1654#[allow(clippy::needless_lifetimes)]
1655impl<'a> ReverseChainSingleSubstFormat1<'a> {
1656    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1657        + Offset16::RAW_BYTE_LEN
1658        + u16::RAW_BYTE_LEN
1659        + u16::RAW_BYTE_LEN
1660        + u16::RAW_BYTE_LEN);
1661    basic_table_impls!(impl_the_methods);
1662
1663    /// Format identifier: format = 1
1664    pub fn subst_format(&self) -> u16 {
1665        let range = self.subst_format_byte_range();
1666        self.data.read_at(range.start).ok().unwrap()
1667    }
1668
1669    /// Offset to Coverage table, from beginning of substitution
1670    /// subtable.
1671    pub fn coverage_offset(&self) -> Offset16 {
1672        let range = self.coverage_offset_byte_range();
1673        self.data.read_at(range.start).ok().unwrap()
1674    }
1675
1676    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1677    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1678        let data = self.data;
1679        self.coverage_offset().resolve(data)
1680    }
1681
1682    /// Number of glyphs in the backtrack sequence.
1683    pub fn backtrack_glyph_count(&self) -> u16 {
1684        let range = self.backtrack_glyph_count_byte_range();
1685        self.data.read_at(range.start).ok().unwrap()
1686    }
1687
1688    /// Array of offsets to coverage tables in backtrack sequence, in
1689    /// glyph sequence order.
1690    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1691        let range = self.backtrack_coverage_offsets_byte_range();
1692        self.data.read_array(range).ok().unwrap_or_default()
1693    }
1694
1695    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
1696    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1697        let data = self.data;
1698        let offsets = self.backtrack_coverage_offsets();
1699        ArrayOfOffsets::new(offsets, data, ())
1700    }
1701
1702    /// Number of glyphs in lookahead sequence.
1703    pub fn lookahead_glyph_count(&self) -> u16 {
1704        let range = self.lookahead_glyph_count_byte_range();
1705        self.data.read_at(range.start).ok().unwrap_or_default()
1706    }
1707
1708    /// Array of offsets to coverage tables in lookahead sequence, in
1709    /// glyph sequence order.
1710    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1711        let range = self.lookahead_coverage_offsets_byte_range();
1712        self.data.read_array(range).ok().unwrap_or_default()
1713    }
1714
1715    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
1716    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1717        let data = self.data;
1718        let offsets = self.lookahead_coverage_offsets();
1719        ArrayOfOffsets::new(offsets, data, ())
1720    }
1721
1722    /// Number of glyph IDs in the substituteGlyphIDs array.
1723    pub fn glyph_count(&self) -> u16 {
1724        let range = self.glyph_count_byte_range();
1725        self.data.read_at(range.start).ok().unwrap_or_default()
1726    }
1727
1728    /// Array of substitute glyph IDs — ordered by Coverage index.
1729    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1730        let range = self.substitute_glyph_ids_byte_range();
1731        self.data.read_array(range).ok().unwrap_or_default()
1732    }
1733
1734    pub fn subst_format_byte_range(&self) -> Range<usize> {
1735        let start = 0;
1736        start..start + u16::RAW_BYTE_LEN
1737    }
1738
1739    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1740        let start = self.subst_format_byte_range().end;
1741        start..start + Offset16::RAW_BYTE_LEN
1742    }
1743
1744    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
1745        let start = self.coverage_offset_byte_range().end;
1746        start..start + u16::RAW_BYTE_LEN
1747    }
1748
1749    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
1750        let backtrack_glyph_count = self.backtrack_glyph_count();
1751        let start = self.backtrack_glyph_count_byte_range().end;
1752        start..start + (backtrack_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
1753    }
1754
1755    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
1756        let start = self.backtrack_coverage_offsets_byte_range().end;
1757        start..start + u16::RAW_BYTE_LEN
1758    }
1759
1760    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
1761        let lookahead_glyph_count = self.lookahead_glyph_count();
1762        let start = self.lookahead_glyph_count_byte_range().end;
1763        start..start + (lookahead_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
1764    }
1765
1766    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1767        let start = self.lookahead_coverage_offsets_byte_range().end;
1768        start..start + u16::RAW_BYTE_LEN
1769    }
1770
1771    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
1772        let glyph_count = self.glyph_count();
1773        let start = self.glyph_count_byte_range().end;
1774        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
1775    }
1776}
1777
1778const _: () = assert!(FontData::default_data_long_enough(
1779    ReverseChainSingleSubstFormat1::MIN_SIZE
1780));
1781
1782impl Default for ReverseChainSingleSubstFormat1<'_> {
1783    fn default() -> Self {
1784        Self {
1785            data: FontData::default_format_1_u16_table_data(),
1786        }
1787    }
1788}
1789
1790#[cfg(feature = "experimental_traverse")]
1791impl<'a> SomeTable<'a> for ReverseChainSingleSubstFormat1<'a> {
1792    fn type_name(&self) -> &str {
1793        "ReverseChainSingleSubstFormat1"
1794    }
1795    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
1796        match idx {
1797            0usize => Some(Field::new("subst_format", self.subst_format())),
1798            1usize => Some(Field::new(
1799                "coverage_offset",
1800                FieldType::offset(self.coverage_offset(), self.coverage()),
1801            )),
1802            2usize => Some(Field::new(
1803                "backtrack_glyph_count",
1804                self.backtrack_glyph_count(),
1805            )),
1806            3usize => Some(Field::new(
1807                "backtrack_coverage_offsets",
1808                FieldType::from(self.backtrack_coverages()),
1809            )),
1810            4usize => Some(Field::new(
1811                "lookahead_glyph_count",
1812                self.lookahead_glyph_count(),
1813            )),
1814            5usize => Some(Field::new(
1815                "lookahead_coverage_offsets",
1816                FieldType::from(self.lookahead_coverages()),
1817            )),
1818            6usize => Some(Field::new("glyph_count", self.glyph_count())),
1819            7usize => Some(Field::new(
1820                "substitute_glyph_ids",
1821                self.substitute_glyph_ids(),
1822            )),
1823            _ => None,
1824        }
1825    }
1826}
1827
1828#[cfg(feature = "experimental_traverse")]
1829#[allow(clippy::needless_lifetimes)]
1830impl<'a> std::fmt::Debug for ReverseChainSingleSubstFormat1<'a> {
1831    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1832        (self as &dyn SomeTable<'a>).fmt(f)
1833    }
1834}