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