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/// A [GSUB Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsubLookupTypeEnum) subtable.
154pub enum SubstitutionLookup<'a> {
155    Single(Lookup<'a, SingleSubst<'a>>),
156    Multiple(Lookup<'a, MultipleSubstFormat1<'a>>),
157    Alternate(Lookup<'a, AlternateSubstFormat1<'a>>),
158    Ligature(Lookup<'a, LigatureSubstFormat1<'a>>),
159    Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>),
160    ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>),
161    Extension(Lookup<'a, ExtensionSubtable<'a>>),
162    Reverse(Lookup<'a, ReverseChainSingleSubstFormat1<'a>>),
163}
164
165impl Default for SubstitutionLookup<'_> {
166    fn default() -> Self {
167        Self::Single(Default::default())
168    }
169}
170
171impl ReadArgs for SubstitutionLookup<'_> {
172    type Args = ();
173}
174
175impl<'a> FontRead<'a> for SubstitutionLookup<'a> {
176    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
177        let discriminant = Lookup::read_discriminant(bytes)?;
178        match discriminant {
179            1 => Ok(SubstitutionLookup::Single(FontRead::read(bytes)?)),
180            2 => Ok(SubstitutionLookup::Multiple(FontRead::read(bytes)?)),
181            3 => Ok(SubstitutionLookup::Alternate(FontRead::read(bytes)?)),
182            4 => Ok(SubstitutionLookup::Ligature(FontRead::read(bytes)?)),
183            5 => Ok(SubstitutionLookup::Contextual(FontRead::read(bytes)?)),
184            6 => Ok(SubstitutionLookup::ChainContextual(FontRead::read(bytes)?)),
185            7 => Ok(SubstitutionLookup::Extension(FontRead::read(bytes)?)),
186            8 => Ok(SubstitutionLookup::Reverse(FontRead::read(bytes)?)),
187            other => Err(ReadError::InvalidFormat(other.into())),
188        }
189    }
190}
191
192impl<'a> SubstitutionLookup<'a> {
193    #[allow(dead_code)]
194    /// Return the inner table, removing the specific generics.
195    ///
196    /// This lets us return a single concrete type we can call methods on.
197    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
198        match self {
199            SubstitutionLookup::Single(inner) => inner.of_unit_type(),
200            SubstitutionLookup::Multiple(inner) => inner.of_unit_type(),
201            SubstitutionLookup::Alternate(inner) => inner.of_unit_type(),
202            SubstitutionLookup::Ligature(inner) => inner.of_unit_type(),
203            SubstitutionLookup::Contextual(inner) => inner.of_unit_type(),
204            SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(),
205            SubstitutionLookup::Extension(inner) => inner.of_unit_type(),
206            SubstitutionLookup::Reverse(inner) => inner.of_unit_type(),
207        }
208    }
209}
210
211/// LookupType 1: [Single Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable) Subtable
212#[derive(Clone)]
213pub enum SingleSubst<'a> {
214    Format1(SingleSubstFormat1<'a>),
215    Format2(SingleSubstFormat2<'a>),
216}
217
218impl Default for SingleSubst<'_> {
219    fn default() -> Self {
220        Self::Format1(Default::default())
221    }
222}
223
224impl<'a> SingleSubst<'a> {
225    ///Return the `FontData` used to resolve offsets for this table.
226    pub fn offset_data(&self) -> FontData<'a> {
227        match self {
228            Self::Format1(item) => item.offset_data(),
229            Self::Format2(item) => item.offset_data(),
230        }
231    }
232
233    /// Format identifier: format = 1
234    pub fn subst_format(&self) -> u16 {
235        match self {
236            Self::Format1(item) => item.subst_format(),
237            Self::Format2(item) => item.subst_format(),
238        }
239    }
240
241    /// Offset to Coverage table, from beginning of substitution
242    /// subtable
243    pub fn coverage_offset(&self) -> Offset16 {
244        match self {
245            Self::Format1(item) => item.coverage_offset(),
246            Self::Format2(item) => item.coverage_offset(),
247        }
248    }
249}
250
251impl ReadArgs for SingleSubst<'_> {
252    type Args = ();
253}
254
255impl<'a> FontRead<'a> for SingleSubst<'a> {
256    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
257        let format: u16 = data.read_at(0usize)?;
258        match format {
259            SingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
260            SingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
261            other => Err(ReadError::InvalidFormat(other.into())),
262        }
263    }
264}
265
266impl<'a> MinByteRange<'a> for SingleSubst<'a> {
267    fn min_byte_range(&self) -> Range<usize> {
268        match self {
269            Self::Format1(item) => item.min_byte_range(),
270            Self::Format2(item) => item.min_byte_range(),
271        }
272    }
273    fn min_table_bytes(&self) -> &'a [u8] {
274        match self {
275            Self::Format1(item) => item.min_table_bytes(),
276            Self::Format2(item) => item.min_table_bytes(),
277        }
278    }
279}
280
281impl Format<u16> for SingleSubstFormat1<'_> {
282    const FORMAT: u16 = 1;
283}
284
285impl<'a> MinByteRange<'a> for SingleSubstFormat1<'a> {
286    fn min_byte_range(&self) -> Range<usize> {
287        0..self.delta_glyph_id_byte_range().end
288    }
289    fn min_table_bytes(&self) -> &'a [u8] {
290        let range = self.min_byte_range();
291        self.data.as_bytes().get(range).unwrap_or_default()
292    }
293}
294
295impl ReadArgs for SingleSubstFormat1<'_> {
296    type Args = ();
297}
298
299impl<'a> FontRead<'a> for SingleSubstFormat1<'a> {
300    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
301        #[allow(clippy::absurd_extreme_comparisons)]
302        if data.len() < Self::MIN_SIZE {
303            return Err(ReadError::OutOfBounds);
304        }
305        Ok(Self { data })
306    }
307}
308
309/// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1)
310#[derive(Clone)]
311pub struct SingleSubstFormat1<'a> {
312    data: FontData<'a>,
313}
314
315#[allow(clippy::needless_lifetimes)]
316impl<'a> SingleSubstFormat1<'a> {
317    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
318    basic_table_impls!(impl_the_methods);
319
320    /// Format identifier: format = 1
321    pub fn subst_format(&self) -> u16 {
322        let range = self.subst_format_byte_range();
323        self.data.read_at(range.start).ok().unwrap()
324    }
325
326    /// Offset to Coverage table, from beginning of substitution
327    /// subtable
328    pub fn coverage_offset(&self) -> Offset16 {
329        let range = self.coverage_offset_byte_range();
330        self.data.read_at(range.start).ok().unwrap()
331    }
332
333    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
334    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
335        let data = self.data;
336        self.coverage_offset().resolve(data)
337    }
338
339    /// Add to original glyph ID to get substitute glyph ID
340    pub fn delta_glyph_id(&self) -> i16 {
341        let range = self.delta_glyph_id_byte_range();
342        self.data.read_at(range.start).ok().unwrap()
343    }
344
345    pub fn subst_format_byte_range(&self) -> Range<usize> {
346        let start = 0;
347        let end = start + u16::RAW_BYTE_LEN;
348        start..end
349    }
350
351    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
352        let start = self.subst_format_byte_range().end;
353        let end = start + Offset16::RAW_BYTE_LEN;
354        start..end
355    }
356
357    pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
358        let start = self.coverage_offset_byte_range().end;
359        let end = start + i16::RAW_BYTE_LEN;
360        start..end
361    }
362}
363
364const _: () = assert!(FontData::default_data_long_enough(
365    SingleSubstFormat1::MIN_SIZE
366));
367
368impl Default for SingleSubstFormat1<'_> {
369    fn default() -> Self {
370        Self {
371            data: FontData::default_format_1_u16_table_data(),
372        }
373    }
374}
375
376impl Format<u16> for SingleSubstFormat2<'_> {
377    const FORMAT: u16 = 2;
378}
379
380impl<'a> MinByteRange<'a> for SingleSubstFormat2<'a> {
381    fn min_byte_range(&self) -> Range<usize> {
382        0..self.substitute_glyph_ids_byte_range().end
383    }
384    fn min_table_bytes(&self) -> &'a [u8] {
385        let range = self.min_byte_range();
386        self.data.as_bytes().get(range).unwrap_or_default()
387    }
388}
389
390impl ReadArgs for SingleSubstFormat2<'_> {
391    type Args = ();
392}
393
394impl<'a> FontRead<'a> for SingleSubstFormat2<'a> {
395    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
396        #[allow(clippy::absurd_extreme_comparisons)]
397        if data.len() < Self::MIN_SIZE {
398            return Err(ReadError::OutOfBounds);
399        }
400        Ok(Self { data })
401    }
402}
403
404/// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2)
405#[derive(Clone)]
406pub struct SingleSubstFormat2<'a> {
407    data: FontData<'a>,
408}
409
410#[allow(clippy::needless_lifetimes)]
411impl<'a> SingleSubstFormat2<'a> {
412    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
413    basic_table_impls!(impl_the_methods);
414
415    /// Format identifier: format = 2
416    pub fn subst_format(&self) -> u16 {
417        let range = self.subst_format_byte_range();
418        self.data.read_at(range.start).ok().unwrap()
419    }
420
421    /// Offset to Coverage table, from beginning of substitution
422    /// subtable
423    pub fn coverage_offset(&self) -> Offset16 {
424        let range = self.coverage_offset_byte_range();
425        self.data.read_at(range.start).ok().unwrap()
426    }
427
428    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
429    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
430        let data = self.data;
431        self.coverage_offset().resolve(data)
432    }
433
434    /// Number of glyph IDs in the substituteGlyphIDs array
435    pub fn glyph_count(&self) -> u16 {
436        let range = self.glyph_count_byte_range();
437        self.data.read_at(range.start).ok().unwrap()
438    }
439
440    /// Array of substitute glyph IDs — ordered by Coverage index
441    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
442        let range = self.substitute_glyph_ids_byte_range();
443        self.data.read_array(range).ok().unwrap_or_default()
444    }
445
446    pub fn subst_format_byte_range(&self) -> Range<usize> {
447        let start = 0;
448        let end = start + u16::RAW_BYTE_LEN;
449        start..end
450    }
451
452    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
453        let start = self.subst_format_byte_range().end;
454        let end = start + Offset16::RAW_BYTE_LEN;
455        start..end
456    }
457
458    pub fn glyph_count_byte_range(&self) -> Range<usize> {
459        let start = self.coverage_offset_byte_range().end;
460        let end = start + u16::RAW_BYTE_LEN;
461        start..end
462    }
463
464    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
465        let glyph_count = self.glyph_count();
466        let start = self.glyph_count_byte_range().end;
467        let end =
468            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
469        start..end
470    }
471}
472
473impl Format<u16> for MultipleSubstFormat1<'_> {
474    const FORMAT: u16 = 1;
475}
476
477impl<'a> MinByteRange<'a> for MultipleSubstFormat1<'a> {
478    fn min_byte_range(&self) -> Range<usize> {
479        0..self.sequence_offsets_byte_range().end
480    }
481    fn min_table_bytes(&self) -> &'a [u8] {
482        let range = self.min_byte_range();
483        self.data.as_bytes().get(range).unwrap_or_default()
484    }
485}
486
487impl ReadArgs for MultipleSubstFormat1<'_> {
488    type Args = ();
489}
490
491impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> {
492    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
493        #[allow(clippy::absurd_extreme_comparisons)]
494        if data.len() < Self::MIN_SIZE {
495            return Err(ReadError::OutOfBounds);
496        }
497        Ok(Self { data })
498    }
499}
500
501/// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1)
502#[derive(Clone)]
503pub struct MultipleSubstFormat1<'a> {
504    data: FontData<'a>,
505}
506
507#[allow(clippy::needless_lifetimes)]
508impl<'a> MultipleSubstFormat1<'a> {
509    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
510    basic_table_impls!(impl_the_methods);
511
512    /// Format identifier: format = 1
513    pub fn subst_format(&self) -> u16 {
514        let range = self.subst_format_byte_range();
515        self.data.read_at(range.start).ok().unwrap()
516    }
517
518    /// Offset to Coverage table, from beginning of substitution
519    /// subtable
520    pub fn coverage_offset(&self) -> Offset16 {
521        let range = self.coverage_offset_byte_range();
522        self.data.read_at(range.start).ok().unwrap()
523    }
524
525    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
526    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
527        let data = self.data;
528        self.coverage_offset().resolve(data)
529    }
530
531    /// Number of Sequence table offsets in the sequenceOffsets array
532    pub fn sequence_count(&self) -> u16 {
533        let range = self.sequence_count_byte_range();
534        self.data.read_at(range.start).ok().unwrap()
535    }
536
537    /// Array of offsets to Sequence tables. Offsets are from beginning
538    /// of substitution subtable, ordered by Coverage index
539    pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset16>] {
540        let range = self.sequence_offsets_byte_range();
541        self.data.read_array(range).ok().unwrap_or_default()
542    }
543
544    /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets].
545    pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> {
546        let data = self.data;
547        let offsets = self.sequence_offsets();
548        ArrayOfOffsets::new(offsets, data, ())
549    }
550
551    pub fn subst_format_byte_range(&self) -> Range<usize> {
552        let start = 0;
553        let end = start + u16::RAW_BYTE_LEN;
554        start..end
555    }
556
557    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
558        let start = self.subst_format_byte_range().end;
559        let end = start + Offset16::RAW_BYTE_LEN;
560        start..end
561    }
562
563    pub fn sequence_count_byte_range(&self) -> Range<usize> {
564        let start = self.coverage_offset_byte_range().end;
565        let end = start + u16::RAW_BYTE_LEN;
566        start..end
567    }
568
569    pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
570        let sequence_count = self.sequence_count();
571        let start = self.sequence_count_byte_range().end;
572        let end =
573            start + (transforms::to_usize(sequence_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
574        start..end
575    }
576}
577
578const _: () = assert!(FontData::default_data_long_enough(
579    MultipleSubstFormat1::MIN_SIZE
580));
581
582impl Default for MultipleSubstFormat1<'_> {
583    fn default() -> Self {
584        Self {
585            data: FontData::default_format_1_u16_table_data(),
586        }
587    }
588}
589
590impl<'a> MinByteRange<'a> for Sequence<'a> {
591    fn min_byte_range(&self) -> Range<usize> {
592        0..self.substitute_glyph_ids_byte_range().end
593    }
594    fn min_table_bytes(&self) -> &'a [u8] {
595        let range = self.min_byte_range();
596        self.data.as_bytes().get(range).unwrap_or_default()
597    }
598}
599
600impl ReadArgs for Sequence<'_> {
601    type Args = ();
602}
603
604impl<'a> FontRead<'a> for Sequence<'a> {
605    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
606        #[allow(clippy::absurd_extreme_comparisons)]
607        if data.len() < Self::MIN_SIZE {
608            return Err(ReadError::OutOfBounds);
609        }
610        Ok(Self { data })
611    }
612}
613
614/// Part of [MultipleSubstFormat1]
615#[derive(Clone)]
616pub struct Sequence<'a> {
617    data: FontData<'a>,
618}
619
620#[allow(clippy::needless_lifetimes)]
621impl<'a> Sequence<'a> {
622    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
623    basic_table_impls!(impl_the_methods);
624
625    /// Number of glyph IDs in the substituteGlyphIDs array. This must
626    /// always be greater than 0.
627    pub fn glyph_count(&self) -> u16 {
628        let range = self.glyph_count_byte_range();
629        self.data.read_at(range.start).ok().unwrap()
630    }
631
632    /// String of glyph IDs to substitute
633    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
634        let range = self.substitute_glyph_ids_byte_range();
635        self.data.read_array(range).ok().unwrap_or_default()
636    }
637
638    pub fn glyph_count_byte_range(&self) -> Range<usize> {
639        let start = 0;
640        let end = start + u16::RAW_BYTE_LEN;
641        start..end
642    }
643
644    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
645        let glyph_count = self.glyph_count();
646        let start = self.glyph_count_byte_range().end;
647        let end =
648            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
649        start..end
650    }
651}
652
653const _: () = assert!(FontData::default_data_long_enough(Sequence::MIN_SIZE));
654
655impl Default for Sequence<'_> {
656    fn default() -> Self {
657        Self {
658            data: FontData::default_table_data(),
659        }
660    }
661}
662
663impl Format<u16> for AlternateSubstFormat1<'_> {
664    const FORMAT: u16 = 1;
665}
666
667impl<'a> MinByteRange<'a> for AlternateSubstFormat1<'a> {
668    fn min_byte_range(&self) -> Range<usize> {
669        0..self.alternate_set_offsets_byte_range().end
670    }
671    fn min_table_bytes(&self) -> &'a [u8] {
672        let range = self.min_byte_range();
673        self.data.as_bytes().get(range).unwrap_or_default()
674    }
675}
676
677impl ReadArgs for AlternateSubstFormat1<'_> {
678    type Args = ();
679}
680
681impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> {
682    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
683        #[allow(clippy::absurd_extreme_comparisons)]
684        if data.len() < Self::MIN_SIZE {
685            return Err(ReadError::OutOfBounds);
686        }
687        Ok(Self { data })
688    }
689}
690
691/// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1)
692#[derive(Clone)]
693pub struct AlternateSubstFormat1<'a> {
694    data: FontData<'a>,
695}
696
697#[allow(clippy::needless_lifetimes)]
698impl<'a> AlternateSubstFormat1<'a> {
699    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
700    basic_table_impls!(impl_the_methods);
701
702    /// Format identifier: format = 1
703    pub fn subst_format(&self) -> u16 {
704        let range = self.subst_format_byte_range();
705        self.data.read_at(range.start).ok().unwrap()
706    }
707
708    /// Offset to Coverage table, from beginning of substitution
709    /// subtable
710    pub fn coverage_offset(&self) -> Offset16 {
711        let range = self.coverage_offset_byte_range();
712        self.data.read_at(range.start).ok().unwrap()
713    }
714
715    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
716    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
717        let data = self.data;
718        self.coverage_offset().resolve(data)
719    }
720
721    /// Number of AlternateSet tables
722    pub fn alternate_set_count(&self) -> u16 {
723        let range = self.alternate_set_count_byte_range();
724        self.data.read_at(range.start).ok().unwrap()
725    }
726
727    /// Array of offsets to AlternateSet tables. Offsets are from
728    /// beginning of substitution subtable, ordered by Coverage index
729    pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
730        let range = self.alternate_set_offsets_byte_range();
731        self.data.read_array(range).ok().unwrap_or_default()
732    }
733
734    /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets].
735    pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> {
736        let data = self.data;
737        let offsets = self.alternate_set_offsets();
738        ArrayOfOffsets::new(offsets, data, ())
739    }
740
741    pub fn subst_format_byte_range(&self) -> Range<usize> {
742        let start = 0;
743        let end = start + u16::RAW_BYTE_LEN;
744        start..end
745    }
746
747    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
748        let start = self.subst_format_byte_range().end;
749        let end = start + Offset16::RAW_BYTE_LEN;
750        start..end
751    }
752
753    pub fn alternate_set_count_byte_range(&self) -> Range<usize> {
754        let start = self.coverage_offset_byte_range().end;
755        let end = start + u16::RAW_BYTE_LEN;
756        start..end
757    }
758
759    pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
760        let alternate_set_count = self.alternate_set_count();
761        let start = self.alternate_set_count_byte_range().end;
762        let end = start
763            + (transforms::to_usize(alternate_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
764        start..end
765    }
766}
767
768const _: () = assert!(FontData::default_data_long_enough(
769    AlternateSubstFormat1::MIN_SIZE
770));
771
772impl Default for AlternateSubstFormat1<'_> {
773    fn default() -> Self {
774        Self {
775            data: FontData::default_format_1_u16_table_data(),
776        }
777    }
778}
779
780impl<'a> MinByteRange<'a> for AlternateSet<'a> {
781    fn min_byte_range(&self) -> Range<usize> {
782        0..self.alternate_glyph_ids_byte_range().end
783    }
784    fn min_table_bytes(&self) -> &'a [u8] {
785        let range = self.min_byte_range();
786        self.data.as_bytes().get(range).unwrap_or_default()
787    }
788}
789
790impl ReadArgs for AlternateSet<'_> {
791    type Args = ();
792}
793
794impl<'a> FontRead<'a> for AlternateSet<'a> {
795    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
796        #[allow(clippy::absurd_extreme_comparisons)]
797        if data.len() < Self::MIN_SIZE {
798            return Err(ReadError::OutOfBounds);
799        }
800        Ok(Self { data })
801    }
802}
803
804/// Part of [AlternateSubstFormat1]
805#[derive(Clone)]
806pub struct AlternateSet<'a> {
807    data: FontData<'a>,
808}
809
810#[allow(clippy::needless_lifetimes)]
811impl<'a> AlternateSet<'a> {
812    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
813    basic_table_impls!(impl_the_methods);
814
815    /// Number of glyph IDs in the alternateGlyphIDs array
816    pub fn glyph_count(&self) -> u16 {
817        let range = self.glyph_count_byte_range();
818        self.data.read_at(range.start).ok().unwrap()
819    }
820
821    /// Array of alternate glyph IDs, in arbitrary order
822    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
823        let range = self.alternate_glyph_ids_byte_range();
824        self.data.read_array(range).ok().unwrap_or_default()
825    }
826
827    pub fn glyph_count_byte_range(&self) -> Range<usize> {
828        let start = 0;
829        let end = start + u16::RAW_BYTE_LEN;
830        start..end
831    }
832
833    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
834        let glyph_count = self.glyph_count();
835        let start = self.glyph_count_byte_range().end;
836        let end =
837            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
838        start..end
839    }
840}
841
842const _: () = assert!(FontData::default_data_long_enough(AlternateSet::MIN_SIZE));
843
844impl Default for AlternateSet<'_> {
845    fn default() -> Self {
846        Self {
847            data: FontData::default_table_data(),
848        }
849    }
850}
851
852impl Format<u16> for LigatureSubstFormat1<'_> {
853    const FORMAT: u16 = 1;
854}
855
856impl<'a> MinByteRange<'a> for LigatureSubstFormat1<'a> {
857    fn min_byte_range(&self) -> Range<usize> {
858        0..self.ligature_set_offsets_byte_range().end
859    }
860    fn min_table_bytes(&self) -> &'a [u8] {
861        let range = self.min_byte_range();
862        self.data.as_bytes().get(range).unwrap_or_default()
863    }
864}
865
866impl ReadArgs for LigatureSubstFormat1<'_> {
867    type Args = ();
868}
869
870impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
871    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
872        #[allow(clippy::absurd_extreme_comparisons)]
873        if data.len() < Self::MIN_SIZE {
874            return Err(ReadError::OutOfBounds);
875        }
876        Ok(Self { data })
877    }
878}
879
880/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
881#[derive(Clone)]
882pub struct LigatureSubstFormat1<'a> {
883    data: FontData<'a>,
884}
885
886#[allow(clippy::needless_lifetimes)]
887impl<'a> LigatureSubstFormat1<'a> {
888    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
889    basic_table_impls!(impl_the_methods);
890
891    /// Format identifier: format = 1
892    pub fn subst_format(&self) -> u16 {
893        let range = self.subst_format_byte_range();
894        self.data.read_at(range.start).ok().unwrap()
895    }
896
897    /// Offset to Coverage table, from beginning of substitution
898    /// subtable
899    pub fn coverage_offset(&self) -> Offset16 {
900        let range = self.coverage_offset_byte_range();
901        self.data.read_at(range.start).ok().unwrap()
902    }
903
904    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
905    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
906        let data = self.data;
907        self.coverage_offset().resolve(data)
908    }
909
910    /// Number of LigatureSet tables
911    pub fn ligature_set_count(&self) -> u16 {
912        let range = self.ligature_set_count_byte_range();
913        self.data.read_at(range.start).ok().unwrap()
914    }
915
916    /// Array of offsets to LigatureSet tables. Offsets are from
917    /// beginning of substitution subtable, ordered by Coverage index
918    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
919        let range = self.ligature_set_offsets_byte_range();
920        self.data.read_array(range).ok().unwrap_or_default()
921    }
922
923    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
924    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
925        let data = self.data;
926        let offsets = self.ligature_set_offsets();
927        ArrayOfOffsets::new(offsets, data, ())
928    }
929
930    pub fn subst_format_byte_range(&self) -> Range<usize> {
931        let start = 0;
932        let end = start + u16::RAW_BYTE_LEN;
933        start..end
934    }
935
936    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
937        let start = self.subst_format_byte_range().end;
938        let end = start + Offset16::RAW_BYTE_LEN;
939        start..end
940    }
941
942    pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
943        let start = self.coverage_offset_byte_range().end;
944        let end = start + u16::RAW_BYTE_LEN;
945        start..end
946    }
947
948    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
949        let ligature_set_count = self.ligature_set_count();
950        let start = self.ligature_set_count_byte_range().end;
951        let end = start
952            + (transforms::to_usize(ligature_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
953        start..end
954    }
955}
956
957const _: () = assert!(FontData::default_data_long_enough(
958    LigatureSubstFormat1::MIN_SIZE
959));
960
961impl Default for LigatureSubstFormat1<'_> {
962    fn default() -> Self {
963        Self {
964            data: FontData::default_format_1_u16_table_data(),
965        }
966    }
967}
968
969impl<'a> MinByteRange<'a> for LigatureSet<'a> {
970    fn min_byte_range(&self) -> Range<usize> {
971        0..self.ligature_offsets_byte_range().end
972    }
973    fn min_table_bytes(&self) -> &'a [u8] {
974        let range = self.min_byte_range();
975        self.data.as_bytes().get(range).unwrap_or_default()
976    }
977}
978
979impl ReadArgs for LigatureSet<'_> {
980    type Args = ();
981}
982
983impl<'a> FontRead<'a> for LigatureSet<'a> {
984    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
985        #[allow(clippy::absurd_extreme_comparisons)]
986        if data.len() < Self::MIN_SIZE {
987            return Err(ReadError::OutOfBounds);
988        }
989        Ok(Self { data })
990    }
991}
992
993/// Part of [LigatureSubstFormat1]
994#[derive(Clone)]
995pub struct LigatureSet<'a> {
996    data: FontData<'a>,
997}
998
999#[allow(clippy::needless_lifetimes)]
1000impl<'a> LigatureSet<'a> {
1001    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1002    basic_table_impls!(impl_the_methods);
1003
1004    /// Number of Ligature tables
1005    pub fn ligature_count(&self) -> u16 {
1006        let range = self.ligature_count_byte_range();
1007        self.data.read_at(range.start).ok().unwrap()
1008    }
1009
1010    /// Array of offsets to Ligature tables. Offsets are from beginning
1011    /// of LigatureSet table, ordered by preference.
1012    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
1013        let range = self.ligature_offsets_byte_range();
1014        self.data.read_array(range).ok().unwrap_or_default()
1015    }
1016
1017    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
1018    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
1019        let data = self.data;
1020        let offsets = self.ligature_offsets();
1021        ArrayOfOffsets::new(offsets, data, ())
1022    }
1023
1024    pub fn ligature_count_byte_range(&self) -> Range<usize> {
1025        let start = 0;
1026        let end = start + u16::RAW_BYTE_LEN;
1027        start..end
1028    }
1029
1030    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
1031        let ligature_count = self.ligature_count();
1032        let start = self.ligature_count_byte_range().end;
1033        let end =
1034            start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1035        start..end
1036    }
1037}
1038
1039const _: () = assert!(FontData::default_data_long_enough(LigatureSet::MIN_SIZE));
1040
1041impl Default for LigatureSet<'_> {
1042    fn default() -> Self {
1043        Self {
1044            data: FontData::default_table_data(),
1045        }
1046    }
1047}
1048
1049impl<'a> MinByteRange<'a> for Ligature<'a> {
1050    fn min_byte_range(&self) -> Range<usize> {
1051        0..self.component_glyph_ids_byte_range().end
1052    }
1053    fn min_table_bytes(&self) -> &'a [u8] {
1054        let range = self.min_byte_range();
1055        self.data.as_bytes().get(range).unwrap_or_default()
1056    }
1057}
1058
1059impl ReadArgs for Ligature<'_> {
1060    type Args = ();
1061}
1062
1063impl<'a> FontRead<'a> for Ligature<'a> {
1064    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1065        #[allow(clippy::absurd_extreme_comparisons)]
1066        if data.len() < Self::MIN_SIZE {
1067            return Err(ReadError::OutOfBounds);
1068        }
1069        Ok(Self { data })
1070    }
1071}
1072
1073/// Part of [LigatureSubstFormat1]
1074#[derive(Clone)]
1075pub struct Ligature<'a> {
1076    data: FontData<'a>,
1077}
1078
1079#[allow(clippy::needless_lifetimes)]
1080impl<'a> Ligature<'a> {
1081    pub const MIN_SIZE: usize = (GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
1082    basic_table_impls!(impl_the_methods);
1083
1084    /// glyph ID of ligature to substitute
1085    pub fn ligature_glyph(&self) -> GlyphId16 {
1086        let range = self.ligature_glyph_byte_range();
1087        self.data.read_at(range.start).ok().unwrap()
1088    }
1089
1090    /// Number of components in the ligature
1091    pub fn component_count(&self) -> u16 {
1092        let range = self.component_count_byte_range();
1093        self.data.read_at(range.start).ok().unwrap()
1094    }
1095
1096    /// Array of component glyph IDs — start with the second
1097    /// component, ordered in writing direction
1098    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1099        let range = self.component_glyph_ids_byte_range();
1100        self.data.read_array(range).ok().unwrap_or_default()
1101    }
1102
1103    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
1104        let start = 0;
1105        let end = start + GlyphId16::RAW_BYTE_LEN;
1106        start..end
1107    }
1108
1109    pub fn component_count_byte_range(&self) -> Range<usize> {
1110        let start = self.ligature_glyph_byte_range().end;
1111        let end = start + u16::RAW_BYTE_LEN;
1112        start..end
1113    }
1114
1115    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
1116        let component_count = self.component_count();
1117        let start = self.component_count_byte_range().end;
1118        let end = start
1119            + (transforms::subtract(component_count, 1_usize))
1120                .saturating_mul(GlyphId16::RAW_BYTE_LEN);
1121        start..end
1122    }
1123}
1124
1125const _: () = assert!(FontData::default_data_long_enough(Ligature::MIN_SIZE));
1126
1127impl Default for Ligature<'_> {
1128    fn default() -> Self {
1129        Self {
1130            data: FontData::default_table_data(),
1131        }
1132    }
1133}
1134
1135impl Format<u16> for ExtensionSubstFormat1<'_> {
1136    const FORMAT: u16 = 1;
1137}
1138
1139impl Discriminant for ExtensionSubstFormat1<'_, ()> {
1140    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
1141        data.read_at(u16::RAW_BYTE_LEN)
1142    }
1143}
1144
1145impl<'a, T> MinByteRange<'a> for ExtensionSubstFormat1<'a, T> {
1146    fn min_byte_range(&self) -> Range<usize> {
1147        0..self.extension_offset_byte_range().end
1148    }
1149    fn min_table_bytes(&self) -> &'a [u8] {
1150        let range = self.min_byte_range();
1151        self.data.as_bytes().get(range).unwrap_or_default()
1152    }
1153}
1154
1155impl<T> ReadArgs for ExtensionSubstFormat1<'_, T> {
1156    type Args = ();
1157}
1158
1159impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
1160    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1161        #[allow(clippy::absurd_extreme_comparisons)]
1162        if data.len() < Self::MIN_SIZE {
1163            return Err(ReadError::OutOfBounds);
1164        }
1165        Ok(Self {
1166            data,
1167            offset_type: std::marker::PhantomData,
1168        })
1169    }
1170}
1171
1172impl<'a, T> ExtensionSubstFormat1<'a, T> {
1173    #[allow(dead_code)]
1174    /// Replace the specific generic type on this implementation with `()`
1175    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1176        ExtensionSubstFormat1 {
1177            data: self.data,
1178            offset_type: std::marker::PhantomData,
1179        }
1180    }
1181}
1182
1183/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
1184#[derive(Clone)]
1185pub struct ExtensionSubstFormat1<'a, T = ()> {
1186    data: FontData<'a>,
1187    offset_type: std::marker::PhantomData<*const T>,
1188}
1189
1190#[allow(clippy::needless_lifetimes)]
1191impl<'a, T> ExtensionSubstFormat1<'a, T> {
1192    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
1193    basic_table_impls!(impl_the_methods);
1194
1195    /// Format identifier. Set to 1.
1196    pub fn subst_format(&self) -> u16 {
1197        let range = self.subst_format_byte_range();
1198        self.data.read_at(range.start).ok().unwrap()
1199    }
1200
1201    /// Lookup type of subtable referenced by extensionOffset (that is,
1202    /// the extension subtable).
1203    pub fn extension_lookup_type(&self) -> u16 {
1204        let range = self.extension_lookup_type_byte_range();
1205        self.data.read_at(range.start).ok().unwrap()
1206    }
1207
1208    /// Offset to the extension subtable, of lookup type
1209    /// extensionLookupType, relative to the start of the
1210    /// ExtensionSubstFormat1 subtable.
1211    pub fn extension_offset(&self) -> Offset32 {
1212        let range = self.extension_offset_byte_range();
1213        self.data.read_at(range.start).ok().unwrap()
1214    }
1215
1216    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
1217    pub fn extension(&self) -> Result<T, ReadError>
1218    where
1219        T: FontRead<'a, Args = ()>,
1220    {
1221        let data = self.data;
1222        self.extension_offset().resolve(data)
1223    }
1224
1225    pub fn subst_format_byte_range(&self) -> Range<usize> {
1226        let start = 0;
1227        let end = start + u16::RAW_BYTE_LEN;
1228        start..end
1229    }
1230
1231    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
1232        let start = self.subst_format_byte_range().end;
1233        let end = start + u16::RAW_BYTE_LEN;
1234        start..end
1235    }
1236
1237    pub fn extension_offset_byte_range(&self) -> Range<usize> {
1238        let start = self.extension_lookup_type_byte_range().end;
1239        let end = start + Offset32::RAW_BYTE_LEN;
1240        start..end
1241    }
1242}
1243
1244const _: () = assert!(FontData::default_data_long_enough(
1245    ExtensionSubstFormat1::<()>::MIN_SIZE
1246));
1247
1248impl<T> Default for ExtensionSubstFormat1<'_, T> {
1249    fn default() -> Self {
1250        Self {
1251            data: FontData::default_format_1_u16_table_data(),
1252            offset_type: std::marker::PhantomData,
1253        }
1254    }
1255}
1256
1257/// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable
1258pub enum ExtensionSubtable<'a> {
1259    Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
1260    Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>),
1261    Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>),
1262    Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>),
1263    Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
1264    ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
1265    Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>),
1266}
1267
1268impl Default for ExtensionSubtable<'_> {
1269    fn default() -> Self {
1270        Self::Single(Default::default())
1271    }
1272}
1273
1274impl ReadArgs for ExtensionSubtable<'_> {
1275    type Args = ();
1276}
1277
1278impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
1279    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1280        let discriminant = ExtensionSubstFormat1::read_discriminant(bytes)?;
1281        match discriminant {
1282            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
1283            2 => Ok(ExtensionSubtable::Multiple(FontRead::read(bytes)?)),
1284            3 => Ok(ExtensionSubtable::Alternate(FontRead::read(bytes)?)),
1285            4 => Ok(ExtensionSubtable::Ligature(FontRead::read(bytes)?)),
1286            5 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
1287            6 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
1288            8 => Ok(ExtensionSubtable::Reverse(FontRead::read(bytes)?)),
1289            other => Err(ReadError::InvalidFormat(other.into())),
1290        }
1291    }
1292}
1293
1294impl<'a> ExtensionSubtable<'a> {
1295    #[allow(dead_code)]
1296    /// Return the inner table, removing the specific generics.
1297    ///
1298    /// This lets us return a single concrete type we can call methods on.
1299    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
1300        match self {
1301            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
1302            ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
1303            ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
1304            ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
1305            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
1306            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
1307            ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
1308        }
1309    }
1310}
1311
1312impl Format<u16> for ReverseChainSingleSubstFormat1<'_> {
1313    const FORMAT: u16 = 1;
1314}
1315
1316impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat1<'a> {
1317    fn min_byte_range(&self) -> Range<usize> {
1318        0..self.substitute_glyph_ids_byte_range().end
1319    }
1320    fn min_table_bytes(&self) -> &'a [u8] {
1321        let range = self.min_byte_range();
1322        self.data.as_bytes().get(range).unwrap_or_default()
1323    }
1324}
1325
1326impl ReadArgs for ReverseChainSingleSubstFormat1<'_> {
1327    type Args = ();
1328}
1329
1330impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
1331    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1332        #[allow(clippy::absurd_extreme_comparisons)]
1333        if data.len() < Self::MIN_SIZE {
1334            return Err(ReadError::OutOfBounds);
1335        }
1336        Ok(Self { data })
1337    }
1338}
1339
1340/// [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)
1341#[derive(Clone)]
1342pub struct ReverseChainSingleSubstFormat1<'a> {
1343    data: FontData<'a>,
1344}
1345
1346#[allow(clippy::needless_lifetimes)]
1347impl<'a> ReverseChainSingleSubstFormat1<'a> {
1348    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1349        + Offset16::RAW_BYTE_LEN
1350        + u16::RAW_BYTE_LEN
1351        + u16::RAW_BYTE_LEN
1352        + u16::RAW_BYTE_LEN);
1353    basic_table_impls!(impl_the_methods);
1354
1355    /// Format identifier: format = 1
1356    pub fn subst_format(&self) -> u16 {
1357        let range = self.subst_format_byte_range();
1358        self.data.read_at(range.start).ok().unwrap()
1359    }
1360
1361    /// Offset to Coverage table, from beginning of substitution
1362    /// subtable.
1363    pub fn coverage_offset(&self) -> Offset16 {
1364        let range = self.coverage_offset_byte_range();
1365        self.data.read_at(range.start).ok().unwrap()
1366    }
1367
1368    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1369    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1370        let data = self.data;
1371        self.coverage_offset().resolve(data)
1372    }
1373
1374    /// Number of glyphs in the backtrack sequence.
1375    pub fn backtrack_glyph_count(&self) -> u16 {
1376        let range = self.backtrack_glyph_count_byte_range();
1377        self.data.read_at(range.start).ok().unwrap()
1378    }
1379
1380    /// Array of offsets to coverage tables in backtrack sequence, in
1381    /// glyph sequence order.
1382    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1383        let range = self.backtrack_coverage_offsets_byte_range();
1384        self.data.read_array(range).ok().unwrap_or_default()
1385    }
1386
1387    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
1388    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1389        let data = self.data;
1390        let offsets = self.backtrack_coverage_offsets();
1391        ArrayOfOffsets::new(offsets, data, ())
1392    }
1393
1394    /// Number of glyphs in lookahead sequence.
1395    pub fn lookahead_glyph_count(&self) -> u16 {
1396        let range = self.lookahead_glyph_count_byte_range();
1397        self.data.read_at(range.start).ok().unwrap_or_default()
1398    }
1399
1400    /// Array of offsets to coverage tables in lookahead sequence, in
1401    /// glyph sequence order.
1402    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
1403        let range = self.lookahead_coverage_offsets_byte_range();
1404        self.data.read_array(range).ok().unwrap_or_default()
1405    }
1406
1407    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
1408    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
1409        let data = self.data;
1410        let offsets = self.lookahead_coverage_offsets();
1411        ArrayOfOffsets::new(offsets, data, ())
1412    }
1413
1414    /// Number of glyph IDs in the substituteGlyphIDs array.
1415    pub fn glyph_count(&self) -> u16 {
1416        let range = self.glyph_count_byte_range();
1417        self.data.read_at(range.start).ok().unwrap_or_default()
1418    }
1419
1420    /// Array of substitute glyph IDs — ordered by Coverage index.
1421    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
1422        let range = self.substitute_glyph_ids_byte_range();
1423        self.data.read_array(range).ok().unwrap_or_default()
1424    }
1425
1426    pub fn subst_format_byte_range(&self) -> Range<usize> {
1427        let start = 0;
1428        let end = start + u16::RAW_BYTE_LEN;
1429        start..end
1430    }
1431
1432    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1433        let start = self.subst_format_byte_range().end;
1434        let end = start + Offset16::RAW_BYTE_LEN;
1435        start..end
1436    }
1437
1438    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
1439        let start = self.coverage_offset_byte_range().end;
1440        let end = start + u16::RAW_BYTE_LEN;
1441        start..end
1442    }
1443
1444    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
1445        let backtrack_glyph_count = self.backtrack_glyph_count();
1446        let start = self.backtrack_glyph_count_byte_range().end;
1447        let end = start
1448            + (transforms::to_usize(backtrack_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1449        start..end
1450    }
1451
1452    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
1453        let start = self.backtrack_coverage_offsets_byte_range().end;
1454        let end = start + u16::RAW_BYTE_LEN;
1455        start..end
1456    }
1457
1458    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
1459        let lookahead_glyph_count = self.lookahead_glyph_count();
1460        let start = self.lookahead_glyph_count_byte_range().end;
1461        let end = start
1462            + (transforms::to_usize(lookahead_glyph_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1463        start..end
1464    }
1465
1466    pub fn glyph_count_byte_range(&self) -> Range<usize> {
1467        let start = self.lookahead_coverage_offsets_byte_range().end;
1468        let end = start + u16::RAW_BYTE_LEN;
1469        start..end
1470    }
1471
1472    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
1473        let glyph_count = self.glyph_count();
1474        let start = self.glyph_count_byte_range().end;
1475        let end =
1476            start + (transforms::to_usize(glyph_count)).saturating_mul(GlyphId16::RAW_BYTE_LEN);
1477        start..end
1478    }
1479}
1480
1481const _: () = assert!(FontData::default_data_long_enough(
1482    ReverseChainSingleSubstFormat1::MIN_SIZE
1483));
1484
1485impl Default for ReverseChainSingleSubstFormat1<'_> {
1486    fn default() -> Self {
1487        Self {
1488            data: FontData::default_format_1_u16_table_data(),
1489        }
1490    }
1491}