Skip to main content

read_fonts/generated/
generated_gpos.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 Gpos<'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.offset_data().as_bytes().get(range).unwrap_or_default()
15    }
16}
17
18impl TopLevelTable for Gpos<'_> {
19    /// `GPOS`
20    const TAG: Tag = Tag::new(b"GPOS");
21}
22
23impl ReadArgs for Gpos<'_> {
24    type Args = ();
25}
26
27impl<'a> FontRead<'a> for Gpos<'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        let Some(data) = TableDataRef::new(data.as_bytes()) else {
34            return Err(ReadError::OutOfBounds);
35        };
36        Ok(Self { data })
37    }
38}
39
40/// The fields at the start of the table, which are read directly.
41#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
42#[repr(C, packed)]
43#[doc(hidden)]
44pub struct GposFixedHeader {
45    pub version: BigEndian<MajorMinor>,
46    pub script_list_offset: BigEndian<Offset16>,
47    pub feature_list_offset: BigEndian<Offset16>,
48    pub lookup_list_offset: BigEndian<Offset16>,
49}
50
51#[allow(clippy::absurd_extreme_comparisons)]
52const _: () = assert!(std::mem::size_of::<GposFixedHeader>() <= Gpos::MIN_SIZE);
53
54/// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1)
55/// [GPOS Version 1.0](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#gpos-header)
56#[derive(Clone)]
57pub struct Gpos<'a> {
58    data: TableDataRef<'a, GposFixedHeader>,
59}
60
61#[allow(clippy::needless_lifetimes)]
62impl<'a> Gpos<'a> {
63    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN
64        + Offset16::RAW_BYTE_LEN
65        + Offset16::RAW_BYTE_LEN
66        + Offset16::RAW_BYTE_LEN);
67    basic_table_impls!(impl_the_methods);
68
69    /// The major and minor version of the GPOS table, as a tuple (u16, u16)
70    pub fn version(&self) -> MajorMinor {
71        self.data.header().version.get()
72    }
73
74    /// Offset to ScriptList table, from beginning of GPOS table
75    pub fn script_list_offset(&self) -> Offset16 {
76        self.data.header().script_list_offset.get()
77    }
78
79    /// Attempt to resolve [`script_list_offset`][Self::script_list_offset].
80    pub fn script_list(&self) -> Result<ScriptList<'a>, ReadError> {
81        let data = self.offset_data();
82        self.script_list_offset().resolve(data)
83    }
84
85    /// Offset to FeatureList table, from beginning of GPOS table
86    pub fn feature_list_offset(&self) -> Offset16 {
87        self.data.header().feature_list_offset.get()
88    }
89
90    /// Attempt to resolve [`feature_list_offset`][Self::feature_list_offset].
91    pub fn feature_list(&self) -> Result<FeatureList<'a>, ReadError> {
92        let data = self.offset_data();
93        self.feature_list_offset().resolve(data)
94    }
95
96    /// Offset to LookupList table, from beginning of GPOS table
97    pub fn lookup_list_offset(&self) -> Offset16 {
98        self.data.header().lookup_list_offset.get()
99    }
100
101    /// Attempt to resolve [`lookup_list_offset`][Self::lookup_list_offset].
102    pub fn lookup_list(&self) -> Result<PositionLookupList<'a>, ReadError> {
103        let data = self.offset_data();
104        self.lookup_list_offset().resolve(data)
105    }
106
107    pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
108        let range = self.feature_variations_offset_byte_range();
109        (!range.is_empty())
110            .then(|| self.offset_data().read_at(range.start).ok())
111            .flatten()
112    }
113
114    /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset].
115    pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
116        let data = self.offset_data();
117        self.feature_variations_offset().map(|x| x.resolve(data))?
118    }
119
120    pub fn version_byte_range(&self) -> Range<usize> {
121        let start = 0;
122        let end = start + MajorMinor::RAW_BYTE_LEN;
123        start..end
124    }
125
126    pub fn script_list_offset_byte_range(&self) -> Range<usize> {
127        let start = self.version_byte_range().end;
128        let end = start + Offset16::RAW_BYTE_LEN;
129        start..end
130    }
131
132    pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
133        let start = self.script_list_offset_byte_range().end;
134        let end = start + Offset16::RAW_BYTE_LEN;
135        start..end
136    }
137
138    pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
139        let start = self.feature_list_offset_byte_range().end;
140        let end = start + Offset16::RAW_BYTE_LEN;
141        start..end
142    }
143
144    pub fn feature_variations_offset_byte_range(&self) -> Range<usize> {
145        let start = self.lookup_list_offset_byte_range().end;
146        let end = if self.version().compatible((1u16, 1u16)) {
147            start + Offset32::RAW_BYTE_LEN
148        } else {
149            start
150        };
151        start..end
152    }
153}
154
155const _: () = assert!(FontData::default_data_long_enough(Gpos::MIN_SIZE));
156
157impl Default for Gpos<'_> {
158    fn default() -> Self {
159        Self {
160            data: TableDataRef::new(FontData::default_table_data().as_bytes())
161                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
162        }
163    }
164}
165
166/// A [GPOS Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#gsubLookupTypeEnum) subtable.
167pub enum PositionLookup<'a> {
168    Single(Lookup<'a, SinglePos<'a>>),
169    Pair(Lookup<'a, PairPos<'a>>),
170    Cursive(Lookup<'a, CursivePosFormat1<'a>>),
171    MarkToBase(Lookup<'a, MarkBasePosFormat1<'a>>),
172    MarkToLig(Lookup<'a, MarkLigPosFormat1<'a>>),
173    MarkToMark(Lookup<'a, MarkMarkPosFormat1<'a>>),
174    Contextual(Lookup<'a, PositionSequenceContext<'a>>),
175    ChainContextual(Lookup<'a, PositionChainContext<'a>>),
176    Extension(Lookup<'a, ExtensionSubtable<'a>>),
177}
178
179impl Default for PositionLookup<'_> {
180    fn default() -> Self {
181        Self::Single(Default::default())
182    }
183}
184
185impl ReadArgs for PositionLookup<'_> {
186    type Args = ();
187}
188
189impl<'a> FontRead<'a> for PositionLookup<'a> {
190    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
191        let discriminant = Lookup::read_discriminant(bytes)?;
192        match discriminant {
193            1 => Ok(PositionLookup::Single(FontRead::read(bytes)?)),
194            2 => Ok(PositionLookup::Pair(FontRead::read(bytes)?)),
195            3 => Ok(PositionLookup::Cursive(FontRead::read(bytes)?)),
196            4 => Ok(PositionLookup::MarkToBase(FontRead::read(bytes)?)),
197            5 => Ok(PositionLookup::MarkToLig(FontRead::read(bytes)?)),
198            6 => Ok(PositionLookup::MarkToMark(FontRead::read(bytes)?)),
199            7 => Ok(PositionLookup::Contextual(FontRead::read(bytes)?)),
200            8 => Ok(PositionLookup::ChainContextual(FontRead::read(bytes)?)),
201            9 => Ok(PositionLookup::Extension(FontRead::read(bytes)?)),
202            other => Err(ReadError::InvalidFormat(other.into())),
203        }
204    }
205}
206
207impl<'a> PositionLookup<'a> {
208    #[allow(dead_code)]
209    /// Return the inner table, removing the specific generics.
210    ///
211    /// This lets us return a single concrete type we can call methods on.
212    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
213        match self {
214            PositionLookup::Single(inner) => inner.of_unit_type(),
215            PositionLookup::Pair(inner) => inner.of_unit_type(),
216            PositionLookup::Cursive(inner) => inner.of_unit_type(),
217            PositionLookup::MarkToBase(inner) => inner.of_unit_type(),
218            PositionLookup::MarkToLig(inner) => inner.of_unit_type(),
219            PositionLookup::MarkToMark(inner) => inner.of_unit_type(),
220            PositionLookup::Contextual(inner) => inner.of_unit_type(),
221            PositionLookup::ChainContextual(inner) => inner.of_unit_type(),
222            PositionLookup::Extension(inner) => inner.of_unit_type(),
223        }
224    }
225}
226
227/// See [ValueRecord]
228#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, bytemuck :: AnyBitPattern)]
229#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
230#[repr(transparent)]
231pub struct ValueFormat {
232    bits: u16,
233}
234
235impl ValueFormat {
236    /// Includes horizontal adjustment for placement
237    pub const X_PLACEMENT: Self = Self { bits: 0x0001 };
238
239    /// Includes vertical adjustment for placement
240    pub const Y_PLACEMENT: Self = Self { bits: 0x0002 };
241
242    /// Includes horizontal adjustment for advance
243    pub const X_ADVANCE: Self = Self { bits: 0x0004 };
244
245    /// Includes vertical adjustment for advance
246    pub const Y_ADVANCE: Self = Self { bits: 0x0008 };
247
248    /// Includes Device table (non-variable font) / VariationIndex
249    /// table (variable font) for horizontal placement
250    pub const X_PLACEMENT_DEVICE: Self = Self { bits: 0x0010 };
251
252    /// Includes Device table (non-variable font) / VariationIndex
253    /// table (variable font) for vertical placement
254    pub const Y_PLACEMENT_DEVICE: Self = Self { bits: 0x0020 };
255
256    /// Includes Device table (non-variable font) / VariationIndex
257    /// table (variable font) for horizontal advance
258    pub const X_ADVANCE_DEVICE: Self = Self { bits: 0x0040 };
259
260    /// Includes Device table (non-variable font) / VariationIndex
261    /// table (variable font) for vertical advance
262    pub const Y_ADVANCE_DEVICE: Self = Self { bits: 0x0080 };
263}
264
265impl ValueFormat {
266    ///  Returns an empty set of flags.
267    #[inline]
268    pub const fn empty() -> Self {
269        Self { bits: 0 }
270    }
271
272    /// Returns the set containing all flags.
273    #[inline]
274    pub const fn all() -> Self {
275        Self {
276            bits: Self::X_PLACEMENT.bits
277                | Self::Y_PLACEMENT.bits
278                | Self::X_ADVANCE.bits
279                | Self::Y_ADVANCE.bits
280                | Self::X_PLACEMENT_DEVICE.bits
281                | Self::Y_PLACEMENT_DEVICE.bits
282                | Self::X_ADVANCE_DEVICE.bits
283                | Self::Y_ADVANCE_DEVICE.bits,
284        }
285    }
286
287    /// Returns the raw value of the flags currently stored.
288    #[inline]
289    pub const fn bits(&self) -> u16 {
290        self.bits
291    }
292
293    /// Convert from underlying bit representation, unless that
294    /// representation contains bits that do not correspond to a flag.
295    #[inline]
296    pub const fn from_bits(bits: u16) -> Option<Self> {
297        if (bits & !Self::all().bits()) == 0 {
298            Some(Self { bits })
299        } else {
300            None
301        }
302    }
303
304    /// Convert from underlying bit representation, dropping any bits
305    /// that do not correspond to flags.
306    #[inline]
307    pub const fn from_bits_truncate(bits: u16) -> Self {
308        Self {
309            bits: bits & Self::all().bits,
310        }
311    }
312
313    /// Returns `true` if no flags are currently stored.
314    #[inline]
315    pub const fn is_empty(&self) -> bool {
316        self.bits() == Self::empty().bits()
317    }
318
319    /// Returns `true` if there are flags common to both `self` and `other`.
320    #[inline]
321    pub const fn intersects(&self, other: Self) -> bool {
322        !(Self {
323            bits: self.bits & other.bits,
324        })
325        .is_empty()
326    }
327
328    /// Returns `true` if all of the flags in `other` are contained within `self`.
329    #[inline]
330    pub const fn contains(&self, other: Self) -> bool {
331        (self.bits & other.bits) == other.bits
332    }
333
334    /// Inserts the specified flags in-place.
335    #[inline]
336    pub fn insert(&mut self, other: Self) {
337        self.bits |= other.bits;
338    }
339
340    /// Removes the specified flags in-place.
341    #[inline]
342    pub fn remove(&mut self, other: Self) {
343        self.bits &= !other.bits;
344    }
345
346    /// Toggles the specified flags in-place.
347    #[inline]
348    pub fn toggle(&mut self, other: Self) {
349        self.bits ^= other.bits;
350    }
351
352    /// Returns the intersection between the flags in `self` and
353    /// `other`.
354    ///
355    /// Specifically, the returned set contains only the flags which are
356    /// present in *both* `self` *and* `other`.
357    ///
358    /// This is equivalent to using the `&` operator (e.g.
359    /// [`ops::BitAnd`]), as in `flags & other`.
360    ///
361    /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
362    #[inline]
363    #[must_use]
364    pub const fn intersection(self, other: Self) -> Self {
365        Self {
366            bits: self.bits & other.bits,
367        }
368    }
369
370    /// Returns the union of between the flags in `self` and `other`.
371    ///
372    /// Specifically, the returned set contains all flags which are
373    /// present in *either* `self` *or* `other`, including any which are
374    /// present in both.
375    ///
376    /// This is equivalent to using the `|` operator (e.g.
377    /// [`ops::BitOr`]), as in `flags | other`.
378    ///
379    /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
380    #[inline]
381    #[must_use]
382    pub const fn union(self, other: Self) -> Self {
383        Self {
384            bits: self.bits | other.bits,
385        }
386    }
387
388    /// Returns the difference between the flags in `self` and `other`.
389    ///
390    /// Specifically, the returned set contains all flags present in
391    /// `self`, except for the ones present in `other`.
392    ///
393    /// It is also conceptually equivalent to the "bit-clear" operation:
394    /// `flags & !other` (and this syntax is also supported).
395    ///
396    /// This is equivalent to using the `-` operator (e.g.
397    /// [`ops::Sub`]), as in `flags - other`.
398    ///
399    /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
400    #[inline]
401    #[must_use]
402    pub const fn difference(self, other: Self) -> Self {
403        Self {
404            bits: self.bits & !other.bits,
405        }
406    }
407}
408
409impl std::ops::BitOr for ValueFormat {
410    type Output = Self;
411
412    /// Returns the union of the two sets of flags.
413    #[inline]
414    fn bitor(self, other: ValueFormat) -> Self {
415        Self {
416            bits: self.bits | other.bits,
417        }
418    }
419}
420
421impl std::ops::BitOrAssign for ValueFormat {
422    /// Adds the set of flags.
423    #[inline]
424    fn bitor_assign(&mut self, other: Self) {
425        self.bits |= other.bits;
426    }
427}
428
429impl std::ops::BitXor for ValueFormat {
430    type Output = Self;
431
432    /// Returns the left flags, but with all the right flags toggled.
433    #[inline]
434    fn bitxor(self, other: Self) -> Self {
435        Self {
436            bits: self.bits ^ other.bits,
437        }
438    }
439}
440
441impl std::ops::BitXorAssign for ValueFormat {
442    /// Toggles the set of flags.
443    #[inline]
444    fn bitxor_assign(&mut self, other: Self) {
445        self.bits ^= other.bits;
446    }
447}
448
449impl std::ops::BitAnd for ValueFormat {
450    type Output = Self;
451
452    /// Returns the intersection between the two sets of flags.
453    #[inline]
454    fn bitand(self, other: Self) -> Self {
455        Self {
456            bits: self.bits & other.bits,
457        }
458    }
459}
460
461impl std::ops::BitAndAssign for ValueFormat {
462    /// Disables all flags disabled in the set.
463    #[inline]
464    fn bitand_assign(&mut self, other: Self) {
465        self.bits &= other.bits;
466    }
467}
468
469impl std::ops::Sub for ValueFormat {
470    type Output = Self;
471
472    /// Returns the set difference of the two sets of flags.
473    #[inline]
474    fn sub(self, other: Self) -> Self {
475        Self {
476            bits: self.bits & !other.bits,
477        }
478    }
479}
480
481impl std::ops::SubAssign for ValueFormat {
482    /// Disables all flags enabled in the set.
483    #[inline]
484    fn sub_assign(&mut self, other: Self) {
485        self.bits &= !other.bits;
486    }
487}
488
489impl std::ops::Not for ValueFormat {
490    type Output = Self;
491
492    /// Returns the complement of this set of flags.
493    #[inline]
494    fn not(self) -> Self {
495        Self { bits: !self.bits } & Self::all()
496    }
497}
498
499impl std::fmt::Debug for ValueFormat {
500    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
501        let members: &[(&str, Self)] = &[
502            ("X_PLACEMENT", Self::X_PLACEMENT),
503            ("Y_PLACEMENT", Self::Y_PLACEMENT),
504            ("X_ADVANCE", Self::X_ADVANCE),
505            ("Y_ADVANCE", Self::Y_ADVANCE),
506            ("X_PLACEMENT_DEVICE", Self::X_PLACEMENT_DEVICE),
507            ("Y_PLACEMENT_DEVICE", Self::Y_PLACEMENT_DEVICE),
508            ("X_ADVANCE_DEVICE", Self::X_ADVANCE_DEVICE),
509            ("Y_ADVANCE_DEVICE", Self::Y_ADVANCE_DEVICE),
510        ];
511        let mut first = true;
512        for (name, value) in members {
513            if self.contains(*value) {
514                if !first {
515                    f.write_str(" | ")?;
516                }
517                first = false;
518                f.write_str(name)?;
519            }
520        }
521        if first {
522            f.write_str("(empty)")?;
523        }
524        Ok(())
525    }
526}
527
528impl std::fmt::Binary for ValueFormat {
529    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
530        std::fmt::Binary::fmt(&self.bits, f)
531    }
532}
533
534impl std::fmt::Octal for ValueFormat {
535    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
536        std::fmt::Octal::fmt(&self.bits, f)
537    }
538}
539
540impl std::fmt::LowerHex for ValueFormat {
541    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
542        std::fmt::LowerHex::fmt(&self.bits, f)
543    }
544}
545
546impl std::fmt::UpperHex for ValueFormat {
547    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
548        std::fmt::UpperHex::fmt(&self.bits, f)
549    }
550}
551
552impl font_types::Scalar for ValueFormat {
553    type Raw = <u16 as font_types::Scalar>::Raw;
554    fn to_raw(self) -> Self::Raw {
555        self.bits().to_raw()
556    }
557    fn from_raw(raw: Self::Raw) -> Self {
558        let t = <u16>::from_raw(raw);
559        Self::from_bits_truncate(t)
560    }
561}
562
563/// [Anchor Tables](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables)
564/// position one glyph with respect to another.
565#[derive(Clone)]
566pub enum AnchorTable<'a> {
567    Format1(AnchorFormat1<'a>),
568    Format2(AnchorFormat2<'a>),
569    Format3(AnchorFormat3<'a>),
570}
571
572impl Default for AnchorTable<'_> {
573    fn default() -> Self {
574        Self::Format1(Default::default())
575    }
576}
577
578impl<'a> AnchorTable<'a> {
579    ///Return the `FontData` used to resolve offsets for this table.
580    pub fn offset_data(&self) -> FontData<'a> {
581        match self {
582            Self::Format1(item) => item.offset_data(),
583            Self::Format2(item) => item.offset_data(),
584            Self::Format3(item) => item.offset_data(),
585        }
586    }
587
588    /// Format identifier, = 1
589    pub fn anchor_format(&self) -> u16 {
590        match self {
591            Self::Format1(item) => item.anchor_format(),
592            Self::Format2(item) => item.anchor_format(),
593            Self::Format3(item) => item.anchor_format(),
594        }
595    }
596
597    /// Horizontal value, in design units
598    pub fn x_coordinate(&self) -> i16 {
599        match self {
600            Self::Format1(item) => item.x_coordinate(),
601            Self::Format2(item) => item.x_coordinate(),
602            Self::Format3(item) => item.x_coordinate(),
603        }
604    }
605
606    /// Vertical value, in design units
607    pub fn y_coordinate(&self) -> i16 {
608        match self {
609            Self::Format1(item) => item.y_coordinate(),
610            Self::Format2(item) => item.y_coordinate(),
611            Self::Format3(item) => item.y_coordinate(),
612        }
613    }
614}
615
616impl ReadArgs for AnchorTable<'_> {
617    type Args = ();
618}
619
620impl<'a> FontRead<'a> for AnchorTable<'a> {
621    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
622        let format: u16 = data.read_at(0usize)?;
623        match format {
624            AnchorFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
625            AnchorFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
626            AnchorFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
627            other => Err(ReadError::InvalidFormat(other.into())),
628        }
629    }
630}
631
632impl<'a> MinByteRange<'a> for AnchorTable<'a> {
633    fn min_byte_range(&self) -> Range<usize> {
634        match self {
635            Self::Format1(item) => item.min_byte_range(),
636            Self::Format2(item) => item.min_byte_range(),
637            Self::Format3(item) => item.min_byte_range(),
638        }
639    }
640    fn min_table_bytes(&self) -> &'a [u8] {
641        match self {
642            Self::Format1(item) => item.min_table_bytes(),
643            Self::Format2(item) => item.min_table_bytes(),
644            Self::Format3(item) => item.min_table_bytes(),
645        }
646    }
647}
648
649impl Format<u16> for AnchorFormat1<'_> {
650    const FORMAT: u16 = 1;
651}
652
653impl<'a> MinByteRange<'a> for AnchorFormat1<'a> {
654    fn min_byte_range(&self) -> Range<usize> {
655        0..self.y_coordinate_byte_range().end
656    }
657    fn min_table_bytes(&self) -> &'a [u8] {
658        let range = self.min_byte_range();
659        self.offset_data().as_bytes().get(range).unwrap_or_default()
660    }
661}
662
663impl ReadArgs for AnchorFormat1<'_> {
664    type Args = ();
665}
666
667impl<'a> FontRead<'a> for AnchorFormat1<'a> {
668    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
669        #[allow(clippy::absurd_extreme_comparisons)]
670        if data.len() < Self::MIN_SIZE {
671            return Err(ReadError::OutOfBounds);
672        }
673        let Some(data) = TableDataRef::new(data.as_bytes()) else {
674            return Err(ReadError::OutOfBounds);
675        };
676        Ok(Self { data })
677    }
678}
679
680/// The fields at the start of the table, which are read directly.
681#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
682#[repr(C, packed)]
683#[doc(hidden)]
684pub struct AnchorFormat1FixedHeader {
685    pub anchor_format: BigEndian<u16>,
686    pub x_coordinate: BigEndian<i16>,
687    pub y_coordinate: BigEndian<i16>,
688}
689
690#[allow(clippy::absurd_extreme_comparisons)]
691const _: () = assert!(std::mem::size_of::<AnchorFormat1FixedHeader>() <= AnchorFormat1::MIN_SIZE);
692
693/// [Anchor Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-1-design-units): Design Units
694#[derive(Clone)]
695pub struct AnchorFormat1<'a> {
696    data: TableDataRef<'a, AnchorFormat1FixedHeader>,
697}
698
699#[allow(clippy::needless_lifetimes)]
700impl<'a> AnchorFormat1<'a> {
701    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
702    basic_table_impls!(impl_the_methods);
703
704    /// Format identifier, = 1
705    pub fn anchor_format(&self) -> u16 {
706        self.data.header().anchor_format.get()
707    }
708
709    /// Horizontal value, in design units
710    pub fn x_coordinate(&self) -> i16 {
711        self.data.header().x_coordinate.get()
712    }
713
714    /// Vertical value, in design units
715    pub fn y_coordinate(&self) -> i16 {
716        self.data.header().y_coordinate.get()
717    }
718
719    pub fn anchor_format_byte_range(&self) -> Range<usize> {
720        let start = 0;
721        let end = start + u16::RAW_BYTE_LEN;
722        start..end
723    }
724
725    pub fn x_coordinate_byte_range(&self) -> Range<usize> {
726        let start = self.anchor_format_byte_range().end;
727        let end = start + i16::RAW_BYTE_LEN;
728        start..end
729    }
730
731    pub fn y_coordinate_byte_range(&self) -> Range<usize> {
732        let start = self.x_coordinate_byte_range().end;
733        let end = start + i16::RAW_BYTE_LEN;
734        start..end
735    }
736}
737
738const _: () = assert!(FontData::default_data_long_enough(AnchorFormat1::MIN_SIZE));
739
740impl Default for AnchorFormat1<'_> {
741    fn default() -> Self {
742        Self {
743            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
744                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
745        }
746    }
747}
748
749impl Format<u16> for AnchorFormat2<'_> {
750    const FORMAT: u16 = 2;
751}
752
753impl<'a> MinByteRange<'a> for AnchorFormat2<'a> {
754    fn min_byte_range(&self) -> Range<usize> {
755        0..self.anchor_point_byte_range().end
756    }
757    fn min_table_bytes(&self) -> &'a [u8] {
758        let range = self.min_byte_range();
759        self.offset_data().as_bytes().get(range).unwrap_or_default()
760    }
761}
762
763impl ReadArgs for AnchorFormat2<'_> {
764    type Args = ();
765}
766
767impl<'a> FontRead<'a> for AnchorFormat2<'a> {
768    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
769        #[allow(clippy::absurd_extreme_comparisons)]
770        if data.len() < Self::MIN_SIZE {
771            return Err(ReadError::OutOfBounds);
772        }
773        let Some(data) = TableDataRef::new(data.as_bytes()) else {
774            return Err(ReadError::OutOfBounds);
775        };
776        Ok(Self { data })
777    }
778}
779
780/// The fields at the start of the table, which are read directly.
781#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
782#[repr(C, packed)]
783#[doc(hidden)]
784pub struct AnchorFormat2FixedHeader {
785    pub anchor_format: BigEndian<u16>,
786    pub x_coordinate: BigEndian<i16>,
787    pub y_coordinate: BigEndian<i16>,
788    pub anchor_point: BigEndian<u16>,
789}
790
791#[allow(clippy::absurd_extreme_comparisons)]
792const _: () = assert!(std::mem::size_of::<AnchorFormat2FixedHeader>() <= AnchorFormat2::MIN_SIZE);
793
794/// [Anchor Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-2-design-units-plus-contour-point): Design Units Plus Contour Point
795#[derive(Clone)]
796pub struct AnchorFormat2<'a> {
797    data: TableDataRef<'a, AnchorFormat2FixedHeader>,
798}
799
800#[allow(clippy::needless_lifetimes)]
801impl<'a> AnchorFormat2<'a> {
802    pub const MIN_SIZE: usize =
803        (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
804    basic_table_impls!(impl_the_methods);
805
806    /// Format identifier, = 2
807    pub fn anchor_format(&self) -> u16 {
808        self.data.header().anchor_format.get()
809    }
810
811    /// Horizontal value, in design units
812    pub fn x_coordinate(&self) -> i16 {
813        self.data.header().x_coordinate.get()
814    }
815
816    /// Vertical value, in design units
817    pub fn y_coordinate(&self) -> i16 {
818        self.data.header().y_coordinate.get()
819    }
820
821    /// Index to glyph contour point
822    pub fn anchor_point(&self) -> u16 {
823        self.data.header().anchor_point.get()
824    }
825
826    pub fn anchor_format_byte_range(&self) -> Range<usize> {
827        let start = 0;
828        let end = start + u16::RAW_BYTE_LEN;
829        start..end
830    }
831
832    pub fn x_coordinate_byte_range(&self) -> Range<usize> {
833        let start = self.anchor_format_byte_range().end;
834        let end = start + i16::RAW_BYTE_LEN;
835        start..end
836    }
837
838    pub fn y_coordinate_byte_range(&self) -> Range<usize> {
839        let start = self.x_coordinate_byte_range().end;
840        let end = start + i16::RAW_BYTE_LEN;
841        start..end
842    }
843
844    pub fn anchor_point_byte_range(&self) -> Range<usize> {
845        let start = self.y_coordinate_byte_range().end;
846        let end = start + u16::RAW_BYTE_LEN;
847        start..end
848    }
849}
850
851impl Format<u16> for AnchorFormat3<'_> {
852    const FORMAT: u16 = 3;
853}
854
855impl<'a> MinByteRange<'a> for AnchorFormat3<'a> {
856    fn min_byte_range(&self) -> Range<usize> {
857        0..self.y_device_offset_byte_range().end
858    }
859    fn min_table_bytes(&self) -> &'a [u8] {
860        let range = self.min_byte_range();
861        self.offset_data().as_bytes().get(range).unwrap_or_default()
862    }
863}
864
865impl ReadArgs for AnchorFormat3<'_> {
866    type Args = ();
867}
868
869impl<'a> FontRead<'a> for AnchorFormat3<'a> {
870    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
871        #[allow(clippy::absurd_extreme_comparisons)]
872        if data.len() < Self::MIN_SIZE {
873            return Err(ReadError::OutOfBounds);
874        }
875        let Some(data) = TableDataRef::new(data.as_bytes()) else {
876            return Err(ReadError::OutOfBounds);
877        };
878        Ok(Self { data })
879    }
880}
881
882/// The fields at the start of the table, which are read directly.
883#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
884#[repr(C, packed)]
885#[doc(hidden)]
886pub struct AnchorFormat3FixedHeader {
887    pub anchor_format: BigEndian<u16>,
888    pub x_coordinate: BigEndian<i16>,
889    pub y_coordinate: BigEndian<i16>,
890    pub x_device_offset: BigEndian<Nullable<Offset16>>,
891    pub y_device_offset: BigEndian<Nullable<Offset16>>,
892}
893
894#[allow(clippy::absurd_extreme_comparisons)]
895const _: () = assert!(std::mem::size_of::<AnchorFormat3FixedHeader>() <= AnchorFormat3::MIN_SIZE);
896
897/// [Anchor Table Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-3-design-units-plus-device-or-variationindex-tables): Design Units Plus Device or VariationIndex Tables
898#[derive(Clone)]
899pub struct AnchorFormat3<'a> {
900    data: TableDataRef<'a, AnchorFormat3FixedHeader>,
901}
902
903#[allow(clippy::needless_lifetimes)]
904impl<'a> AnchorFormat3<'a> {
905    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
906        + i16::RAW_BYTE_LEN
907        + i16::RAW_BYTE_LEN
908        + Offset16::RAW_BYTE_LEN
909        + Offset16::RAW_BYTE_LEN);
910    basic_table_impls!(impl_the_methods);
911
912    /// Format identifier, = 3
913    pub fn anchor_format(&self) -> u16 {
914        self.data.header().anchor_format.get()
915    }
916
917    /// Horizontal value, in design units
918    pub fn x_coordinate(&self) -> i16 {
919        self.data.header().x_coordinate.get()
920    }
921
922    /// Vertical value, in design units
923    pub fn y_coordinate(&self) -> i16 {
924        self.data.header().y_coordinate.get()
925    }
926
927    /// Offset to Device table (non-variable font) / VariationIndex
928    /// table (variable font) for X coordinate, from beginning of
929    /// Anchor table (may be NULL)
930    pub fn x_device_offset(&self) -> Nullable<Offset16> {
931        self.data.header().x_device_offset.get()
932    }
933
934    /// Attempt to resolve [`x_device_offset`][Self::x_device_offset].
935    pub fn x_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
936        let data = self.offset_data();
937        self.x_device_offset().resolve(data)
938    }
939
940    /// Offset to Device table (non-variable font) / VariationIndex
941    /// table (variable font) for Y coordinate, from beginning of
942    /// Anchor table (may be NULL)
943    pub fn y_device_offset(&self) -> Nullable<Offset16> {
944        self.data.header().y_device_offset.get()
945    }
946
947    /// Attempt to resolve [`y_device_offset`][Self::y_device_offset].
948    pub fn y_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
949        let data = self.offset_data();
950        self.y_device_offset().resolve(data)
951    }
952
953    pub fn anchor_format_byte_range(&self) -> Range<usize> {
954        let start = 0;
955        let end = start + u16::RAW_BYTE_LEN;
956        start..end
957    }
958
959    pub fn x_coordinate_byte_range(&self) -> Range<usize> {
960        let start = self.anchor_format_byte_range().end;
961        let end = start + i16::RAW_BYTE_LEN;
962        start..end
963    }
964
965    pub fn y_coordinate_byte_range(&self) -> Range<usize> {
966        let start = self.x_coordinate_byte_range().end;
967        let end = start + i16::RAW_BYTE_LEN;
968        start..end
969    }
970
971    pub fn x_device_offset_byte_range(&self) -> Range<usize> {
972        let start = self.y_coordinate_byte_range().end;
973        let end = start + Offset16::RAW_BYTE_LEN;
974        start..end
975    }
976
977    pub fn y_device_offset_byte_range(&self) -> Range<usize> {
978        let start = self.x_device_offset_byte_range().end;
979        let end = start + Offset16::RAW_BYTE_LEN;
980        start..end
981    }
982}
983
984impl<'a> MinByteRange<'a> for MarkArray<'a> {
985    fn min_byte_range(&self) -> Range<usize> {
986        0..self.mark_records_byte_range().end
987    }
988    fn min_table_bytes(&self) -> &'a [u8] {
989        let range = self.min_byte_range();
990        self.offset_data().as_bytes().get(range).unwrap_or_default()
991    }
992}
993
994impl ReadArgs for MarkArray<'_> {
995    type Args = ();
996}
997
998impl<'a> FontRead<'a> for MarkArray<'a> {
999    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1000        #[allow(clippy::absurd_extreme_comparisons)]
1001        if data.len() < Self::MIN_SIZE {
1002            return Err(ReadError::OutOfBounds);
1003        }
1004        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1005            return Err(ReadError::OutOfBounds);
1006        };
1007        Ok(Self { data })
1008    }
1009}
1010
1011/// The fields at the start of the table, which are read directly.
1012#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1013#[repr(C, packed)]
1014#[doc(hidden)]
1015pub struct MarkArrayFixedHeader {
1016    pub mark_count: BigEndian<u16>,
1017}
1018
1019#[allow(clippy::absurd_extreme_comparisons)]
1020const _: () = assert!(std::mem::size_of::<MarkArrayFixedHeader>() <= MarkArray::MIN_SIZE);
1021
1022/// [Mark Array Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table)
1023#[derive(Clone)]
1024pub struct MarkArray<'a> {
1025    data: TableDataRef<'a, MarkArrayFixedHeader>,
1026}
1027
1028#[allow(clippy::needless_lifetimes)]
1029impl<'a> MarkArray<'a> {
1030    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1031    basic_table_impls!(impl_the_methods);
1032
1033    /// Number of MarkRecords
1034    pub fn mark_count(&self) -> u16 {
1035        self.data.header().mark_count.get()
1036    }
1037
1038    /// Array of MarkRecords, ordered by corresponding glyphs in the
1039    /// associated mark Coverage table.
1040    pub fn mark_records(&self) -> &'a [MarkRecord] {
1041        let range = self.mark_records_byte_range();
1042        self.offset_data()
1043            .read_array(range)
1044            .ok()
1045            .unwrap_or_default()
1046    }
1047
1048    pub fn mark_count_byte_range(&self) -> Range<usize> {
1049        let start = 0;
1050        let end = start + u16::RAW_BYTE_LEN;
1051        start..end
1052    }
1053
1054    pub fn mark_records_byte_range(&self) -> Range<usize> {
1055        let mark_count = self.mark_count();
1056        let start = self.mark_count_byte_range().end;
1057        let end =
1058            start + (transforms::to_usize(mark_count)).saturating_mul(MarkRecord::RAW_BYTE_LEN);
1059        start..end
1060    }
1061}
1062
1063const _: () = assert!(FontData::default_data_long_enough(MarkArray::MIN_SIZE));
1064
1065impl Default for MarkArray<'_> {
1066    fn default() -> Self {
1067        Self {
1068            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1069                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1070        }
1071    }
1072}
1073
1074/// Part of [MarkArray]
1075#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
1076#[repr(C)]
1077#[repr(packed)]
1078pub struct MarkRecord {
1079    /// Class defined for the associated mark.
1080    pub mark_class: BigEndian<u16>,
1081    /// Offset to Anchor table, from beginning of MarkArray table.
1082    pub mark_anchor_offset: BigEndian<Offset16>,
1083}
1084
1085impl MarkRecord {
1086    /// Class defined for the associated mark.
1087    pub fn mark_class(&self) -> u16 {
1088        self.mark_class.get()
1089    }
1090
1091    /// Offset to Anchor table, from beginning of MarkArray table.
1092    pub fn mark_anchor_offset(&self) -> Offset16 {
1093        self.mark_anchor_offset.get()
1094    }
1095
1096    /// Offset to Anchor table, from beginning of MarkArray table.
1097    ///
1098    /// The `data` argument should be retrieved from the parent table
1099    /// By calling its `offset_data` method.
1100    pub fn mark_anchor<'a>(&self, data: FontData<'a>) -> Result<AnchorTable<'a>, ReadError> {
1101        self.mark_anchor_offset().resolve(data)
1102    }
1103}
1104
1105impl FixedSize for MarkRecord {
1106    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
1107}
1108
1109/// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable
1110#[derive(Clone)]
1111pub enum SinglePos<'a> {
1112    Format1(SinglePosFormat1<'a>),
1113    Format2(SinglePosFormat2<'a>),
1114}
1115
1116impl Default for SinglePos<'_> {
1117    fn default() -> Self {
1118        Self::Format1(Default::default())
1119    }
1120}
1121
1122impl<'a> SinglePos<'a> {
1123    ///Return the `FontData` used to resolve offsets for this table.
1124    pub fn offset_data(&self) -> FontData<'a> {
1125        match self {
1126            Self::Format1(item) => item.offset_data(),
1127            Self::Format2(item) => item.offset_data(),
1128        }
1129    }
1130
1131    /// Format identifier: format = 1
1132    pub fn pos_format(&self) -> u16 {
1133        match self {
1134            Self::Format1(item) => item.pos_format(),
1135            Self::Format2(item) => item.pos_format(),
1136        }
1137    }
1138
1139    /// Offset to Coverage table, from beginning of SinglePos subtable.
1140    pub fn coverage_offset(&self) -> Offset16 {
1141        match self {
1142            Self::Format1(item) => item.coverage_offset(),
1143            Self::Format2(item) => item.coverage_offset(),
1144        }
1145    }
1146
1147    /// Defines the types of data in the ValueRecord.
1148    pub fn value_format(&self) -> ValueFormat {
1149        match self {
1150            Self::Format1(item) => item.value_format(),
1151            Self::Format2(item) => item.value_format(),
1152        }
1153    }
1154}
1155
1156impl ReadArgs for SinglePos<'_> {
1157    type Args = ();
1158}
1159
1160impl<'a> FontRead<'a> for SinglePos<'a> {
1161    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1162        let format: u16 = data.read_at(0usize)?;
1163        match format {
1164            SinglePosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1165            SinglePosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1166            other => Err(ReadError::InvalidFormat(other.into())),
1167        }
1168    }
1169}
1170
1171impl<'a> MinByteRange<'a> for SinglePos<'a> {
1172    fn min_byte_range(&self) -> Range<usize> {
1173        match self {
1174            Self::Format1(item) => item.min_byte_range(),
1175            Self::Format2(item) => item.min_byte_range(),
1176        }
1177    }
1178    fn min_table_bytes(&self) -> &'a [u8] {
1179        match self {
1180            Self::Format1(item) => item.min_table_bytes(),
1181            Self::Format2(item) => item.min_table_bytes(),
1182        }
1183    }
1184}
1185
1186impl Format<u16> for SinglePosFormat1<'_> {
1187    const FORMAT: u16 = 1;
1188}
1189
1190impl<'a> MinByteRange<'a> for SinglePosFormat1<'a> {
1191    fn min_byte_range(&self) -> Range<usize> {
1192        0..self.value_record_byte_range().end
1193    }
1194    fn min_table_bytes(&self) -> &'a [u8] {
1195        let range = self.min_byte_range();
1196        self.offset_data().as_bytes().get(range).unwrap_or_default()
1197    }
1198}
1199
1200impl ReadArgs for SinglePosFormat1<'_> {
1201    type Args = ();
1202}
1203
1204impl<'a> FontRead<'a> for SinglePosFormat1<'a> {
1205    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1206        #[allow(clippy::absurd_extreme_comparisons)]
1207        if data.len() < Self::MIN_SIZE {
1208            return Err(ReadError::OutOfBounds);
1209        }
1210        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1211            return Err(ReadError::OutOfBounds);
1212        };
1213        Ok(Self { data })
1214    }
1215}
1216
1217/// The fields at the start of the table, which are read directly.
1218#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1219#[repr(C, packed)]
1220#[doc(hidden)]
1221pub struct SinglePosFormat1FixedHeader {
1222    pub pos_format: BigEndian<u16>,
1223    pub coverage_offset: BigEndian<Offset16>,
1224    pub value_format: BigEndian<ValueFormat>,
1225}
1226
1227#[allow(clippy::absurd_extreme_comparisons)]
1228const _: () =
1229    assert!(std::mem::size_of::<SinglePosFormat1FixedHeader>() <= SinglePosFormat1::MIN_SIZE);
1230
1231/// [Single Adjustment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#single-adjustment-positioning-format-1-single-positioning-value): Single Positioning Value
1232#[derive(Clone)]
1233pub struct SinglePosFormat1<'a> {
1234    data: TableDataRef<'a, SinglePosFormat1FixedHeader>,
1235}
1236
1237#[allow(clippy::needless_lifetimes)]
1238impl<'a> SinglePosFormat1<'a> {
1239    pub const MIN_SIZE: usize =
1240        (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + ValueFormat::RAW_BYTE_LEN);
1241    basic_table_impls!(impl_the_methods);
1242
1243    /// Format identifier: format = 1
1244    pub fn pos_format(&self) -> u16 {
1245        self.data.header().pos_format.get()
1246    }
1247
1248    /// Offset to Coverage table, from beginning of SinglePos subtable.
1249    pub fn coverage_offset(&self) -> Offset16 {
1250        self.data.header().coverage_offset.get()
1251    }
1252
1253    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1254    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1255        let data = self.offset_data();
1256        self.coverage_offset().resolve(data)
1257    }
1258
1259    /// Defines the types of data in the ValueRecord.
1260    pub fn value_format(&self) -> ValueFormat {
1261        self.data.header().value_format.get()
1262    }
1263
1264    /// Defines positioning value(s) — applied to all glyphs in the
1265    /// Coverage table.
1266    pub fn value_record(&self) -> ValueRecord<'a> {
1267        let range = self.value_record_byte_range();
1268        ValueRecord::read_at(self.offset_data(), range.start, self.value_format())
1269            .unwrap_or_default()
1270    }
1271
1272    pub fn pos_format_byte_range(&self) -> Range<usize> {
1273        let start = 0;
1274        let end = start + u16::RAW_BYTE_LEN;
1275        start..end
1276    }
1277
1278    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1279        let start = self.pos_format_byte_range().end;
1280        let end = start + Offset16::RAW_BYTE_LEN;
1281        start..end
1282    }
1283
1284    pub fn value_format_byte_range(&self) -> Range<usize> {
1285        let start = self.coverage_offset_byte_range().end;
1286        let end = start + ValueFormat::RAW_BYTE_LEN;
1287        start..end
1288    }
1289
1290    pub fn value_record_byte_range(&self) -> Range<usize> {
1291        let start = self.value_format_byte_range().end;
1292        let end =
1293            start + <ValueRecord as ComputeSize>::compute_size(self.value_format()).unwrap_or(0);
1294        start..end
1295    }
1296}
1297
1298const _: () = assert!(FontData::default_data_long_enough(
1299    SinglePosFormat1::MIN_SIZE
1300));
1301
1302impl Default for SinglePosFormat1<'_> {
1303    fn default() -> Self {
1304        Self {
1305            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1306                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1307        }
1308    }
1309}
1310
1311impl Format<u16> for SinglePosFormat2<'_> {
1312    const FORMAT: u16 = 2;
1313}
1314
1315impl<'a> MinByteRange<'a> for SinglePosFormat2<'a> {
1316    fn min_byte_range(&self) -> Range<usize> {
1317        0..self.value_records_byte_range().end
1318    }
1319    fn min_table_bytes(&self) -> &'a [u8] {
1320        let range = self.min_byte_range();
1321        self.offset_data().as_bytes().get(range).unwrap_or_default()
1322    }
1323}
1324
1325impl ReadArgs for SinglePosFormat2<'_> {
1326    type Args = ();
1327}
1328
1329impl<'a> FontRead<'a> for SinglePosFormat2<'a> {
1330    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1331        #[allow(clippy::absurd_extreme_comparisons)]
1332        if data.len() < Self::MIN_SIZE {
1333            return Err(ReadError::OutOfBounds);
1334        }
1335        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1336            return Err(ReadError::OutOfBounds);
1337        };
1338        Ok(Self { data })
1339    }
1340}
1341
1342/// The fields at the start of the table, which are read directly.
1343#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1344#[repr(C, packed)]
1345#[doc(hidden)]
1346pub struct SinglePosFormat2FixedHeader {
1347    pub pos_format: BigEndian<u16>,
1348    pub coverage_offset: BigEndian<Offset16>,
1349    pub value_format: BigEndian<ValueFormat>,
1350    pub value_count: BigEndian<u16>,
1351}
1352
1353#[allow(clippy::absurd_extreme_comparisons)]
1354const _: () =
1355    assert!(std::mem::size_of::<SinglePosFormat2FixedHeader>() <= SinglePosFormat2::MIN_SIZE);
1356
1357/// [Single Adjustment Positioning Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#single-adjustment-positioning-format-2-array-of-positioning-values): Array of Positioning Values
1358#[derive(Clone)]
1359pub struct SinglePosFormat2<'a> {
1360    data: TableDataRef<'a, SinglePosFormat2FixedHeader>,
1361}
1362
1363#[allow(clippy::needless_lifetimes)]
1364impl<'a> SinglePosFormat2<'a> {
1365    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1366        + Offset16::RAW_BYTE_LEN
1367        + ValueFormat::RAW_BYTE_LEN
1368        + u16::RAW_BYTE_LEN);
1369    basic_table_impls!(impl_the_methods);
1370
1371    /// Format identifier: format = 2
1372    pub fn pos_format(&self) -> u16 {
1373        self.data.header().pos_format.get()
1374    }
1375
1376    /// Offset to Coverage table, from beginning of SinglePos subtable.
1377    pub fn coverage_offset(&self) -> Offset16 {
1378        self.data.header().coverage_offset.get()
1379    }
1380
1381    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1382    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1383        let data = self.offset_data();
1384        self.coverage_offset().resolve(data)
1385    }
1386
1387    /// Defines the types of data in the ValueRecords.
1388    pub fn value_format(&self) -> ValueFormat {
1389        self.data.header().value_format.get()
1390    }
1391
1392    /// Number of ValueRecords — must equal glyphCount in the
1393    /// Coverage table.
1394    pub fn value_count(&self) -> u16 {
1395        self.data.header().value_count.get()
1396    }
1397
1398    /// Array of ValueRecords — positioning values applied to glyphs.
1399    pub fn value_records(&self) -> ComputedArray<'a, ValueRecord<'a>> {
1400        let range = self.value_records_byte_range();
1401        ComputedArray::new(self.offset_data(), range, self.value_format()).unwrap_or_default()
1402    }
1403
1404    pub fn pos_format_byte_range(&self) -> Range<usize> {
1405        let start = 0;
1406        let end = start + u16::RAW_BYTE_LEN;
1407        start..end
1408    }
1409
1410    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1411        let start = self.pos_format_byte_range().end;
1412        let end = start + Offset16::RAW_BYTE_LEN;
1413        start..end
1414    }
1415
1416    pub fn value_format_byte_range(&self) -> Range<usize> {
1417        let start = self.coverage_offset_byte_range().end;
1418        let end = start + ValueFormat::RAW_BYTE_LEN;
1419        start..end
1420    }
1421
1422    pub fn value_count_byte_range(&self) -> Range<usize> {
1423        let start = self.value_format_byte_range().end;
1424        let end = start + u16::RAW_BYTE_LEN;
1425        start..end
1426    }
1427
1428    pub fn value_records_byte_range(&self) -> Range<usize> {
1429        let value_count = self.value_count();
1430        let start = self.value_count_byte_range().end;
1431        let end = start
1432            + (transforms::to_usize(value_count)).saturating_mul(
1433                <ValueRecord as ComputeSize>::compute_size(self.value_format()).unwrap_or(0),
1434            );
1435        start..end
1436    }
1437}
1438
1439/// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable
1440#[derive(Clone)]
1441pub enum PairPos<'a> {
1442    Format1(PairPosFormat1<'a>),
1443    Format2(PairPosFormat2<'a>),
1444}
1445
1446impl Default for PairPos<'_> {
1447    fn default() -> Self {
1448        Self::Format1(Default::default())
1449    }
1450}
1451
1452impl<'a> PairPos<'a> {
1453    ///Return the `FontData` used to resolve offsets for this table.
1454    pub fn offset_data(&self) -> FontData<'a> {
1455        match self {
1456            Self::Format1(item) => item.offset_data(),
1457            Self::Format2(item) => item.offset_data(),
1458        }
1459    }
1460
1461    /// Format identifier: format = 1
1462    pub fn pos_format(&self) -> u16 {
1463        match self {
1464            Self::Format1(item) => item.pos_format(),
1465            Self::Format2(item) => item.pos_format(),
1466        }
1467    }
1468
1469    /// Offset to Coverage table, from beginning of PairPos subtable.
1470    pub fn coverage_offset(&self) -> Offset16 {
1471        match self {
1472            Self::Format1(item) => item.coverage_offset(),
1473            Self::Format2(item) => item.coverage_offset(),
1474        }
1475    }
1476
1477    /// Defines the types of data in valueRecord1 — for the first
1478    /// glyph in the pair (may be zero).
1479    pub fn value_format1(&self) -> ValueFormat {
1480        match self {
1481            Self::Format1(item) => item.value_format1(),
1482            Self::Format2(item) => item.value_format1(),
1483        }
1484    }
1485
1486    /// Defines the types of data in valueRecord2 — for the second
1487    /// glyph in the pair (may be zero).
1488    pub fn value_format2(&self) -> ValueFormat {
1489        match self {
1490            Self::Format1(item) => item.value_format2(),
1491            Self::Format2(item) => item.value_format2(),
1492        }
1493    }
1494}
1495
1496impl ReadArgs for PairPos<'_> {
1497    type Args = ();
1498}
1499
1500impl<'a> FontRead<'a> for PairPos<'a> {
1501    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1502        let format: u16 = data.read_at(0usize)?;
1503        match format {
1504            PairPosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1505            PairPosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1506            other => Err(ReadError::InvalidFormat(other.into())),
1507        }
1508    }
1509}
1510
1511impl<'a> MinByteRange<'a> for PairPos<'a> {
1512    fn min_byte_range(&self) -> Range<usize> {
1513        match self {
1514            Self::Format1(item) => item.min_byte_range(),
1515            Self::Format2(item) => item.min_byte_range(),
1516        }
1517    }
1518    fn min_table_bytes(&self) -> &'a [u8] {
1519        match self {
1520            Self::Format1(item) => item.min_table_bytes(),
1521            Self::Format2(item) => item.min_table_bytes(),
1522        }
1523    }
1524}
1525
1526impl Format<u16> for PairPosFormat1<'_> {
1527    const FORMAT: u16 = 1;
1528}
1529
1530impl<'a> MinByteRange<'a> for PairPosFormat1<'a> {
1531    fn min_byte_range(&self) -> Range<usize> {
1532        0..self.pair_set_offsets_byte_range().end
1533    }
1534    fn min_table_bytes(&self) -> &'a [u8] {
1535        let range = self.min_byte_range();
1536        self.offset_data().as_bytes().get(range).unwrap_or_default()
1537    }
1538}
1539
1540impl ReadArgs for PairPosFormat1<'_> {
1541    type Args = ();
1542}
1543
1544impl<'a> FontRead<'a> for PairPosFormat1<'a> {
1545    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1546        #[allow(clippy::absurd_extreme_comparisons)]
1547        if data.len() < Self::MIN_SIZE {
1548            return Err(ReadError::OutOfBounds);
1549        }
1550        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1551            return Err(ReadError::OutOfBounds);
1552        };
1553        Ok(Self { data })
1554    }
1555}
1556
1557/// The fields at the start of the table, which are read directly.
1558#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1559#[repr(C, packed)]
1560#[doc(hidden)]
1561pub struct PairPosFormat1FixedHeader {
1562    pub pos_format: BigEndian<u16>,
1563    pub coverage_offset: BigEndian<Offset16>,
1564    pub value_format1: BigEndian<ValueFormat>,
1565    pub value_format2: BigEndian<ValueFormat>,
1566    pub pair_set_count: BigEndian<u16>,
1567}
1568
1569#[allow(clippy::absurd_extreme_comparisons)]
1570const _: () = assert!(std::mem::size_of::<PairPosFormat1FixedHeader>() <= PairPosFormat1::MIN_SIZE);
1571
1572/// [Pair Adjustment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-1-adjustments-for-glyph-pairs): Adjustments for Glyph Pairs
1573#[derive(Clone)]
1574pub struct PairPosFormat1<'a> {
1575    data: TableDataRef<'a, PairPosFormat1FixedHeader>,
1576}
1577
1578#[allow(clippy::needless_lifetimes)]
1579impl<'a> PairPosFormat1<'a> {
1580    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1581        + Offset16::RAW_BYTE_LEN
1582        + ValueFormat::RAW_BYTE_LEN
1583        + ValueFormat::RAW_BYTE_LEN
1584        + u16::RAW_BYTE_LEN);
1585    basic_table_impls!(impl_the_methods);
1586
1587    /// Format identifier: format = 1
1588    pub fn pos_format(&self) -> u16 {
1589        self.data.header().pos_format.get()
1590    }
1591
1592    /// Offset to Coverage table, from beginning of PairPos subtable.
1593    pub fn coverage_offset(&self) -> Offset16 {
1594        self.data.header().coverage_offset.get()
1595    }
1596
1597    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1598    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1599        let data = self.offset_data();
1600        self.coverage_offset().resolve(data)
1601    }
1602
1603    /// Defines the types of data in valueRecord1 — for the first
1604    /// glyph in the pair (may be zero).
1605    pub fn value_format1(&self) -> ValueFormat {
1606        self.data.header().value_format1.get()
1607    }
1608
1609    /// Defines the types of data in valueRecord2 — for the second
1610    /// glyph in the pair (may be zero).
1611    pub fn value_format2(&self) -> ValueFormat {
1612        self.data.header().value_format2.get()
1613    }
1614
1615    /// Number of PairSet tables
1616    pub fn pair_set_count(&self) -> u16 {
1617        self.data.header().pair_set_count.get()
1618    }
1619
1620    /// Array of offsets to PairSet tables. Offsets are from beginning
1621    /// of PairPos subtable, ordered by Coverage Index.
1622    pub fn pair_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
1623        let range = self.pair_set_offsets_byte_range();
1624        self.offset_data()
1625            .read_array(range)
1626            .ok()
1627            .unwrap_or_default()
1628    }
1629
1630    /// A dynamically resolving wrapper for [`pair_set_offsets`][Self::pair_set_offsets].
1631    pub fn pair_sets(&self) -> ArrayOfOffsets<'a, PairSet<'a>, Offset16> {
1632        let data = self.offset_data();
1633        let offsets = self.pair_set_offsets();
1634        let args = (self.value_format1(), self.value_format2());
1635        ArrayOfOffsets::new(offsets, data, args)
1636    }
1637
1638    pub fn pos_format_byte_range(&self) -> Range<usize> {
1639        let start = 0;
1640        let end = start + u16::RAW_BYTE_LEN;
1641        start..end
1642    }
1643
1644    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1645        let start = self.pos_format_byte_range().end;
1646        let end = start + Offset16::RAW_BYTE_LEN;
1647        start..end
1648    }
1649
1650    pub fn value_format1_byte_range(&self) -> Range<usize> {
1651        let start = self.coverage_offset_byte_range().end;
1652        let end = start + ValueFormat::RAW_BYTE_LEN;
1653        start..end
1654    }
1655
1656    pub fn value_format2_byte_range(&self) -> Range<usize> {
1657        let start = self.value_format1_byte_range().end;
1658        let end = start + ValueFormat::RAW_BYTE_LEN;
1659        start..end
1660    }
1661
1662    pub fn pair_set_count_byte_range(&self) -> Range<usize> {
1663        let start = self.value_format2_byte_range().end;
1664        let end = start + u16::RAW_BYTE_LEN;
1665        start..end
1666    }
1667
1668    pub fn pair_set_offsets_byte_range(&self) -> Range<usize> {
1669        let pair_set_count = self.pair_set_count();
1670        let start = self.pair_set_count_byte_range().end;
1671        let end =
1672            start + (transforms::to_usize(pair_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1673        start..end
1674    }
1675}
1676
1677const _: () = assert!(FontData::default_data_long_enough(PairPosFormat1::MIN_SIZE));
1678
1679impl Default for PairPosFormat1<'_> {
1680    fn default() -> Self {
1681        Self {
1682            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1683                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1684        }
1685    }
1686}
1687
1688impl<'a> MinByteRange<'a> for PairSet<'a> {
1689    fn min_byte_range(&self) -> Range<usize> {
1690        0..self.pair_value_records_byte_range().end
1691    }
1692    fn min_table_bytes(&self) -> &'a [u8] {
1693        let range = self.min_byte_range();
1694        self.offset_data().as_bytes().get(range).unwrap_or_default()
1695    }
1696}
1697
1698impl ReadArgs for PairSet<'_> {
1699    type Args = (ValueFormat, ValueFormat);
1700}
1701
1702impl<'a> FontRead<'a> for PairSet<'a> {
1703    fn read_with_args(
1704        data: FontData<'a>,
1705        args: (ValueFormat, ValueFormat),
1706    ) -> Result<Self, ReadError> {
1707        let (value_format1, value_format2) = args;
1708
1709        #[allow(clippy::absurd_extreme_comparisons)]
1710        if data.len() < Self::MIN_SIZE {
1711            return Err(ReadError::OutOfBounds);
1712        }
1713        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1714            return Err(ReadError::OutOfBounds);
1715        };
1716        Ok(Self {
1717            data,
1718            value_format1,
1719            value_format2,
1720        })
1721    }
1722}
1723
1724impl<'a> PairSet<'a> {
1725    /// A constructor that requires additional arguments.
1726    ///
1727    /// This type requires some external state in order to be
1728    /// parsed.
1729    pub fn read(
1730        data: FontData<'a>,
1731        value_format1: ValueFormat,
1732        value_format2: ValueFormat,
1733    ) -> Result<Self, ReadError> {
1734        let args = (value_format1, value_format2);
1735        Self::read_with_args(data, args)
1736    }
1737}
1738
1739/// The fields at the start of the table, which are read directly.
1740#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1741#[repr(C, packed)]
1742#[doc(hidden)]
1743pub struct PairSetFixedHeader {
1744    pub pair_value_count: BigEndian<u16>,
1745}
1746
1747#[allow(clippy::absurd_extreme_comparisons)]
1748const _: () = assert!(std::mem::size_of::<PairSetFixedHeader>() <= PairSet::MIN_SIZE);
1749
1750/// Part of [PairPosFormat1]
1751#[derive(Clone)]
1752pub struct PairSet<'a> {
1753    data: TableDataRef<'a, PairSetFixedHeader>,
1754    value_format1: ValueFormat,
1755    value_format2: ValueFormat,
1756}
1757
1758#[allow(clippy::needless_lifetimes)]
1759impl<'a> PairSet<'a> {
1760    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1761    basic_table_impls!(impl_the_methods);
1762
1763    /// Number of PairValueRecords
1764    pub fn pair_value_count(&self) -> u16 {
1765        self.data.header().pair_value_count.get()
1766    }
1767
1768    /// Array of PairValueRecords, ordered by glyph ID of the second
1769    /// glyph.
1770    pub fn pair_value_records(&self) -> ComputedArray<'a, PairValueRecord<'a>> {
1771        let range = self.pair_value_records_byte_range();
1772        ComputedArray::new(
1773            self.offset_data(),
1774            range,
1775            (self.value_format1(), self.value_format2()),
1776        )
1777        .unwrap_or_default()
1778    }
1779
1780    pub(crate) fn value_format1(&self) -> ValueFormat {
1781        self.value_format1
1782    }
1783
1784    pub(crate) fn value_format2(&self) -> ValueFormat {
1785        self.value_format2
1786    }
1787
1788    pub fn pair_value_count_byte_range(&self) -> Range<usize> {
1789        let start = 0;
1790        let end = start + u16::RAW_BYTE_LEN;
1791        start..end
1792    }
1793
1794    pub fn pair_value_records_byte_range(&self) -> Range<usize> {
1795        let pair_value_count = self.pair_value_count();
1796        let start = self.pair_value_count_byte_range().end;
1797        let end = start
1798            + (transforms::to_usize(pair_value_count)).saturating_mul(
1799                <PairValueRecord as ComputeSize>::compute_size((
1800                    self.value_format1(),
1801                    self.value_format2(),
1802                ))
1803                .unwrap_or(0),
1804            );
1805        start..end
1806    }
1807}
1808
1809const _: () = assert!(FontData::default_data_long_enough(PairSet::MIN_SIZE));
1810
1811impl Default for PairSet<'_> {
1812    fn default() -> Self {
1813        Self {
1814            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1815                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1816            value_format1: Default::default(),
1817            value_format2: Default::default(),
1818        }
1819    }
1820}
1821
1822/// Part of [PairSet]
1823#[derive(Clone, Debug)]
1824pub struct PairValueRecord<'a> {
1825    /// Glyph ID of second glyph in the pair (first glyph is listed in
1826    /// the Coverage table).
1827    pub second_glyph: BigEndian<GlyphId16>,
1828    /// Positioning data for the first glyph in the pair.
1829    pub value_record1: ValueRecord<'a>,
1830    /// Positioning data for the second glyph in the pair.
1831    pub value_record2: ValueRecord<'a>,
1832}
1833
1834impl<'a> PairValueRecord<'a> {
1835    /// Glyph ID of second glyph in the pair (first glyph is listed in
1836    /// the Coverage table).
1837    pub fn second_glyph(&self) -> GlyphId16 {
1838        self.second_glyph.get()
1839    }
1840
1841    /// Positioning data for the first glyph in the pair.
1842    pub fn value_record1(&self) -> &ValueRecord<'a> {
1843        &self.value_record1
1844    }
1845
1846    /// Positioning data for the second glyph in the pair.
1847    pub fn value_record2(&self) -> &ValueRecord<'a> {
1848        &self.value_record2
1849    }
1850}
1851
1852impl ReadArgs for PairValueRecord<'_> {
1853    type Args = (ValueFormat, ValueFormat);
1854}
1855
1856impl ComputeSize for PairValueRecord<'_> {
1857    #[allow(clippy::needless_question_mark)]
1858    fn compute_size(args: (ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
1859        let (value_format1, value_format2) = args;
1860        let mut result = 0usize;
1861        result = result
1862            .checked_add(GlyphId16::RAW_BYTE_LEN)
1863            .ok_or(ReadError::OutOfBounds)?;
1864        result = result
1865            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format1).unwrap_or(0))
1866            .ok_or(ReadError::OutOfBounds)?;
1867        result = result
1868            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format2).unwrap_or(0))
1869            .ok_or(ReadError::OutOfBounds)?;
1870        Ok(result)
1871    }
1872}
1873
1874impl<'a> FontReadAt<'a> for PairValueRecord<'a> {
1875    fn read_at(
1876        data: FontData<'a>,
1877        offset: usize,
1878        args: (ValueFormat, ValueFormat),
1879    ) -> Result<Self, ReadError> {
1880        let mut cursor = data.cursor();
1881        cursor.advance_by(offset);
1882        let (value_format1, value_format2) = args;
1883        Ok(Self {
1884            second_glyph: cursor.read_be()?,
1885            value_record1: cursor.read_at_with_args(value_format1)?,
1886            value_record2: cursor.read_at_with_args(value_format2)?,
1887        })
1888    }
1889}
1890
1891#[allow(clippy::needless_lifetimes)]
1892impl<'a> PairValueRecord<'a> {
1893    /// A constructor that requires additional arguments.
1894    ///
1895    /// This type requires some external state in order to be
1896    /// parsed.
1897    pub fn read(
1898        data: FontData<'a>,
1899        offset: usize,
1900        value_format1: ValueFormat,
1901        value_format2: ValueFormat,
1902    ) -> Result<Self, ReadError> {
1903        let args = (value_format1, value_format2);
1904        Self::read_at(data, offset, args)
1905    }
1906}
1907
1908impl Format<u16> for PairPosFormat2<'_> {
1909    const FORMAT: u16 = 2;
1910}
1911
1912impl<'a> MinByteRange<'a> for PairPosFormat2<'a> {
1913    fn min_byte_range(&self) -> Range<usize> {
1914        0..self.class1_records_byte_range().end
1915    }
1916    fn min_table_bytes(&self) -> &'a [u8] {
1917        let range = self.min_byte_range();
1918        self.offset_data().as_bytes().get(range).unwrap_or_default()
1919    }
1920}
1921
1922impl ReadArgs for PairPosFormat2<'_> {
1923    type Args = ();
1924}
1925
1926impl<'a> FontRead<'a> for PairPosFormat2<'a> {
1927    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1928        #[allow(clippy::absurd_extreme_comparisons)]
1929        if data.len() < Self::MIN_SIZE {
1930            return Err(ReadError::OutOfBounds);
1931        }
1932        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1933            return Err(ReadError::OutOfBounds);
1934        };
1935        Ok(Self { data })
1936    }
1937}
1938
1939/// The fields at the start of the table, which are read directly.
1940#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1941#[repr(C, packed)]
1942#[doc(hidden)]
1943pub struct PairPosFormat2FixedHeader {
1944    pub pos_format: BigEndian<u16>,
1945    pub coverage_offset: BigEndian<Offset16>,
1946    pub value_format1: BigEndian<ValueFormat>,
1947    pub value_format2: BigEndian<ValueFormat>,
1948    pub class_def1_offset: BigEndian<Offset16>,
1949    pub class_def2_offset: BigEndian<Offset16>,
1950    pub class1_count: BigEndian<u16>,
1951    pub class2_count: BigEndian<u16>,
1952}
1953
1954#[allow(clippy::absurd_extreme_comparisons)]
1955const _: () = assert!(std::mem::size_of::<PairPosFormat2FixedHeader>() <= PairPosFormat2::MIN_SIZE);
1956
1957/// [Pair Adjustment Positioning Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-2-class-pair-adjustment): Class Pair Adjustment
1958#[derive(Clone)]
1959pub struct PairPosFormat2<'a> {
1960    data: TableDataRef<'a, PairPosFormat2FixedHeader>,
1961}
1962
1963#[allow(clippy::needless_lifetimes)]
1964impl<'a> PairPosFormat2<'a> {
1965    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1966        + Offset16::RAW_BYTE_LEN
1967        + ValueFormat::RAW_BYTE_LEN
1968        + ValueFormat::RAW_BYTE_LEN
1969        + Offset16::RAW_BYTE_LEN
1970        + Offset16::RAW_BYTE_LEN
1971        + u16::RAW_BYTE_LEN
1972        + u16::RAW_BYTE_LEN);
1973    basic_table_impls!(impl_the_methods);
1974
1975    /// Format identifier: format = 2
1976    pub fn pos_format(&self) -> u16 {
1977        self.data.header().pos_format.get()
1978    }
1979
1980    /// Offset to Coverage table, from beginning of PairPos subtable.
1981    pub fn coverage_offset(&self) -> Offset16 {
1982        self.data.header().coverage_offset.get()
1983    }
1984
1985    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1986    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1987        let data = self.offset_data();
1988        self.coverage_offset().resolve(data)
1989    }
1990
1991    /// ValueRecord definition — for the first glyph of the pair (may
1992    /// be zero).
1993    pub fn value_format1(&self) -> ValueFormat {
1994        self.data.header().value_format1.get()
1995    }
1996
1997    /// ValueRecord definition — for the second glyph of the pair
1998    /// (may be zero).
1999    pub fn value_format2(&self) -> ValueFormat {
2000        self.data.header().value_format2.get()
2001    }
2002
2003    /// Offset to ClassDef table, from beginning of PairPos subtable
2004    /// — for the first glyph of the pair.
2005    pub fn class_def1_offset(&self) -> Offset16 {
2006        self.data.header().class_def1_offset.get()
2007    }
2008
2009    /// Attempt to resolve [`class_def1_offset`][Self::class_def1_offset].
2010    pub fn class_def1(&self) -> Result<ClassDef<'a>, ReadError> {
2011        let data = self.offset_data();
2012        self.class_def1_offset().resolve(data)
2013    }
2014
2015    /// Offset to ClassDef table, from beginning of PairPos subtable
2016    /// — for the second glyph of the pair.
2017    pub fn class_def2_offset(&self) -> Offset16 {
2018        self.data.header().class_def2_offset.get()
2019    }
2020
2021    /// Attempt to resolve [`class_def2_offset`][Self::class_def2_offset].
2022    pub fn class_def2(&self) -> Result<ClassDef<'a>, ReadError> {
2023        let data = self.offset_data();
2024        self.class_def2_offset().resolve(data)
2025    }
2026
2027    /// Number of classes in classDef1 table — includes Class 0.
2028    pub fn class1_count(&self) -> u16 {
2029        self.data.header().class1_count.get()
2030    }
2031
2032    /// Number of classes in classDef2 table — includes Class 0.
2033    pub fn class2_count(&self) -> u16 {
2034        self.data.header().class2_count.get()
2035    }
2036
2037    /// Array of Class1 records, ordered by classes in classDef1.
2038    pub fn class1_records(&self) -> ComputedArray<'a, Class1Record<'a>> {
2039        let range = self.class1_records_byte_range();
2040        ComputedArray::new(
2041            self.offset_data(),
2042            range,
2043            (
2044                self.class2_count(),
2045                self.value_format1(),
2046                self.value_format2(),
2047            ),
2048        )
2049        .unwrap_or_default()
2050    }
2051
2052    pub fn pos_format_byte_range(&self) -> Range<usize> {
2053        let start = 0;
2054        let end = start + u16::RAW_BYTE_LEN;
2055        start..end
2056    }
2057
2058    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2059        let start = self.pos_format_byte_range().end;
2060        let end = start + Offset16::RAW_BYTE_LEN;
2061        start..end
2062    }
2063
2064    pub fn value_format1_byte_range(&self) -> Range<usize> {
2065        let start = self.coverage_offset_byte_range().end;
2066        let end = start + ValueFormat::RAW_BYTE_LEN;
2067        start..end
2068    }
2069
2070    pub fn value_format2_byte_range(&self) -> Range<usize> {
2071        let start = self.value_format1_byte_range().end;
2072        let end = start + ValueFormat::RAW_BYTE_LEN;
2073        start..end
2074    }
2075
2076    pub fn class_def1_offset_byte_range(&self) -> Range<usize> {
2077        let start = self.value_format2_byte_range().end;
2078        let end = start + Offset16::RAW_BYTE_LEN;
2079        start..end
2080    }
2081
2082    pub fn class_def2_offset_byte_range(&self) -> Range<usize> {
2083        let start = self.class_def1_offset_byte_range().end;
2084        let end = start + Offset16::RAW_BYTE_LEN;
2085        start..end
2086    }
2087
2088    pub fn class1_count_byte_range(&self) -> Range<usize> {
2089        let start = self.class_def2_offset_byte_range().end;
2090        let end = start + u16::RAW_BYTE_LEN;
2091        start..end
2092    }
2093
2094    pub fn class2_count_byte_range(&self) -> Range<usize> {
2095        let start = self.class1_count_byte_range().end;
2096        let end = start + u16::RAW_BYTE_LEN;
2097        start..end
2098    }
2099
2100    pub fn class1_records_byte_range(&self) -> Range<usize> {
2101        let class1_count = self.class1_count();
2102        let start = self.class2_count_byte_range().end;
2103        let end = start
2104            + (transforms::to_usize(class1_count)).saturating_mul(
2105                <Class1Record as ComputeSize>::compute_size((
2106                    self.class2_count(),
2107                    self.value_format1(),
2108                    self.value_format2(),
2109                ))
2110                .unwrap_or(0),
2111            );
2112        start..end
2113    }
2114}
2115
2116/// Part of [PairPosFormat2]
2117#[derive(Clone, Debug)]
2118pub struct Class1Record<'a> {
2119    /// Array of Class2 records, ordered by classes in classDef2.
2120    pub class2_records: ComputedArray<'a, Class2Record<'a>>,
2121}
2122
2123impl<'a> Class1Record<'a> {
2124    /// Array of Class2 records, ordered by classes in classDef2.
2125    pub fn class2_records(&self) -> &ComputedArray<'a, Class2Record<'a>> {
2126        &self.class2_records
2127    }
2128}
2129
2130impl ReadArgs for Class1Record<'_> {
2131    type Args = (u16, ValueFormat, ValueFormat);
2132}
2133
2134impl ComputeSize for Class1Record<'_> {
2135    #[allow(clippy::needless_question_mark)]
2136    fn compute_size(args: (u16, ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
2137        let (class2_count, value_format1, value_format2) = args;
2138        Ok((transforms::to_usize(class2_count)).saturating_mul(
2139            <Class2Record as ComputeSize>::compute_size((value_format1, value_format2))
2140                .unwrap_or(0),
2141        ))
2142    }
2143}
2144
2145impl<'a> FontReadAt<'a> for Class1Record<'a> {
2146    fn read_at(
2147        data: FontData<'a>,
2148        offset: usize,
2149        args: (u16, ValueFormat, ValueFormat),
2150    ) -> Result<Self, ReadError> {
2151        let mut cursor = data.cursor();
2152        cursor.advance_by(offset);
2153        let (class2_count, value_format1, value_format2) = args;
2154        Ok(Self {
2155            class2_records: cursor.read_computed_array(
2156                transforms::to_usize(class2_count),
2157                (value_format1, value_format2),
2158            )?,
2159        })
2160    }
2161}
2162
2163#[allow(clippy::needless_lifetimes)]
2164impl<'a> Class1Record<'a> {
2165    /// A constructor that requires additional arguments.
2166    ///
2167    /// This type requires some external state in order to be
2168    /// parsed.
2169    pub fn read(
2170        data: FontData<'a>,
2171        offset: usize,
2172        class2_count: u16,
2173        value_format1: ValueFormat,
2174        value_format2: ValueFormat,
2175    ) -> Result<Self, ReadError> {
2176        let args = (class2_count, value_format1, value_format2);
2177        Self::read_at(data, offset, args)
2178    }
2179}
2180
2181/// Part of [PairPosFormat2]
2182#[derive(Clone, Debug)]
2183pub struct Class2Record<'a> {
2184    /// Positioning for first glyph — empty if valueFormat1 = 0.
2185    pub value_record1: ValueRecord<'a>,
2186    /// Positioning for second glyph — empty if valueFormat2 = 0.
2187    pub value_record2: ValueRecord<'a>,
2188}
2189
2190impl<'a> Class2Record<'a> {
2191    /// Positioning for first glyph — empty if valueFormat1 = 0.
2192    pub fn value_record1(&self) -> &ValueRecord<'a> {
2193        &self.value_record1
2194    }
2195
2196    /// Positioning for second glyph — empty if valueFormat2 = 0.
2197    pub fn value_record2(&self) -> &ValueRecord<'a> {
2198        &self.value_record2
2199    }
2200}
2201
2202impl ReadArgs for Class2Record<'_> {
2203    type Args = (ValueFormat, ValueFormat);
2204}
2205
2206impl ComputeSize for Class2Record<'_> {
2207    #[allow(clippy::needless_question_mark)]
2208    fn compute_size(args: (ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
2209        let (value_format1, value_format2) = args;
2210        let mut result = 0usize;
2211        result = result
2212            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format1).unwrap_or(0))
2213            .ok_or(ReadError::OutOfBounds)?;
2214        result = result
2215            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format2).unwrap_or(0))
2216            .ok_or(ReadError::OutOfBounds)?;
2217        Ok(result)
2218    }
2219}
2220
2221impl<'a> FontReadAt<'a> for Class2Record<'a> {
2222    fn read_at(
2223        data: FontData<'a>,
2224        offset: usize,
2225        args: (ValueFormat, ValueFormat),
2226    ) -> Result<Self, ReadError> {
2227        let mut cursor = data.cursor();
2228        cursor.advance_by(offset);
2229        let (value_format1, value_format2) = args;
2230        Ok(Self {
2231            value_record1: cursor.read_at_with_args(value_format1)?,
2232            value_record2: cursor.read_at_with_args(value_format2)?,
2233        })
2234    }
2235}
2236
2237#[allow(clippy::needless_lifetimes)]
2238impl<'a> Class2Record<'a> {
2239    /// A constructor that requires additional arguments.
2240    ///
2241    /// This type requires some external state in order to be
2242    /// parsed.
2243    pub fn read(
2244        data: FontData<'a>,
2245        offset: usize,
2246        value_format1: ValueFormat,
2247        value_format2: ValueFormat,
2248    ) -> Result<Self, ReadError> {
2249        let args = (value_format1, value_format2);
2250        Self::read_at(data, offset, args)
2251    }
2252}
2253
2254impl Format<u16> for CursivePosFormat1<'_> {
2255    const FORMAT: u16 = 1;
2256}
2257
2258impl<'a> MinByteRange<'a> for CursivePosFormat1<'a> {
2259    fn min_byte_range(&self) -> Range<usize> {
2260        0..self.entry_exit_record_byte_range().end
2261    }
2262    fn min_table_bytes(&self) -> &'a [u8] {
2263        let range = self.min_byte_range();
2264        self.offset_data().as_bytes().get(range).unwrap_or_default()
2265    }
2266}
2267
2268impl ReadArgs for CursivePosFormat1<'_> {
2269    type Args = ();
2270}
2271
2272impl<'a> FontRead<'a> for CursivePosFormat1<'a> {
2273    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2274        #[allow(clippy::absurd_extreme_comparisons)]
2275        if data.len() < Self::MIN_SIZE {
2276            return Err(ReadError::OutOfBounds);
2277        }
2278        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2279            return Err(ReadError::OutOfBounds);
2280        };
2281        Ok(Self { data })
2282    }
2283}
2284
2285/// The fields at the start of the table, which are read directly.
2286#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2287#[repr(C, packed)]
2288#[doc(hidden)]
2289pub struct CursivePosFormat1FixedHeader {
2290    pub pos_format: BigEndian<u16>,
2291    pub coverage_offset: BigEndian<Offset16>,
2292    pub entry_exit_count: BigEndian<u16>,
2293}
2294
2295#[allow(clippy::absurd_extreme_comparisons)]
2296const _: () =
2297    assert!(std::mem::size_of::<CursivePosFormat1FixedHeader>() <= CursivePosFormat1::MIN_SIZE);
2298
2299/// [Cursive Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#cursive-attachment-positioning-format1-cursive-attachment): Cursvie attachment
2300#[derive(Clone)]
2301pub struct CursivePosFormat1<'a> {
2302    data: TableDataRef<'a, CursivePosFormat1FixedHeader>,
2303}
2304
2305#[allow(clippy::needless_lifetimes)]
2306impl<'a> CursivePosFormat1<'a> {
2307    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2308    basic_table_impls!(impl_the_methods);
2309
2310    /// Format identifier: format = 1
2311    pub fn pos_format(&self) -> u16 {
2312        self.data.header().pos_format.get()
2313    }
2314
2315    /// Offset to Coverage table, from beginning of CursivePos subtable.
2316    pub fn coverage_offset(&self) -> Offset16 {
2317        self.data.header().coverage_offset.get()
2318    }
2319
2320    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2321    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2322        let data = self.offset_data();
2323        self.coverage_offset().resolve(data)
2324    }
2325
2326    /// Number of EntryExit records
2327    pub fn entry_exit_count(&self) -> u16 {
2328        self.data.header().entry_exit_count.get()
2329    }
2330
2331    /// Array of EntryExit records, in Coverage index order.
2332    pub fn entry_exit_record(&self) -> &'a [EntryExitRecord] {
2333        let range = self.entry_exit_record_byte_range();
2334        self.offset_data()
2335            .read_array(range)
2336            .ok()
2337            .unwrap_or_default()
2338    }
2339
2340    pub fn pos_format_byte_range(&self) -> Range<usize> {
2341        let start = 0;
2342        let end = start + u16::RAW_BYTE_LEN;
2343        start..end
2344    }
2345
2346    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2347        let start = self.pos_format_byte_range().end;
2348        let end = start + Offset16::RAW_BYTE_LEN;
2349        start..end
2350    }
2351
2352    pub fn entry_exit_count_byte_range(&self) -> Range<usize> {
2353        let start = self.coverage_offset_byte_range().end;
2354        let end = start + u16::RAW_BYTE_LEN;
2355        start..end
2356    }
2357
2358    pub fn entry_exit_record_byte_range(&self) -> Range<usize> {
2359        let entry_exit_count = self.entry_exit_count();
2360        let start = self.entry_exit_count_byte_range().end;
2361        let end = start
2362            + (transforms::to_usize(entry_exit_count))
2363                .saturating_mul(EntryExitRecord::RAW_BYTE_LEN);
2364        start..end
2365    }
2366}
2367
2368const _: () = assert!(FontData::default_data_long_enough(
2369    CursivePosFormat1::MIN_SIZE
2370));
2371
2372impl Default for CursivePosFormat1<'_> {
2373    fn default() -> Self {
2374        Self {
2375            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
2376                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2377        }
2378    }
2379}
2380
2381/// Part of [CursivePosFormat1]
2382#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
2383#[repr(C)]
2384#[repr(packed)]
2385pub struct EntryExitRecord {
2386    /// Offset to entryAnchor table, from beginning of CursivePos
2387    /// subtable (may be NULL).
2388    pub entry_anchor_offset: BigEndian<Nullable<Offset16>>,
2389    /// Offset to exitAnchor table, from beginning of CursivePos
2390    /// subtable (may be NULL).
2391    pub exit_anchor_offset: BigEndian<Nullable<Offset16>>,
2392}
2393
2394impl EntryExitRecord {
2395    /// Offset to entryAnchor table, from beginning of CursivePos
2396    /// subtable (may be NULL).
2397    pub fn entry_anchor_offset(&self) -> Nullable<Offset16> {
2398        self.entry_anchor_offset.get()
2399    }
2400
2401    /// Offset to entryAnchor table, from beginning of CursivePos
2402    /// subtable (may be NULL).
2403    ///
2404    /// The `data` argument should be retrieved from the parent table
2405    /// By calling its `offset_data` method.
2406    pub fn entry_anchor<'a>(
2407        &self,
2408        data: FontData<'a>,
2409    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
2410        self.entry_anchor_offset().resolve(data)
2411    }
2412
2413    /// Offset to exitAnchor table, from beginning of CursivePos
2414    /// subtable (may be NULL).
2415    pub fn exit_anchor_offset(&self) -> Nullable<Offset16> {
2416        self.exit_anchor_offset.get()
2417    }
2418
2419    /// Offset to exitAnchor table, from beginning of CursivePos
2420    /// subtable (may be NULL).
2421    ///
2422    /// The `data` argument should be retrieved from the parent table
2423    /// By calling its `offset_data` method.
2424    pub fn exit_anchor<'a>(
2425        &self,
2426        data: FontData<'a>,
2427    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
2428        self.exit_anchor_offset().resolve(data)
2429    }
2430}
2431
2432impl FixedSize for EntryExitRecord {
2433    const RAW_BYTE_LEN: usize = Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
2434}
2435
2436impl Format<u16> for MarkBasePosFormat1<'_> {
2437    const FORMAT: u16 = 1;
2438}
2439
2440impl<'a> MinByteRange<'a> for MarkBasePosFormat1<'a> {
2441    fn min_byte_range(&self) -> Range<usize> {
2442        0..self.base_array_offset_byte_range().end
2443    }
2444    fn min_table_bytes(&self) -> &'a [u8] {
2445        let range = self.min_byte_range();
2446        self.offset_data().as_bytes().get(range).unwrap_or_default()
2447    }
2448}
2449
2450impl ReadArgs for MarkBasePosFormat1<'_> {
2451    type Args = ();
2452}
2453
2454impl<'a> FontRead<'a> for MarkBasePosFormat1<'a> {
2455    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2456        #[allow(clippy::absurd_extreme_comparisons)]
2457        if data.len() < Self::MIN_SIZE {
2458            return Err(ReadError::OutOfBounds);
2459        }
2460        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2461            return Err(ReadError::OutOfBounds);
2462        };
2463        Ok(Self { data })
2464    }
2465}
2466
2467/// The fields at the start of the table, which are read directly.
2468#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2469#[repr(C, packed)]
2470#[doc(hidden)]
2471pub struct MarkBasePosFormat1FixedHeader {
2472    pub pos_format: BigEndian<u16>,
2473    pub mark_coverage_offset: BigEndian<Offset16>,
2474    pub base_coverage_offset: BigEndian<Offset16>,
2475    pub mark_class_count: BigEndian<u16>,
2476    pub mark_array_offset: BigEndian<Offset16>,
2477    pub base_array_offset: BigEndian<Offset16>,
2478}
2479
2480#[allow(clippy::absurd_extreme_comparisons)]
2481const _: () =
2482    assert!(std::mem::size_of::<MarkBasePosFormat1FixedHeader>() <= MarkBasePosFormat1::MIN_SIZE);
2483
2484/// [Mark-to-Base Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-base-attachment-positioning-format-1-mark-to-base-attachment-point): Mark-to-base Attachment Point
2485#[derive(Clone)]
2486pub struct MarkBasePosFormat1<'a> {
2487    data: TableDataRef<'a, MarkBasePosFormat1FixedHeader>,
2488}
2489
2490#[allow(clippy::needless_lifetimes)]
2491impl<'a> MarkBasePosFormat1<'a> {
2492    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2493        + Offset16::RAW_BYTE_LEN
2494        + Offset16::RAW_BYTE_LEN
2495        + u16::RAW_BYTE_LEN
2496        + Offset16::RAW_BYTE_LEN
2497        + Offset16::RAW_BYTE_LEN);
2498    basic_table_impls!(impl_the_methods);
2499
2500    /// Format identifier: format = 1
2501    pub fn pos_format(&self) -> u16 {
2502        self.data.header().pos_format.get()
2503    }
2504
2505    /// Offset to markCoverage table, from beginning of MarkBasePos
2506    /// subtable.
2507    pub fn mark_coverage_offset(&self) -> Offset16 {
2508        self.data.header().mark_coverage_offset.get()
2509    }
2510
2511    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
2512    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2513        let data = self.offset_data();
2514        self.mark_coverage_offset().resolve(data)
2515    }
2516
2517    /// Offset to baseCoverage table, from beginning of MarkBasePos
2518    /// subtable.
2519    pub fn base_coverage_offset(&self) -> Offset16 {
2520        self.data.header().base_coverage_offset.get()
2521    }
2522
2523    /// Attempt to resolve [`base_coverage_offset`][Self::base_coverage_offset].
2524    pub fn base_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2525        let data = self.offset_data();
2526        self.base_coverage_offset().resolve(data)
2527    }
2528
2529    /// Number of classes defined for marks
2530    pub fn mark_class_count(&self) -> u16 {
2531        self.data.header().mark_class_count.get()
2532    }
2533
2534    /// Offset to MarkArray table, from beginning of MarkBasePos
2535    /// subtable.
2536    pub fn mark_array_offset(&self) -> Offset16 {
2537        self.data.header().mark_array_offset.get()
2538    }
2539
2540    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
2541    pub fn mark_array(&self) -> Result<MarkArray<'a>, ReadError> {
2542        let data = self.offset_data();
2543        self.mark_array_offset().resolve(data)
2544    }
2545
2546    /// Offset to BaseArray table, from beginning of MarkBasePos
2547    /// subtable.
2548    pub fn base_array_offset(&self) -> Offset16 {
2549        self.data.header().base_array_offset.get()
2550    }
2551
2552    /// Attempt to resolve [`base_array_offset`][Self::base_array_offset].
2553    pub fn base_array(&self) -> Result<BaseArray<'a>, ReadError> {
2554        let data = self.offset_data();
2555        let args = self.mark_class_count();
2556        self.base_array_offset().resolve_with_args(data, args)
2557    }
2558
2559    pub fn pos_format_byte_range(&self) -> Range<usize> {
2560        let start = 0;
2561        let end = start + u16::RAW_BYTE_LEN;
2562        start..end
2563    }
2564
2565    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
2566        let start = self.pos_format_byte_range().end;
2567        let end = start + Offset16::RAW_BYTE_LEN;
2568        start..end
2569    }
2570
2571    pub fn base_coverage_offset_byte_range(&self) -> Range<usize> {
2572        let start = self.mark_coverage_offset_byte_range().end;
2573        let end = start + Offset16::RAW_BYTE_LEN;
2574        start..end
2575    }
2576
2577    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
2578        let start = self.base_coverage_offset_byte_range().end;
2579        let end = start + u16::RAW_BYTE_LEN;
2580        start..end
2581    }
2582
2583    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
2584        let start = self.mark_class_count_byte_range().end;
2585        let end = start + Offset16::RAW_BYTE_LEN;
2586        start..end
2587    }
2588
2589    pub fn base_array_offset_byte_range(&self) -> Range<usize> {
2590        let start = self.mark_array_offset_byte_range().end;
2591        let end = start + Offset16::RAW_BYTE_LEN;
2592        start..end
2593    }
2594}
2595
2596const _: () = assert!(FontData::default_data_long_enough(
2597    MarkBasePosFormat1::MIN_SIZE
2598));
2599
2600impl Default for MarkBasePosFormat1<'_> {
2601    fn default() -> Self {
2602        Self {
2603            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
2604                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2605        }
2606    }
2607}
2608
2609impl<'a> MinByteRange<'a> for BaseArray<'a> {
2610    fn min_byte_range(&self) -> Range<usize> {
2611        0..self.base_records_byte_range().end
2612    }
2613    fn min_table_bytes(&self) -> &'a [u8] {
2614        let range = self.min_byte_range();
2615        self.offset_data().as_bytes().get(range).unwrap_or_default()
2616    }
2617}
2618
2619impl ReadArgs for BaseArray<'_> {
2620    type Args = u16;
2621}
2622
2623impl<'a> FontRead<'a> for BaseArray<'a> {
2624    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
2625        let mark_class_count = args;
2626
2627        #[allow(clippy::absurd_extreme_comparisons)]
2628        if data.len() < Self::MIN_SIZE {
2629            return Err(ReadError::OutOfBounds);
2630        }
2631        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2632            return Err(ReadError::OutOfBounds);
2633        };
2634        Ok(Self {
2635            data,
2636            mark_class_count,
2637        })
2638    }
2639}
2640
2641impl<'a> BaseArray<'a> {
2642    /// A constructor that requires additional arguments.
2643    ///
2644    /// This type requires some external state in order to be
2645    /// parsed.
2646    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
2647        let args = mark_class_count;
2648        Self::read_with_args(data, args)
2649    }
2650}
2651
2652/// The fields at the start of the table, which are read directly.
2653#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2654#[repr(C, packed)]
2655#[doc(hidden)]
2656pub struct BaseArrayFixedHeader {
2657    pub base_count: BigEndian<u16>,
2658}
2659
2660#[allow(clippy::absurd_extreme_comparisons)]
2661const _: () = assert!(std::mem::size_of::<BaseArrayFixedHeader>() <= BaseArray::MIN_SIZE);
2662
2663/// Part of [MarkBasePosFormat1]
2664#[derive(Clone)]
2665pub struct BaseArray<'a> {
2666    data: TableDataRef<'a, BaseArrayFixedHeader>,
2667    mark_class_count: u16,
2668}
2669
2670#[allow(clippy::needless_lifetimes)]
2671impl<'a> BaseArray<'a> {
2672    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2673    basic_table_impls!(impl_the_methods);
2674
2675    /// Number of BaseRecords
2676    pub fn base_count(&self) -> u16 {
2677        self.data.header().base_count.get()
2678    }
2679
2680    /// Array of BaseRecords, in order of baseCoverage Index.
2681    pub fn base_records(&self) -> ComputedArray<'a, BaseRecord<'a>> {
2682        let range = self.base_records_byte_range();
2683        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
2684    }
2685
2686    pub(crate) fn mark_class_count(&self) -> u16 {
2687        self.mark_class_count
2688    }
2689
2690    pub fn base_count_byte_range(&self) -> Range<usize> {
2691        let start = 0;
2692        let end = start + u16::RAW_BYTE_LEN;
2693        start..end
2694    }
2695
2696    pub fn base_records_byte_range(&self) -> Range<usize> {
2697        let base_count = self.base_count();
2698        let start = self.base_count_byte_range().end;
2699        let end = start
2700            + (transforms::to_usize(base_count)).saturating_mul(
2701                <BaseRecord as ComputeSize>::compute_size(self.mark_class_count()).unwrap_or(0),
2702            );
2703        start..end
2704    }
2705}
2706
2707const _: () = assert!(FontData::default_data_long_enough(BaseArray::MIN_SIZE));
2708
2709impl Default for BaseArray<'_> {
2710    fn default() -> Self {
2711        Self {
2712            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2713                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2714            mark_class_count: Default::default(),
2715        }
2716    }
2717}
2718
2719/// Part of [BaseArray]
2720#[derive(Clone, Debug)]
2721pub struct BaseRecord<'a> {
2722    /// Array of offsets (one per mark class) to Anchor tables. Offsets
2723    /// are from beginning of BaseArray table, ordered by class
2724    /// (offsets may be NULL).
2725    pub base_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
2726}
2727
2728impl<'a> BaseRecord<'a> {
2729    /// Array of offsets (one per mark class) to Anchor tables. Offsets
2730    /// are from beginning of BaseArray table, ordered by class
2731    /// (offsets may be NULL).
2732    pub fn base_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2733        self.base_anchor_offsets
2734    }
2735
2736    /// Array of offsets (one per mark class) to Anchor tables. Offsets
2737    /// are from beginning of BaseArray table, ordered by class
2738    /// (offsets may be NULL).
2739    ///
2740    /// The `data` argument should be retrieved from the parent table
2741    /// By calling its `offset_data` method.
2742    pub fn base_anchors(
2743        &self,
2744        data: FontData<'a>,
2745    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
2746        let offsets = self.base_anchor_offsets();
2747        ArrayOfNullableOffsets::new(offsets, data, ())
2748    }
2749}
2750
2751impl ReadArgs for BaseRecord<'_> {
2752    type Args = u16;
2753}
2754
2755impl ComputeSize for BaseRecord<'_> {
2756    #[allow(clippy::needless_question_mark)]
2757    fn compute_size(args: u16) -> Result<usize, ReadError> {
2758        let mark_class_count = args;
2759        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
2760    }
2761}
2762
2763impl<'a> FontRead<'a> for BaseRecord<'a> {
2764    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
2765        let mut cursor = data.cursor();
2766        let mark_class_count = args;
2767        Ok(Self {
2768            base_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
2769        })
2770    }
2771}
2772
2773crate::impl_font_read_at!(BaseRecord<'a>);
2774
2775#[allow(clippy::needless_lifetimes)]
2776impl<'a> BaseRecord<'a> {
2777    /// A constructor that requires additional arguments.
2778    ///
2779    /// This type requires some external state in order to be
2780    /// parsed.
2781    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
2782        let args = mark_class_count;
2783        Self::read_with_args(data, args)
2784    }
2785}
2786
2787impl Format<u16> for MarkLigPosFormat1<'_> {
2788    const FORMAT: u16 = 1;
2789}
2790
2791impl<'a> MinByteRange<'a> for MarkLigPosFormat1<'a> {
2792    fn min_byte_range(&self) -> Range<usize> {
2793        0..self.ligature_array_offset_byte_range().end
2794    }
2795    fn min_table_bytes(&self) -> &'a [u8] {
2796        let range = self.min_byte_range();
2797        self.offset_data().as_bytes().get(range).unwrap_or_default()
2798    }
2799}
2800
2801impl ReadArgs for MarkLigPosFormat1<'_> {
2802    type Args = ();
2803}
2804
2805impl<'a> FontRead<'a> for MarkLigPosFormat1<'a> {
2806    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2807        #[allow(clippy::absurd_extreme_comparisons)]
2808        if data.len() < Self::MIN_SIZE {
2809            return Err(ReadError::OutOfBounds);
2810        }
2811        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2812            return Err(ReadError::OutOfBounds);
2813        };
2814        Ok(Self { data })
2815    }
2816}
2817
2818/// The fields at the start of the table, which are read directly.
2819#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2820#[repr(C, packed)]
2821#[doc(hidden)]
2822pub struct MarkLigPosFormat1FixedHeader {
2823    pub pos_format: BigEndian<u16>,
2824    pub mark_coverage_offset: BigEndian<Offset16>,
2825    pub ligature_coverage_offset: BigEndian<Offset16>,
2826    pub mark_class_count: BigEndian<u16>,
2827    pub mark_array_offset: BigEndian<Offset16>,
2828    pub ligature_array_offset: BigEndian<Offset16>,
2829}
2830
2831#[allow(clippy::absurd_extreme_comparisons)]
2832const _: () =
2833    assert!(std::mem::size_of::<MarkLigPosFormat1FixedHeader>() <= MarkLigPosFormat1::MIN_SIZE);
2834
2835/// [Mark-to-Ligature Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-ligature-attachment-positioning-format-1-mark-to-ligature-attachment): Mark-to-Ligature Attachment
2836#[derive(Clone)]
2837pub struct MarkLigPosFormat1<'a> {
2838    data: TableDataRef<'a, MarkLigPosFormat1FixedHeader>,
2839}
2840
2841#[allow(clippy::needless_lifetimes)]
2842impl<'a> MarkLigPosFormat1<'a> {
2843    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2844        + Offset16::RAW_BYTE_LEN
2845        + Offset16::RAW_BYTE_LEN
2846        + u16::RAW_BYTE_LEN
2847        + Offset16::RAW_BYTE_LEN
2848        + Offset16::RAW_BYTE_LEN);
2849    basic_table_impls!(impl_the_methods);
2850
2851    /// Format identifier: format = 1
2852    pub fn pos_format(&self) -> u16 {
2853        self.data.header().pos_format.get()
2854    }
2855
2856    /// Offset to markCoverage table, from beginning of MarkLigPos
2857    /// subtable.
2858    pub fn mark_coverage_offset(&self) -> Offset16 {
2859        self.data.header().mark_coverage_offset.get()
2860    }
2861
2862    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
2863    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2864        let data = self.offset_data();
2865        self.mark_coverage_offset().resolve(data)
2866    }
2867
2868    /// Offset to ligatureCoverage table, from beginning of MarkLigPos
2869    /// subtable.
2870    pub fn ligature_coverage_offset(&self) -> Offset16 {
2871        self.data.header().ligature_coverage_offset.get()
2872    }
2873
2874    /// Attempt to resolve [`ligature_coverage_offset`][Self::ligature_coverage_offset].
2875    pub fn ligature_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2876        let data = self.offset_data();
2877        self.ligature_coverage_offset().resolve(data)
2878    }
2879
2880    /// Number of defined mark classes
2881    pub fn mark_class_count(&self) -> u16 {
2882        self.data.header().mark_class_count.get()
2883    }
2884
2885    /// Offset to MarkArray table, from beginning of MarkLigPos
2886    /// subtable.
2887    pub fn mark_array_offset(&self) -> Offset16 {
2888        self.data.header().mark_array_offset.get()
2889    }
2890
2891    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
2892    pub fn mark_array(&self) -> Result<MarkArray<'a>, ReadError> {
2893        let data = self.offset_data();
2894        self.mark_array_offset().resolve(data)
2895    }
2896
2897    /// Offset to LigatureArray table, from beginning of MarkLigPos
2898    /// subtable.
2899    pub fn ligature_array_offset(&self) -> Offset16 {
2900        self.data.header().ligature_array_offset.get()
2901    }
2902
2903    /// Attempt to resolve [`ligature_array_offset`][Self::ligature_array_offset].
2904    pub fn ligature_array(&self) -> Result<LigatureArray<'a>, ReadError> {
2905        let data = self.offset_data();
2906        let args = self.mark_class_count();
2907        self.ligature_array_offset().resolve_with_args(data, args)
2908    }
2909
2910    pub fn pos_format_byte_range(&self) -> Range<usize> {
2911        let start = 0;
2912        let end = start + u16::RAW_BYTE_LEN;
2913        start..end
2914    }
2915
2916    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
2917        let start = self.pos_format_byte_range().end;
2918        let end = start + Offset16::RAW_BYTE_LEN;
2919        start..end
2920    }
2921
2922    pub fn ligature_coverage_offset_byte_range(&self) -> Range<usize> {
2923        let start = self.mark_coverage_offset_byte_range().end;
2924        let end = start + Offset16::RAW_BYTE_LEN;
2925        start..end
2926    }
2927
2928    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
2929        let start = self.ligature_coverage_offset_byte_range().end;
2930        let end = start + u16::RAW_BYTE_LEN;
2931        start..end
2932    }
2933
2934    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
2935        let start = self.mark_class_count_byte_range().end;
2936        let end = start + Offset16::RAW_BYTE_LEN;
2937        start..end
2938    }
2939
2940    pub fn ligature_array_offset_byte_range(&self) -> Range<usize> {
2941        let start = self.mark_array_offset_byte_range().end;
2942        let end = start + Offset16::RAW_BYTE_LEN;
2943        start..end
2944    }
2945}
2946
2947const _: () = assert!(FontData::default_data_long_enough(
2948    MarkLigPosFormat1::MIN_SIZE
2949));
2950
2951impl Default for MarkLigPosFormat1<'_> {
2952    fn default() -> Self {
2953        Self {
2954            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
2955                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2956        }
2957    }
2958}
2959
2960impl<'a> MinByteRange<'a> for LigatureArray<'a> {
2961    fn min_byte_range(&self) -> Range<usize> {
2962        0..self.ligature_attach_offsets_byte_range().end
2963    }
2964    fn min_table_bytes(&self) -> &'a [u8] {
2965        let range = self.min_byte_range();
2966        self.offset_data().as_bytes().get(range).unwrap_or_default()
2967    }
2968}
2969
2970impl ReadArgs for LigatureArray<'_> {
2971    type Args = u16;
2972}
2973
2974impl<'a> FontRead<'a> for LigatureArray<'a> {
2975    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
2976        let mark_class_count = args;
2977
2978        #[allow(clippy::absurd_extreme_comparisons)]
2979        if data.len() < Self::MIN_SIZE {
2980            return Err(ReadError::OutOfBounds);
2981        }
2982        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2983            return Err(ReadError::OutOfBounds);
2984        };
2985        Ok(Self {
2986            data,
2987            mark_class_count,
2988        })
2989    }
2990}
2991
2992impl<'a> LigatureArray<'a> {
2993    /// A constructor that requires additional arguments.
2994    ///
2995    /// This type requires some external state in order to be
2996    /// parsed.
2997    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
2998        let args = mark_class_count;
2999        Self::read_with_args(data, args)
3000    }
3001}
3002
3003/// The fields at the start of the table, which are read directly.
3004#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3005#[repr(C, packed)]
3006#[doc(hidden)]
3007pub struct LigatureArrayFixedHeader {
3008    pub ligature_count: BigEndian<u16>,
3009}
3010
3011#[allow(clippy::absurd_extreme_comparisons)]
3012const _: () = assert!(std::mem::size_of::<LigatureArrayFixedHeader>() <= LigatureArray::MIN_SIZE);
3013
3014/// Part of [MarkLigPosFormat1]
3015#[derive(Clone)]
3016pub struct LigatureArray<'a> {
3017    data: TableDataRef<'a, LigatureArrayFixedHeader>,
3018    mark_class_count: u16,
3019}
3020
3021#[allow(clippy::needless_lifetimes)]
3022impl<'a> LigatureArray<'a> {
3023    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3024    basic_table_impls!(impl_the_methods);
3025
3026    /// Number of LigatureAttach table offsets
3027    pub fn ligature_count(&self) -> u16 {
3028        self.data.header().ligature_count.get()
3029    }
3030
3031    /// Array of offsets to LigatureAttach tables. Offsets are from
3032    /// beginning of LigatureArray table, ordered by ligatureCoverage
3033    /// index.
3034    pub fn ligature_attach_offsets(&self) -> &'a [BigEndian<Offset16>] {
3035        let range = self.ligature_attach_offsets_byte_range();
3036        self.offset_data()
3037            .read_array(range)
3038            .ok()
3039            .unwrap_or_default()
3040    }
3041
3042    /// A dynamically resolving wrapper for [`ligature_attach_offsets`][Self::ligature_attach_offsets].
3043    pub fn ligature_attaches(&self) -> ArrayOfOffsets<'a, LigatureAttach<'a>, Offset16> {
3044        let data = self.offset_data();
3045        let offsets = self.ligature_attach_offsets();
3046        let args = self.mark_class_count();
3047        ArrayOfOffsets::new(offsets, data, args)
3048    }
3049
3050    pub(crate) fn mark_class_count(&self) -> u16 {
3051        self.mark_class_count
3052    }
3053
3054    pub fn ligature_count_byte_range(&self) -> Range<usize> {
3055        let start = 0;
3056        let end = start + u16::RAW_BYTE_LEN;
3057        start..end
3058    }
3059
3060    pub fn ligature_attach_offsets_byte_range(&self) -> Range<usize> {
3061        let ligature_count = self.ligature_count();
3062        let start = self.ligature_count_byte_range().end;
3063        let end =
3064            start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
3065        start..end
3066    }
3067}
3068
3069const _: () = assert!(FontData::default_data_long_enough(LigatureArray::MIN_SIZE));
3070
3071impl Default for LigatureArray<'_> {
3072    fn default() -> Self {
3073        Self {
3074            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3075                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3076            mark_class_count: Default::default(),
3077        }
3078    }
3079}
3080
3081impl<'a> MinByteRange<'a> for LigatureAttach<'a> {
3082    fn min_byte_range(&self) -> Range<usize> {
3083        0..self.component_records_byte_range().end
3084    }
3085    fn min_table_bytes(&self) -> &'a [u8] {
3086        let range = self.min_byte_range();
3087        self.offset_data().as_bytes().get(range).unwrap_or_default()
3088    }
3089}
3090
3091impl ReadArgs for LigatureAttach<'_> {
3092    type Args = u16;
3093}
3094
3095impl<'a> FontRead<'a> for LigatureAttach<'a> {
3096    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3097        let mark_class_count = args;
3098
3099        #[allow(clippy::absurd_extreme_comparisons)]
3100        if data.len() < Self::MIN_SIZE {
3101            return Err(ReadError::OutOfBounds);
3102        }
3103        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3104            return Err(ReadError::OutOfBounds);
3105        };
3106        Ok(Self {
3107            data,
3108            mark_class_count,
3109        })
3110    }
3111}
3112
3113impl<'a> LigatureAttach<'a> {
3114    /// A constructor that requires additional arguments.
3115    ///
3116    /// This type requires some external state in order to be
3117    /// parsed.
3118    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3119        let args = mark_class_count;
3120        Self::read_with_args(data, args)
3121    }
3122}
3123
3124/// The fields at the start of the table, which are read directly.
3125#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3126#[repr(C, packed)]
3127#[doc(hidden)]
3128pub struct LigatureAttachFixedHeader {
3129    pub component_count: BigEndian<u16>,
3130}
3131
3132#[allow(clippy::absurd_extreme_comparisons)]
3133const _: () = assert!(std::mem::size_of::<LigatureAttachFixedHeader>() <= LigatureAttach::MIN_SIZE);
3134
3135/// Part of [MarkLigPosFormat1]
3136#[derive(Clone)]
3137pub struct LigatureAttach<'a> {
3138    data: TableDataRef<'a, LigatureAttachFixedHeader>,
3139    mark_class_count: u16,
3140}
3141
3142#[allow(clippy::needless_lifetimes)]
3143impl<'a> LigatureAttach<'a> {
3144    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3145    basic_table_impls!(impl_the_methods);
3146
3147    /// Number of ComponentRecords in this ligature
3148    pub fn component_count(&self) -> u16 {
3149        self.data.header().component_count.get()
3150    }
3151
3152    /// Array of Component records, ordered in writing direction.
3153    pub fn component_records(&self) -> ComputedArray<'a, ComponentRecord<'a>> {
3154        let range = self.component_records_byte_range();
3155        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
3156    }
3157
3158    pub(crate) fn mark_class_count(&self) -> u16 {
3159        self.mark_class_count
3160    }
3161
3162    pub fn component_count_byte_range(&self) -> Range<usize> {
3163        let start = 0;
3164        let end = start + u16::RAW_BYTE_LEN;
3165        start..end
3166    }
3167
3168    pub fn component_records_byte_range(&self) -> Range<usize> {
3169        let component_count = self.component_count();
3170        let start = self.component_count_byte_range().end;
3171        let end = start
3172            + (transforms::to_usize(component_count)).saturating_mul(
3173                <ComponentRecord as ComputeSize>::compute_size(self.mark_class_count())
3174                    .unwrap_or(0),
3175            );
3176        start..end
3177    }
3178}
3179
3180const _: () = assert!(FontData::default_data_long_enough(LigatureAttach::MIN_SIZE));
3181
3182impl Default for LigatureAttach<'_> {
3183    fn default() -> Self {
3184        Self {
3185            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3186                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3187            mark_class_count: Default::default(),
3188        }
3189    }
3190}
3191
3192/// Part of [MarkLigPosFormat1]
3193#[derive(Clone, Debug)]
3194pub struct ComponentRecord<'a> {
3195    /// Array of offsets (one per class) to Anchor tables. Offsets are
3196    /// from beginning of LigatureAttach table, ordered by class
3197    /// (offsets may be NULL).
3198    pub ligature_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3199}
3200
3201impl<'a> ComponentRecord<'a> {
3202    /// Array of offsets (one per class) to Anchor tables. Offsets are
3203    /// from beginning of LigatureAttach table, ordered by class
3204    /// (offsets may be NULL).
3205    pub fn ligature_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3206        self.ligature_anchor_offsets
3207    }
3208
3209    /// Array of offsets (one per class) to Anchor tables. Offsets are
3210    /// from beginning of LigatureAttach table, ordered by class
3211    /// (offsets may be NULL).
3212    ///
3213    /// The `data` argument should be retrieved from the parent table
3214    /// By calling its `offset_data` method.
3215    pub fn ligature_anchors(
3216        &self,
3217        data: FontData<'a>,
3218    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3219        let offsets = self.ligature_anchor_offsets();
3220        ArrayOfNullableOffsets::new(offsets, data, ())
3221    }
3222}
3223
3224impl ReadArgs for ComponentRecord<'_> {
3225    type Args = u16;
3226}
3227
3228impl ComputeSize for ComponentRecord<'_> {
3229    #[allow(clippy::needless_question_mark)]
3230    fn compute_size(args: u16) -> Result<usize, ReadError> {
3231        let mark_class_count = args;
3232        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
3233    }
3234}
3235
3236impl<'a> FontRead<'a> for ComponentRecord<'a> {
3237    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3238        let mut cursor = data.cursor();
3239        let mark_class_count = args;
3240        Ok(Self {
3241            ligature_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
3242        })
3243    }
3244}
3245
3246crate::impl_font_read_at!(ComponentRecord<'a>);
3247
3248#[allow(clippy::needless_lifetimes)]
3249impl<'a> ComponentRecord<'a> {
3250    /// A constructor that requires additional arguments.
3251    ///
3252    /// This type requires some external state in order to be
3253    /// parsed.
3254    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3255        let args = mark_class_count;
3256        Self::read_with_args(data, args)
3257    }
3258}
3259
3260impl Format<u16> for MarkMarkPosFormat1<'_> {
3261    const FORMAT: u16 = 1;
3262}
3263
3264impl<'a> MinByteRange<'a> for MarkMarkPosFormat1<'a> {
3265    fn min_byte_range(&self) -> Range<usize> {
3266        0..self.mark2_array_offset_byte_range().end
3267    }
3268    fn min_table_bytes(&self) -> &'a [u8] {
3269        let range = self.min_byte_range();
3270        self.offset_data().as_bytes().get(range).unwrap_or_default()
3271    }
3272}
3273
3274impl ReadArgs for MarkMarkPosFormat1<'_> {
3275    type Args = ();
3276}
3277
3278impl<'a> FontRead<'a> for MarkMarkPosFormat1<'a> {
3279    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3280        #[allow(clippy::absurd_extreme_comparisons)]
3281        if data.len() < Self::MIN_SIZE {
3282            return Err(ReadError::OutOfBounds);
3283        }
3284        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3285            return Err(ReadError::OutOfBounds);
3286        };
3287        Ok(Self { data })
3288    }
3289}
3290
3291/// The fields at the start of the table, which are read directly.
3292#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3293#[repr(C, packed)]
3294#[doc(hidden)]
3295pub struct MarkMarkPosFormat1FixedHeader {
3296    pub pos_format: BigEndian<u16>,
3297    pub mark1_coverage_offset: BigEndian<Offset16>,
3298    pub mark2_coverage_offset: BigEndian<Offset16>,
3299    pub mark_class_count: BigEndian<u16>,
3300    pub mark1_array_offset: BigEndian<Offset16>,
3301    pub mark2_array_offset: BigEndian<Offset16>,
3302}
3303
3304#[allow(clippy::absurd_extreme_comparisons)]
3305const _: () =
3306    assert!(std::mem::size_of::<MarkMarkPosFormat1FixedHeader>() <= MarkMarkPosFormat1::MIN_SIZE);
3307
3308/// [Mark-to-Mark Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-mark-attachment-positioning-format-1-mark-to-mark-attachment): Mark-to-Mark Attachment
3309#[derive(Clone)]
3310pub struct MarkMarkPosFormat1<'a> {
3311    data: TableDataRef<'a, MarkMarkPosFormat1FixedHeader>,
3312}
3313
3314#[allow(clippy::needless_lifetimes)]
3315impl<'a> MarkMarkPosFormat1<'a> {
3316    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3317        + Offset16::RAW_BYTE_LEN
3318        + Offset16::RAW_BYTE_LEN
3319        + u16::RAW_BYTE_LEN
3320        + Offset16::RAW_BYTE_LEN
3321        + Offset16::RAW_BYTE_LEN);
3322    basic_table_impls!(impl_the_methods);
3323
3324    /// Format identifier: format = 1
3325    pub fn pos_format(&self) -> u16 {
3326        self.data.header().pos_format.get()
3327    }
3328
3329    /// Offset to Combining Mark Coverage table, from beginning of
3330    /// MarkMarkPos subtable.
3331    pub fn mark1_coverage_offset(&self) -> Offset16 {
3332        self.data.header().mark1_coverage_offset.get()
3333    }
3334
3335    /// Attempt to resolve [`mark1_coverage_offset`][Self::mark1_coverage_offset].
3336    pub fn mark1_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3337        let data = self.offset_data();
3338        self.mark1_coverage_offset().resolve(data)
3339    }
3340
3341    /// Offset to Base Mark Coverage table, from beginning of
3342    /// MarkMarkPos subtable.
3343    pub fn mark2_coverage_offset(&self) -> Offset16 {
3344        self.data.header().mark2_coverage_offset.get()
3345    }
3346
3347    /// Attempt to resolve [`mark2_coverage_offset`][Self::mark2_coverage_offset].
3348    pub fn mark2_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3349        let data = self.offset_data();
3350        self.mark2_coverage_offset().resolve(data)
3351    }
3352
3353    /// Number of Combining Mark classes defined
3354    pub fn mark_class_count(&self) -> u16 {
3355        self.data.header().mark_class_count.get()
3356    }
3357
3358    /// Offset to MarkArray table for mark1, from beginning of
3359    /// MarkMarkPos subtable.
3360    pub fn mark1_array_offset(&self) -> Offset16 {
3361        self.data.header().mark1_array_offset.get()
3362    }
3363
3364    /// Attempt to resolve [`mark1_array_offset`][Self::mark1_array_offset].
3365    pub fn mark1_array(&self) -> Result<MarkArray<'a>, ReadError> {
3366        let data = self.offset_data();
3367        self.mark1_array_offset().resolve(data)
3368    }
3369
3370    /// Offset to Mark2Array table for mark2, from beginning of
3371    /// MarkMarkPos subtable.
3372    pub fn mark2_array_offset(&self) -> Offset16 {
3373        self.data.header().mark2_array_offset.get()
3374    }
3375
3376    /// Attempt to resolve [`mark2_array_offset`][Self::mark2_array_offset].
3377    pub fn mark2_array(&self) -> Result<Mark2Array<'a>, ReadError> {
3378        let data = self.offset_data();
3379        let args = self.mark_class_count();
3380        self.mark2_array_offset().resolve_with_args(data, args)
3381    }
3382
3383    pub fn pos_format_byte_range(&self) -> Range<usize> {
3384        let start = 0;
3385        let end = start + u16::RAW_BYTE_LEN;
3386        start..end
3387    }
3388
3389    pub fn mark1_coverage_offset_byte_range(&self) -> Range<usize> {
3390        let start = self.pos_format_byte_range().end;
3391        let end = start + Offset16::RAW_BYTE_LEN;
3392        start..end
3393    }
3394
3395    pub fn mark2_coverage_offset_byte_range(&self) -> Range<usize> {
3396        let start = self.mark1_coverage_offset_byte_range().end;
3397        let end = start + Offset16::RAW_BYTE_LEN;
3398        start..end
3399    }
3400
3401    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
3402        let start = self.mark2_coverage_offset_byte_range().end;
3403        let end = start + u16::RAW_BYTE_LEN;
3404        start..end
3405    }
3406
3407    pub fn mark1_array_offset_byte_range(&self) -> Range<usize> {
3408        let start = self.mark_class_count_byte_range().end;
3409        let end = start + Offset16::RAW_BYTE_LEN;
3410        start..end
3411    }
3412
3413    pub fn mark2_array_offset_byte_range(&self) -> Range<usize> {
3414        let start = self.mark1_array_offset_byte_range().end;
3415        let end = start + Offset16::RAW_BYTE_LEN;
3416        start..end
3417    }
3418}
3419
3420const _: () = assert!(FontData::default_data_long_enough(
3421    MarkMarkPosFormat1::MIN_SIZE
3422));
3423
3424impl Default for MarkMarkPosFormat1<'_> {
3425    fn default() -> Self {
3426        Self {
3427            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
3428                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3429        }
3430    }
3431}
3432
3433impl<'a> MinByteRange<'a> for Mark2Array<'a> {
3434    fn min_byte_range(&self) -> Range<usize> {
3435        0..self.mark2_records_byte_range().end
3436    }
3437    fn min_table_bytes(&self) -> &'a [u8] {
3438        let range = self.min_byte_range();
3439        self.offset_data().as_bytes().get(range).unwrap_or_default()
3440    }
3441}
3442
3443impl ReadArgs for Mark2Array<'_> {
3444    type Args = u16;
3445}
3446
3447impl<'a> FontRead<'a> for Mark2Array<'a> {
3448    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3449        let mark_class_count = args;
3450
3451        #[allow(clippy::absurd_extreme_comparisons)]
3452        if data.len() < Self::MIN_SIZE {
3453            return Err(ReadError::OutOfBounds);
3454        }
3455        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3456            return Err(ReadError::OutOfBounds);
3457        };
3458        Ok(Self {
3459            data,
3460            mark_class_count,
3461        })
3462    }
3463}
3464
3465impl<'a> Mark2Array<'a> {
3466    /// A constructor that requires additional arguments.
3467    ///
3468    /// This type requires some external state in order to be
3469    /// parsed.
3470    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3471        let args = mark_class_count;
3472        Self::read_with_args(data, args)
3473    }
3474}
3475
3476/// The fields at the start of the table, which are read directly.
3477#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3478#[repr(C, packed)]
3479#[doc(hidden)]
3480pub struct Mark2ArrayFixedHeader {
3481    pub mark2_count: BigEndian<u16>,
3482}
3483
3484#[allow(clippy::absurd_extreme_comparisons)]
3485const _: () = assert!(std::mem::size_of::<Mark2ArrayFixedHeader>() <= Mark2Array::MIN_SIZE);
3486
3487/// Part of [MarkMarkPosFormat1]Class2Record
3488#[derive(Clone)]
3489pub struct Mark2Array<'a> {
3490    data: TableDataRef<'a, Mark2ArrayFixedHeader>,
3491    mark_class_count: u16,
3492}
3493
3494#[allow(clippy::needless_lifetimes)]
3495impl<'a> Mark2Array<'a> {
3496    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3497    basic_table_impls!(impl_the_methods);
3498
3499    /// Number of Mark2 records
3500    pub fn mark2_count(&self) -> u16 {
3501        self.data.header().mark2_count.get()
3502    }
3503
3504    /// Array of Mark2Records, in Coverage order.
3505    pub fn mark2_records(&self) -> ComputedArray<'a, Mark2Record<'a>> {
3506        let range = self.mark2_records_byte_range();
3507        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
3508    }
3509
3510    pub(crate) fn mark_class_count(&self) -> u16 {
3511        self.mark_class_count
3512    }
3513
3514    pub fn mark2_count_byte_range(&self) -> Range<usize> {
3515        let start = 0;
3516        let end = start + u16::RAW_BYTE_LEN;
3517        start..end
3518    }
3519
3520    pub fn mark2_records_byte_range(&self) -> Range<usize> {
3521        let mark2_count = self.mark2_count();
3522        let start = self.mark2_count_byte_range().end;
3523        let end = start
3524            + (transforms::to_usize(mark2_count)).saturating_mul(
3525                <Mark2Record as ComputeSize>::compute_size(self.mark_class_count()).unwrap_or(0),
3526            );
3527        start..end
3528    }
3529}
3530
3531const _: () = assert!(FontData::default_data_long_enough(Mark2Array::MIN_SIZE));
3532
3533impl Default for Mark2Array<'_> {
3534    fn default() -> Self {
3535        Self {
3536            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3537                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3538            mark_class_count: Default::default(),
3539        }
3540    }
3541}
3542
3543/// Part of [MarkMarkPosFormat1]
3544#[derive(Clone, Debug)]
3545pub struct Mark2Record<'a> {
3546    /// Array of offsets (one per class) to Anchor tables. Offsets are
3547    /// from beginning of Mark2Array table, in class order (offsets may
3548    /// be NULL).
3549    pub mark2_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3550}
3551
3552impl<'a> Mark2Record<'a> {
3553    /// Array of offsets (one per class) to Anchor tables. Offsets are
3554    /// from beginning of Mark2Array table, in class order (offsets may
3555    /// be NULL).
3556    pub fn mark2_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3557        self.mark2_anchor_offsets
3558    }
3559
3560    /// Array of offsets (one per class) to Anchor tables. Offsets are
3561    /// from beginning of Mark2Array table, in class order (offsets may
3562    /// be NULL).
3563    ///
3564    /// The `data` argument should be retrieved from the parent table
3565    /// By calling its `offset_data` method.
3566    pub fn mark2_anchors(
3567        &self,
3568        data: FontData<'a>,
3569    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3570        let offsets = self.mark2_anchor_offsets();
3571        ArrayOfNullableOffsets::new(offsets, data, ())
3572    }
3573}
3574
3575impl ReadArgs for Mark2Record<'_> {
3576    type Args = u16;
3577}
3578
3579impl ComputeSize for Mark2Record<'_> {
3580    #[allow(clippy::needless_question_mark)]
3581    fn compute_size(args: u16) -> Result<usize, ReadError> {
3582        let mark_class_count = args;
3583        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
3584    }
3585}
3586
3587impl<'a> FontRead<'a> for Mark2Record<'a> {
3588    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3589        let mut cursor = data.cursor();
3590        let mark_class_count = args;
3591        Ok(Self {
3592            mark2_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
3593        })
3594    }
3595}
3596
3597crate::impl_font_read_at!(Mark2Record<'a>);
3598
3599#[allow(clippy::needless_lifetimes)]
3600impl<'a> Mark2Record<'a> {
3601    /// A constructor that requires additional arguments.
3602    ///
3603    /// This type requires some external state in order to be
3604    /// parsed.
3605    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3606        let args = mark_class_count;
3607        Self::read_with_args(data, args)
3608    }
3609}
3610
3611impl Format<u16> for ExtensionPosFormat1<'_> {
3612    const FORMAT: u16 = 1;
3613}
3614
3615impl Discriminant for ExtensionPosFormat1<'_, ()> {
3616    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
3617        data.read_at(u16::RAW_BYTE_LEN)
3618    }
3619}
3620
3621impl<'a, T> MinByteRange<'a> for ExtensionPosFormat1<'a, T> {
3622    fn min_byte_range(&self) -> Range<usize> {
3623        0..self.extension_offset_byte_range().end
3624    }
3625    fn min_table_bytes(&self) -> &'a [u8] {
3626        let range = self.min_byte_range();
3627        self.offset_data().as_bytes().get(range).unwrap_or_default()
3628    }
3629}
3630
3631impl<T> ReadArgs for ExtensionPosFormat1<'_, T> {
3632    type Args = ();
3633}
3634
3635impl<'a, T> FontRead<'a> for ExtensionPosFormat1<'a, T> {
3636    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3637        #[allow(clippy::absurd_extreme_comparisons)]
3638        if data.len() < Self::MIN_SIZE {
3639            return Err(ReadError::OutOfBounds);
3640        }
3641        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3642            return Err(ReadError::OutOfBounds);
3643        };
3644        Ok(Self {
3645            data,
3646            offset_type: std::marker::PhantomData,
3647        })
3648    }
3649}
3650
3651impl<'a, T> ExtensionPosFormat1<'a, T> {
3652    #[allow(dead_code)]
3653    /// Replace the specific generic type on this implementation with `()`
3654    pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> {
3655        ExtensionPosFormat1 {
3656            data: self.data,
3657            offset_type: std::marker::PhantomData,
3658        }
3659    }
3660}
3661
3662/// The fields at the start of the table, which are read directly.
3663#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3664#[repr(C, packed)]
3665#[doc(hidden)]
3666pub struct ExtensionPosFormat1FixedHeader {
3667    pub pos_format: BigEndian<u16>,
3668    pub extension_lookup_type: BigEndian<u16>,
3669    pub extension_offset: BigEndian<Offset32>,
3670}
3671
3672#[allow(clippy::absurd_extreme_comparisons)]
3673const _: () = assert!(
3674    std::mem::size_of::<ExtensionPosFormat1FixedHeader>() <= ExtensionPosFormat1::<()>::MIN_SIZE
3675);
3676
3677/// [Extension Positioning Subtable Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#extension-positioning-subtable-format-1)
3678#[derive(Clone)]
3679pub struct ExtensionPosFormat1<'a, T = ()> {
3680    data: TableDataRef<'a, ExtensionPosFormat1FixedHeader>,
3681    offset_type: std::marker::PhantomData<*const T>,
3682}
3683
3684#[allow(clippy::needless_lifetimes)]
3685impl<'a, T> ExtensionPosFormat1<'a, T> {
3686    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
3687    basic_table_impls!(impl_the_methods);
3688
3689    /// Format identifier: format = 1
3690    pub fn pos_format(&self) -> u16 {
3691        self.data.header().pos_format.get()
3692    }
3693
3694    /// Lookup type of subtable referenced by extensionOffset (i.e. the
3695    /// extension subtable).
3696    pub fn extension_lookup_type(&self) -> u16 {
3697        self.data.header().extension_lookup_type.get()
3698    }
3699
3700    /// Offset to the extension subtable, of lookup type
3701    /// extensionLookupType, relative to the start of the
3702    /// ExtensionPosFormat1 subtable.
3703    pub fn extension_offset(&self) -> Offset32 {
3704        self.data.header().extension_offset.get()
3705    }
3706
3707    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
3708    pub fn extension(&self) -> Result<T, ReadError>
3709    where
3710        T: FontRead<'a, Args = ()>,
3711    {
3712        let data = self.offset_data();
3713        self.extension_offset().resolve(data)
3714    }
3715
3716    pub fn pos_format_byte_range(&self) -> Range<usize> {
3717        let start = 0;
3718        let end = start + u16::RAW_BYTE_LEN;
3719        start..end
3720    }
3721
3722    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
3723        let start = self.pos_format_byte_range().end;
3724        let end = start + u16::RAW_BYTE_LEN;
3725        start..end
3726    }
3727
3728    pub fn extension_offset_byte_range(&self) -> Range<usize> {
3729        let start = self.extension_lookup_type_byte_range().end;
3730        let end = start + Offset32::RAW_BYTE_LEN;
3731        start..end
3732    }
3733}
3734
3735const _: () = assert!(FontData::default_data_long_enough(
3736    ExtensionPosFormat1::<()>::MIN_SIZE
3737));
3738
3739impl<T> Default for ExtensionPosFormat1<'_, T> {
3740    fn default() -> Self {
3741        Self {
3742            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
3743                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3744            offset_type: std::marker::PhantomData,
3745        }
3746    }
3747}
3748
3749/// A [GPOS Extension Positioning](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#lookuptype-9-extension-positioning) subtable
3750pub enum ExtensionSubtable<'a> {
3751    Single(ExtensionPosFormat1<'a, SinglePos<'a>>),
3752    Pair(ExtensionPosFormat1<'a, PairPos<'a>>),
3753    Cursive(ExtensionPosFormat1<'a, CursivePosFormat1<'a>>),
3754    MarkToBase(ExtensionPosFormat1<'a, MarkBasePosFormat1<'a>>),
3755    MarkToLig(ExtensionPosFormat1<'a, MarkLigPosFormat1<'a>>),
3756    MarkToMark(ExtensionPosFormat1<'a, MarkMarkPosFormat1<'a>>),
3757    Contextual(ExtensionPosFormat1<'a, PositionSequenceContext<'a>>),
3758    ChainContextual(ExtensionPosFormat1<'a, PositionChainContext<'a>>),
3759}
3760
3761impl Default for ExtensionSubtable<'_> {
3762    fn default() -> Self {
3763        Self::Single(Default::default())
3764    }
3765}
3766
3767impl ReadArgs for ExtensionSubtable<'_> {
3768    type Args = ();
3769}
3770
3771impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
3772    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3773        let discriminant = ExtensionPosFormat1::read_discriminant(bytes)?;
3774        match discriminant {
3775            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
3776            2 => Ok(ExtensionSubtable::Pair(FontRead::read(bytes)?)),
3777            3 => Ok(ExtensionSubtable::Cursive(FontRead::read(bytes)?)),
3778            4 => Ok(ExtensionSubtable::MarkToBase(FontRead::read(bytes)?)),
3779            5 => Ok(ExtensionSubtable::MarkToLig(FontRead::read(bytes)?)),
3780            6 => Ok(ExtensionSubtable::MarkToMark(FontRead::read(bytes)?)),
3781            7 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
3782            8 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
3783            other => Err(ReadError::InvalidFormat(other.into())),
3784        }
3785    }
3786}
3787
3788impl<'a> ExtensionSubtable<'a> {
3789    #[allow(dead_code)]
3790    /// Return the inner table, removing the specific generics.
3791    ///
3792    /// This lets us return a single concrete type we can call methods on.
3793    pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> {
3794        match self {
3795            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
3796            ExtensionSubtable::Pair(inner) => inner.of_unit_type(),
3797            ExtensionSubtable::Cursive(inner) => inner.of_unit_type(),
3798            ExtensionSubtable::MarkToBase(inner) => inner.of_unit_type(),
3799            ExtensionSubtable::MarkToLig(inner) => inner.of_unit_type(),
3800            ExtensionSubtable::MarkToMark(inner) => inner.of_unit_type(),
3801            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
3802            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
3803        }
3804    }
3805}