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