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