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<Nullable<Offset16>>,
47    pub feature_list_offset: BigEndian<Nullable<Offset16>>,
48    pub lookup_list_offset: BigEndian<Nullable<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) -> Nullable<Offset16> {
76        self.data.header().script_list_offset.get()
77    }
78
79    /// Offset to FeatureList table, from beginning of GPOS table
80    pub fn feature_list_offset(&self) -> Nullable<Offset16> {
81        self.data.header().feature_list_offset.get()
82    }
83
84    /// Offset to LookupList table, from beginning of GPOS table
85    pub fn lookup_list_offset(&self) -> Nullable<Offset16> {
86        self.data.header().lookup_list_offset.get()
87    }
88
89    pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
90        let range = self.feature_variations_offset_byte_range();
91        (!range.is_empty())
92            .then(|| self.offset_data().read_at(range.start).ok())
93            .flatten()
94    }
95
96    /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset].
97    pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
98        let data = self.offset_data();
99        self.feature_variations_offset().map(|x| x.resolve(data))?
100    }
101
102    /// 32-bit offset to ScriptList, taking precedence when nonzero.
103    pub fn script_list2_offset(&self) -> Option<Nullable<Offset32>> {
104        let range = self.script_list2_offset_byte_range();
105        (!range.is_empty())
106            .then(|| self.offset_data().read_at(range.start).ok())
107            .flatten()
108    }
109
110    /// Attempt to resolve [`script_list2_offset`][Self::script_list2_offset].
111    pub fn script_list2(&self) -> Option<Result<ScriptList<'a>, ReadError>> {
112        let data = self.offset_data();
113        self.script_list2_offset().map(|x| x.resolve(data))?
114    }
115
116    /// 32-bit offset to FeatureList, taking precedence when nonzero.
117    pub fn feature_list2_offset(&self) -> Option<Nullable<Offset32>> {
118        let range = self.feature_list2_offset_byte_range();
119        (!range.is_empty())
120            .then(|| self.offset_data().read_at(range.start).ok())
121            .flatten()
122    }
123
124    /// Attempt to resolve [`feature_list2_offset`][Self::feature_list2_offset].
125    pub fn feature_list2(&self) -> Option<Result<FeatureList<'a>, ReadError>> {
126        let data = self.offset_data();
127        self.feature_list2_offset().map(|x| x.resolve(data))?
128    }
129
130    /// 32-bit offset to LookupList2, taking precedence when nonzero.
131    pub fn lookup_list2_offset(&self) -> Option<Nullable<Offset32>> {
132        let range = self.lookup_list2_offset_byte_range();
133        (!range.is_empty())
134            .then(|| self.offset_data().read_at(range.start).ok())
135            .flatten()
136    }
137
138    /// Attempt to resolve [`lookup_list2_offset`][Self::lookup_list2_offset].
139    pub fn lookup_list2(&self) -> Option<Result<PositionLookupList2<'a>, ReadError>> {
140        let data = self.offset_data();
141        self.lookup_list2_offset().map(|x| x.resolve(data))?
142    }
143
144    pub fn version_byte_range(&self) -> Range<usize> {
145        let start = 0;
146        let end = start + MajorMinor::RAW_BYTE_LEN;
147        start..end
148    }
149
150    pub fn script_list_offset_byte_range(&self) -> Range<usize> {
151        let start = self.version_byte_range().end;
152        let end = start + Offset16::RAW_BYTE_LEN;
153        start..end
154    }
155
156    pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
157        let start = self.script_list_offset_byte_range().end;
158        let end = start + Offset16::RAW_BYTE_LEN;
159        start..end
160    }
161
162    pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
163        let start = self.feature_list_offset_byte_range().end;
164        let end = start + Offset16::RAW_BYTE_LEN;
165        start..end
166    }
167
168    pub fn feature_variations_offset_byte_range(&self) -> Range<usize> {
169        let start = self.lookup_list_offset_byte_range().end;
170        let end = if self.version().compatible((1u16, 1u16)) {
171            start + Offset32::RAW_BYTE_LEN
172        } else {
173            start
174        };
175        start..end
176    }
177
178    pub fn script_list2_offset_byte_range(&self) -> Range<usize> {
179        let start = self.feature_variations_offset_byte_range().end;
180        let end = if self.version().compatible((1u16, 2u16)) {
181            start + Offset32::RAW_BYTE_LEN
182        } else {
183            start
184        };
185        start..end
186    }
187
188    pub fn feature_list2_offset_byte_range(&self) -> Range<usize> {
189        let start = self.script_list2_offset_byte_range().end;
190        let end = if self.version().compatible((1u16, 2u16)) {
191            start + Offset32::RAW_BYTE_LEN
192        } else {
193            start
194        };
195        start..end
196    }
197
198    pub fn lookup_list2_offset_byte_range(&self) -> Range<usize> {
199        let start = self.feature_list2_offset_byte_range().end;
200        let end = if self.version().compatible((1u16, 2u16)) {
201            start + Offset32::RAW_BYTE_LEN
202        } else {
203            start
204        };
205        start..end
206    }
207}
208
209const _: () = assert!(FontData::default_data_long_enough(Gpos::MIN_SIZE));
210
211impl Default for Gpos<'_> {
212    fn default() -> Self {
213        Self {
214            data: TableDataRef::new(FontData::default_table_data().as_bytes())
215                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
216        }
217    }
218}
219
220/// A [GPOS Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#gsubLookupTypeEnum) subtable.
221pub enum PositionLookup<'a> {
222    Single(Lookup<'a, SinglePos<'a>>),
223    Pair(Lookup<'a, PairPos<'a>>),
224    Cursive(Lookup<'a, CursivePos<'a>>),
225    MarkToBase(Lookup<'a, MarkBasePos<'a>>),
226    MarkToLig(Lookup<'a, MarkLigPos<'a>>),
227    MarkToMark(Lookup<'a, MarkMarkPos<'a>>),
228    Contextual(Lookup<'a, PositionSequenceContext<'a>>),
229    ChainContextual(Lookup<'a, PositionChainContext<'a>>),
230    Extension(Lookup<'a, ExtensionSubtable<'a>>),
231}
232
233impl Default for PositionLookup<'_> {
234    fn default() -> Self {
235        Self::Single(Default::default())
236    }
237}
238
239impl ReadArgs for PositionLookup<'_> {
240    type Args = ();
241}
242
243impl<'a> FontRead<'a> for PositionLookup<'a> {
244    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
245        let discriminant = Lookup::read_discriminant(bytes)?;
246        match discriminant {
247            1 => Ok(PositionLookup::Single(FontRead::read(bytes)?)),
248            2 => Ok(PositionLookup::Pair(FontRead::read(bytes)?)),
249            3 => Ok(PositionLookup::Cursive(FontRead::read(bytes)?)),
250            4 => Ok(PositionLookup::MarkToBase(FontRead::read(bytes)?)),
251            5 => Ok(PositionLookup::MarkToLig(FontRead::read(bytes)?)),
252            6 => Ok(PositionLookup::MarkToMark(FontRead::read(bytes)?)),
253            7 => Ok(PositionLookup::Contextual(FontRead::read(bytes)?)),
254            8 => Ok(PositionLookup::ChainContextual(FontRead::read(bytes)?)),
255            9 => Ok(PositionLookup::Extension(FontRead::read(bytes)?)),
256            other => Err(ReadError::InvalidFormat(other.into())),
257        }
258    }
259}
260
261impl<'a> PositionLookup<'a> {
262    #[allow(dead_code)]
263    /// Return the inner table, removing the specific generics.
264    ///
265    /// This lets us return a single concrete type we can call methods on.
266    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
267        match self {
268            PositionLookup::Single(inner) => inner.of_unit_type(),
269            PositionLookup::Pair(inner) => inner.of_unit_type(),
270            PositionLookup::Cursive(inner) => inner.of_unit_type(),
271            PositionLookup::MarkToBase(inner) => inner.of_unit_type(),
272            PositionLookup::MarkToLig(inner) => inner.of_unit_type(),
273            PositionLookup::MarkToMark(inner) => inner.of_unit_type(),
274            PositionLookup::Contextual(inner) => inner.of_unit_type(),
275            PositionLookup::ChainContextual(inner) => inner.of_unit_type(),
276            PositionLookup::Extension(inner) => inner.of_unit_type(),
277        }
278    }
279}
280
281/// See [ValueRecord]
282#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, bytemuck :: AnyBitPattern)]
283#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
284#[repr(transparent)]
285pub struct ValueFormat {
286    bits: u16,
287}
288
289impl ValueFormat {
290    /// Includes horizontal adjustment for placement
291    pub const X_PLACEMENT: Self = Self { bits: 0x0001 };
292
293    /// Includes vertical adjustment for placement
294    pub const Y_PLACEMENT: Self = Self { bits: 0x0002 };
295
296    /// Includes horizontal adjustment for advance
297    pub const X_ADVANCE: Self = Self { bits: 0x0004 };
298
299    /// Includes vertical adjustment for advance
300    pub const Y_ADVANCE: Self = Self { bits: 0x0008 };
301
302    /// Includes Device table (non-variable font) / VariationIndex
303    /// table (variable font) for horizontal placement
304    pub const X_PLACEMENT_DEVICE: Self = Self { bits: 0x0010 };
305
306    /// Includes Device table (non-variable font) / VariationIndex
307    /// table (variable font) for vertical placement
308    pub const Y_PLACEMENT_DEVICE: Self = Self { bits: 0x0020 };
309
310    /// Includes Device table (non-variable font) / VariationIndex
311    /// table (variable font) for horizontal advance
312    pub const X_ADVANCE_DEVICE: Self = Self { bits: 0x0040 };
313
314    /// Includes Device table (non-variable font) / VariationIndex
315    /// table (variable font) for vertical advance
316    pub const Y_ADVANCE_DEVICE: Self = Self { bits: 0x0080 };
317}
318
319impl ValueFormat {
320    ///  Returns an empty set of flags.
321    #[inline]
322    pub const fn empty() -> Self {
323        Self { bits: 0 }
324    }
325
326    /// Returns the set containing all flags.
327    #[inline]
328    pub const fn all() -> Self {
329        Self {
330            bits: Self::X_PLACEMENT.bits
331                | Self::Y_PLACEMENT.bits
332                | Self::X_ADVANCE.bits
333                | Self::Y_ADVANCE.bits
334                | Self::X_PLACEMENT_DEVICE.bits
335                | Self::Y_PLACEMENT_DEVICE.bits
336                | Self::X_ADVANCE_DEVICE.bits
337                | Self::Y_ADVANCE_DEVICE.bits,
338        }
339    }
340
341    /// Returns the raw value of the flags currently stored.
342    #[inline]
343    pub const fn bits(&self) -> u16 {
344        self.bits
345    }
346
347    /// Convert from underlying bit representation, unless that
348    /// representation contains bits that do not correspond to a flag.
349    #[inline]
350    pub const fn from_bits(bits: u16) -> Option<Self> {
351        if (bits & !Self::all().bits()) == 0 {
352            Some(Self { bits })
353        } else {
354            None
355        }
356    }
357
358    /// Convert from underlying bit representation, dropping any bits
359    /// that do not correspond to flags.
360    #[inline]
361    pub const fn from_bits_truncate(bits: u16) -> Self {
362        Self {
363            bits: bits & Self::all().bits,
364        }
365    }
366
367    /// Returns `true` if no flags are currently stored.
368    #[inline]
369    pub const fn is_empty(&self) -> bool {
370        self.bits() == Self::empty().bits()
371    }
372
373    /// Returns `true` if there are flags common to both `self` and `other`.
374    #[inline]
375    pub const fn intersects(&self, other: Self) -> bool {
376        !(Self {
377            bits: self.bits & other.bits,
378        })
379        .is_empty()
380    }
381
382    /// Returns `true` if all of the flags in `other` are contained within `self`.
383    #[inline]
384    pub const fn contains(&self, other: Self) -> bool {
385        (self.bits & other.bits) == other.bits
386    }
387
388    /// Inserts the specified flags in-place.
389    #[inline]
390    pub fn insert(&mut self, other: Self) {
391        self.bits |= other.bits;
392    }
393
394    /// Removes the specified flags in-place.
395    #[inline]
396    pub fn remove(&mut self, other: Self) {
397        self.bits &= !other.bits;
398    }
399
400    /// Toggles the specified flags in-place.
401    #[inline]
402    pub fn toggle(&mut self, other: Self) {
403        self.bits ^= other.bits;
404    }
405
406    /// Returns the intersection between the flags in `self` and
407    /// `other`.
408    ///
409    /// Specifically, the returned set contains only the flags which are
410    /// present in *both* `self` *and* `other`.
411    ///
412    /// This is equivalent to using the `&` operator (e.g.
413    /// [`ops::BitAnd`]), as in `flags & other`.
414    ///
415    /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
416    #[inline]
417    #[must_use]
418    pub const fn intersection(self, other: Self) -> Self {
419        Self {
420            bits: self.bits & other.bits,
421        }
422    }
423
424    /// Returns the union of between the flags in `self` and `other`.
425    ///
426    /// Specifically, the returned set contains all flags which are
427    /// present in *either* `self` *or* `other`, including any which are
428    /// present in both.
429    ///
430    /// This is equivalent to using the `|` operator (e.g.
431    /// [`ops::BitOr`]), as in `flags | other`.
432    ///
433    /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
434    #[inline]
435    #[must_use]
436    pub const fn union(self, other: Self) -> Self {
437        Self {
438            bits: self.bits | other.bits,
439        }
440    }
441
442    /// Returns the difference between the flags in `self` and `other`.
443    ///
444    /// Specifically, the returned set contains all flags present in
445    /// `self`, except for the ones present in `other`.
446    ///
447    /// It is also conceptually equivalent to the "bit-clear" operation:
448    /// `flags & !other` (and this syntax is also supported).
449    ///
450    /// This is equivalent to using the `-` operator (e.g.
451    /// [`ops::Sub`]), as in `flags - other`.
452    ///
453    /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
454    #[inline]
455    #[must_use]
456    pub const fn difference(self, other: Self) -> Self {
457        Self {
458            bits: self.bits & !other.bits,
459        }
460    }
461}
462
463impl std::ops::BitOr for ValueFormat {
464    type Output = Self;
465
466    /// Returns the union of the two sets of flags.
467    #[inline]
468    fn bitor(self, other: ValueFormat) -> Self {
469        Self {
470            bits: self.bits | other.bits,
471        }
472    }
473}
474
475impl std::ops::BitOrAssign for ValueFormat {
476    /// Adds the set of flags.
477    #[inline]
478    fn bitor_assign(&mut self, other: Self) {
479        self.bits |= other.bits;
480    }
481}
482
483impl std::ops::BitXor for ValueFormat {
484    type Output = Self;
485
486    /// Returns the left flags, but with all the right flags toggled.
487    #[inline]
488    fn bitxor(self, other: Self) -> Self {
489        Self {
490            bits: self.bits ^ other.bits,
491        }
492    }
493}
494
495impl std::ops::BitXorAssign for ValueFormat {
496    /// Toggles the set of flags.
497    #[inline]
498    fn bitxor_assign(&mut self, other: Self) {
499        self.bits ^= other.bits;
500    }
501}
502
503impl std::ops::BitAnd for ValueFormat {
504    type Output = Self;
505
506    /// Returns the intersection between the two sets of flags.
507    #[inline]
508    fn bitand(self, other: Self) -> Self {
509        Self {
510            bits: self.bits & other.bits,
511        }
512    }
513}
514
515impl std::ops::BitAndAssign for ValueFormat {
516    /// Disables all flags disabled in the set.
517    #[inline]
518    fn bitand_assign(&mut self, other: Self) {
519        self.bits &= other.bits;
520    }
521}
522
523impl std::ops::Sub for ValueFormat {
524    type Output = Self;
525
526    /// Returns the set difference of the two sets of flags.
527    #[inline]
528    fn sub(self, other: Self) -> Self {
529        Self {
530            bits: self.bits & !other.bits,
531        }
532    }
533}
534
535impl std::ops::SubAssign for ValueFormat {
536    /// Disables all flags enabled in the set.
537    #[inline]
538    fn sub_assign(&mut self, other: Self) {
539        self.bits &= !other.bits;
540    }
541}
542
543impl std::ops::Not for ValueFormat {
544    type Output = Self;
545
546    /// Returns the complement of this set of flags.
547    #[inline]
548    fn not(self) -> Self {
549        Self { bits: !self.bits } & Self::all()
550    }
551}
552
553impl std::fmt::Debug for ValueFormat {
554    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
555        let members: &[(&str, Self)] = &[
556            ("X_PLACEMENT", Self::X_PLACEMENT),
557            ("Y_PLACEMENT", Self::Y_PLACEMENT),
558            ("X_ADVANCE", Self::X_ADVANCE),
559            ("Y_ADVANCE", Self::Y_ADVANCE),
560            ("X_PLACEMENT_DEVICE", Self::X_PLACEMENT_DEVICE),
561            ("Y_PLACEMENT_DEVICE", Self::Y_PLACEMENT_DEVICE),
562            ("X_ADVANCE_DEVICE", Self::X_ADVANCE_DEVICE),
563            ("Y_ADVANCE_DEVICE", Self::Y_ADVANCE_DEVICE),
564        ];
565        let mut first = true;
566        for (name, value) in members {
567            if self.contains(*value) {
568                if !first {
569                    f.write_str(" | ")?;
570                }
571                first = false;
572                f.write_str(name)?;
573            }
574        }
575        if first {
576            f.write_str("(empty)")?;
577        }
578        Ok(())
579    }
580}
581
582impl std::fmt::Binary for ValueFormat {
583    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
584        std::fmt::Binary::fmt(&self.bits, f)
585    }
586}
587
588impl std::fmt::Octal for ValueFormat {
589    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
590        std::fmt::Octal::fmt(&self.bits, f)
591    }
592}
593
594impl std::fmt::LowerHex for ValueFormat {
595    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
596        std::fmt::LowerHex::fmt(&self.bits, f)
597    }
598}
599
600impl std::fmt::UpperHex for ValueFormat {
601    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
602        std::fmt::UpperHex::fmt(&self.bits, f)
603    }
604}
605
606impl font_types::Scalar for ValueFormat {
607    type Raw = <u16 as font_types::Scalar>::Raw;
608    fn to_raw(self) -> Self::Raw {
609        self.bits().to_raw()
610    }
611    fn from_raw(raw: Self::Raw) -> Self {
612        let t = <u16>::from_raw(raw);
613        Self::from_bits_truncate(t)
614    }
615}
616
617/// [Anchor Tables](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables)
618/// position one glyph with respect to another.
619#[derive(Clone)]
620pub enum AnchorTable<'a> {
621    Format1(AnchorFormat1<'a>),
622    Format2(AnchorFormat2<'a>),
623    Format3(AnchorFormat3<'a>),
624}
625
626impl Default for AnchorTable<'_> {
627    fn default() -> Self {
628        Self::Format1(Default::default())
629    }
630}
631
632impl<'a> AnchorTable<'a> {
633    ///Return the `FontData` used to resolve offsets for this table.
634    pub fn offset_data(&self) -> FontData<'a> {
635        match self {
636            Self::Format1(item) => item.offset_data(),
637            Self::Format2(item) => item.offset_data(),
638            Self::Format3(item) => item.offset_data(),
639        }
640    }
641
642    /// Format identifier, = 1
643    pub fn anchor_format(&self) -> u16 {
644        match self {
645            Self::Format1(item) => item.anchor_format(),
646            Self::Format2(item) => item.anchor_format(),
647            Self::Format3(item) => item.anchor_format(),
648        }
649    }
650
651    /// Horizontal value, in design units
652    pub fn x_coordinate(&self) -> i16 {
653        match self {
654            Self::Format1(item) => item.x_coordinate(),
655            Self::Format2(item) => item.x_coordinate(),
656            Self::Format3(item) => item.x_coordinate(),
657        }
658    }
659
660    /// Vertical value, in design units
661    pub fn y_coordinate(&self) -> i16 {
662        match self {
663            Self::Format1(item) => item.y_coordinate(),
664            Self::Format2(item) => item.y_coordinate(),
665            Self::Format3(item) => item.y_coordinate(),
666        }
667    }
668}
669
670impl ReadArgs for AnchorTable<'_> {
671    type Args = ();
672}
673
674impl<'a> FontRead<'a> for AnchorTable<'a> {
675    #[inline]
676    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
677        let format: u16 = data.read_at(0usize)?;
678        match format {
679            AnchorFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
680            AnchorFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
681            AnchorFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
682            other => Err(ReadError::InvalidFormat(other.into())),
683        }
684    }
685}
686
687impl<'a> MinByteRange<'a> for AnchorTable<'a> {
688    fn min_byte_range(&self) -> Range<usize> {
689        match self {
690            Self::Format1(item) => item.min_byte_range(),
691            Self::Format2(item) => item.min_byte_range(),
692            Self::Format3(item) => item.min_byte_range(),
693        }
694    }
695    fn min_table_bytes(&self) -> &'a [u8] {
696        match self {
697            Self::Format1(item) => item.min_table_bytes(),
698            Self::Format2(item) => item.min_table_bytes(),
699            Self::Format3(item) => item.min_table_bytes(),
700        }
701    }
702}
703
704impl Format<u16> for AnchorFormat1<'_> {
705    const FORMAT: u16 = 1;
706}
707
708impl<'a> MinByteRange<'a> for AnchorFormat1<'a> {
709    fn min_byte_range(&self) -> Range<usize> {
710        0..self.y_coordinate_byte_range().end
711    }
712    fn min_table_bytes(&self) -> &'a [u8] {
713        let range = self.min_byte_range();
714        self.offset_data().as_bytes().get(range).unwrap_or_default()
715    }
716}
717
718impl ReadArgs for AnchorFormat1<'_> {
719    type Args = ();
720}
721
722impl<'a> FontRead<'a> for AnchorFormat1<'a> {
723    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
724        #[allow(clippy::absurd_extreme_comparisons)]
725        if data.len() < Self::MIN_SIZE {
726            return Err(ReadError::OutOfBounds);
727        }
728        let Some(data) = TableDataRef::new(data.as_bytes()) else {
729            return Err(ReadError::OutOfBounds);
730        };
731        Ok(Self { data })
732    }
733}
734
735/// The fields at the start of the table, which are read directly.
736#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
737#[repr(C, packed)]
738#[doc(hidden)]
739pub struct AnchorFormat1FixedHeader {
740    pub anchor_format: BigEndian<u16>,
741    pub x_coordinate: BigEndian<i16>,
742    pub y_coordinate: BigEndian<i16>,
743}
744
745#[allow(clippy::absurd_extreme_comparisons)]
746const _: () = assert!(std::mem::size_of::<AnchorFormat1FixedHeader>() <= AnchorFormat1::MIN_SIZE);
747
748/// [Anchor Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-1-design-units): Design Units
749#[derive(Clone)]
750pub struct AnchorFormat1<'a> {
751    data: TableDataRef<'a, AnchorFormat1FixedHeader>,
752}
753
754#[allow(clippy::needless_lifetimes)]
755impl<'a> AnchorFormat1<'a> {
756    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
757    basic_table_impls!(impl_the_methods);
758
759    /// Format identifier, = 1
760    pub fn anchor_format(&self) -> u16 {
761        self.data.header().anchor_format.get()
762    }
763
764    /// Horizontal value, in design units
765    pub fn x_coordinate(&self) -> i16 {
766        self.data.header().x_coordinate.get()
767    }
768
769    /// Vertical value, in design units
770    pub fn y_coordinate(&self) -> i16 {
771        self.data.header().y_coordinate.get()
772    }
773
774    pub fn anchor_format_byte_range(&self) -> Range<usize> {
775        let start = 0;
776        let end = start + u16::RAW_BYTE_LEN;
777        start..end
778    }
779
780    pub fn x_coordinate_byte_range(&self) -> Range<usize> {
781        let start = self.anchor_format_byte_range().end;
782        let end = start + i16::RAW_BYTE_LEN;
783        start..end
784    }
785
786    pub fn y_coordinate_byte_range(&self) -> Range<usize> {
787        let start = self.x_coordinate_byte_range().end;
788        let end = start + i16::RAW_BYTE_LEN;
789        start..end
790    }
791}
792
793const _: () = assert!(FontData::default_data_long_enough(AnchorFormat1::MIN_SIZE));
794
795impl Default for AnchorFormat1<'_> {
796    fn default() -> Self {
797        Self {
798            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
799                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
800        }
801    }
802}
803
804impl Format<u16> for AnchorFormat2<'_> {
805    const FORMAT: u16 = 2;
806}
807
808impl<'a> MinByteRange<'a> for AnchorFormat2<'a> {
809    fn min_byte_range(&self) -> Range<usize> {
810        0..self.anchor_point_byte_range().end
811    }
812    fn min_table_bytes(&self) -> &'a [u8] {
813        let range = self.min_byte_range();
814        self.offset_data().as_bytes().get(range).unwrap_or_default()
815    }
816}
817
818impl ReadArgs for AnchorFormat2<'_> {
819    type Args = ();
820}
821
822impl<'a> FontRead<'a> for AnchorFormat2<'a> {
823    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
824        #[allow(clippy::absurd_extreme_comparisons)]
825        if data.len() < Self::MIN_SIZE {
826            return Err(ReadError::OutOfBounds);
827        }
828        let Some(data) = TableDataRef::new(data.as_bytes()) else {
829            return Err(ReadError::OutOfBounds);
830        };
831        Ok(Self { data })
832    }
833}
834
835/// The fields at the start of the table, which are read directly.
836#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
837#[repr(C, packed)]
838#[doc(hidden)]
839pub struct AnchorFormat2FixedHeader {
840    pub anchor_format: BigEndian<u16>,
841    pub x_coordinate: BigEndian<i16>,
842    pub y_coordinate: BigEndian<i16>,
843    pub anchor_point: BigEndian<u16>,
844}
845
846#[allow(clippy::absurd_extreme_comparisons)]
847const _: () = assert!(std::mem::size_of::<AnchorFormat2FixedHeader>() <= AnchorFormat2::MIN_SIZE);
848
849/// [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
850#[derive(Clone)]
851pub struct AnchorFormat2<'a> {
852    data: TableDataRef<'a, AnchorFormat2FixedHeader>,
853}
854
855#[allow(clippy::needless_lifetimes)]
856impl<'a> AnchorFormat2<'a> {
857    pub const MIN_SIZE: usize =
858        (u16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
859    basic_table_impls!(impl_the_methods);
860
861    /// Format identifier, = 2
862    pub fn anchor_format(&self) -> u16 {
863        self.data.header().anchor_format.get()
864    }
865
866    /// Horizontal value, in design units
867    pub fn x_coordinate(&self) -> i16 {
868        self.data.header().x_coordinate.get()
869    }
870
871    /// Vertical value, in design units
872    pub fn y_coordinate(&self) -> i16 {
873        self.data.header().y_coordinate.get()
874    }
875
876    /// Index to glyph contour point
877    pub fn anchor_point(&self) -> u16 {
878        self.data.header().anchor_point.get()
879    }
880
881    pub fn anchor_format_byte_range(&self) -> Range<usize> {
882        let start = 0;
883        let end = start + u16::RAW_BYTE_LEN;
884        start..end
885    }
886
887    pub fn x_coordinate_byte_range(&self) -> Range<usize> {
888        let start = self.anchor_format_byte_range().end;
889        let end = start + i16::RAW_BYTE_LEN;
890        start..end
891    }
892
893    pub fn y_coordinate_byte_range(&self) -> Range<usize> {
894        let start = self.x_coordinate_byte_range().end;
895        let end = start + i16::RAW_BYTE_LEN;
896        start..end
897    }
898
899    pub fn anchor_point_byte_range(&self) -> Range<usize> {
900        let start = self.y_coordinate_byte_range().end;
901        let end = start + u16::RAW_BYTE_LEN;
902        start..end
903    }
904}
905
906impl Format<u16> for AnchorFormat3<'_> {
907    const FORMAT: u16 = 3;
908}
909
910impl<'a> MinByteRange<'a> for AnchorFormat3<'a> {
911    fn min_byte_range(&self) -> Range<usize> {
912        0..self.y_device_offset_byte_range().end
913    }
914    fn min_table_bytes(&self) -> &'a [u8] {
915        let range = self.min_byte_range();
916        self.offset_data().as_bytes().get(range).unwrap_or_default()
917    }
918}
919
920impl ReadArgs for AnchorFormat3<'_> {
921    type Args = ();
922}
923
924impl<'a> FontRead<'a> for AnchorFormat3<'a> {
925    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
926        #[allow(clippy::absurd_extreme_comparisons)]
927        if data.len() < Self::MIN_SIZE {
928            return Err(ReadError::OutOfBounds);
929        }
930        let Some(data) = TableDataRef::new(data.as_bytes()) else {
931            return Err(ReadError::OutOfBounds);
932        };
933        Ok(Self { data })
934    }
935}
936
937/// The fields at the start of the table, which are read directly.
938#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
939#[repr(C, packed)]
940#[doc(hidden)]
941pub struct AnchorFormat3FixedHeader {
942    pub anchor_format: BigEndian<u16>,
943    pub x_coordinate: BigEndian<i16>,
944    pub y_coordinate: BigEndian<i16>,
945    pub x_device_offset: BigEndian<Nullable<Offset16>>,
946    pub y_device_offset: BigEndian<Nullable<Offset16>>,
947}
948
949#[allow(clippy::absurd_extreme_comparisons)]
950const _: () = assert!(std::mem::size_of::<AnchorFormat3FixedHeader>() <= AnchorFormat3::MIN_SIZE);
951
952/// [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
953#[derive(Clone)]
954pub struct AnchorFormat3<'a> {
955    data: TableDataRef<'a, AnchorFormat3FixedHeader>,
956}
957
958#[allow(clippy::needless_lifetimes)]
959impl<'a> AnchorFormat3<'a> {
960    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
961        + i16::RAW_BYTE_LEN
962        + i16::RAW_BYTE_LEN
963        + Offset16::RAW_BYTE_LEN
964        + Offset16::RAW_BYTE_LEN);
965    basic_table_impls!(impl_the_methods);
966
967    /// Format identifier, = 3
968    pub fn anchor_format(&self) -> u16 {
969        self.data.header().anchor_format.get()
970    }
971
972    /// Horizontal value, in design units
973    pub fn x_coordinate(&self) -> i16 {
974        self.data.header().x_coordinate.get()
975    }
976
977    /// Vertical value, in design units
978    pub fn y_coordinate(&self) -> i16 {
979        self.data.header().y_coordinate.get()
980    }
981
982    /// Offset to Device table (non-variable font) / VariationIndex
983    /// table (variable font) for X coordinate, from beginning of
984    /// Anchor table (may be NULL)
985    pub fn x_device_offset(&self) -> Nullable<Offset16> {
986        self.data.header().x_device_offset.get()
987    }
988
989    /// Attempt to resolve [`x_device_offset`][Self::x_device_offset].
990    pub fn x_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
991        let data = self.offset_data();
992        self.x_device_offset().resolve(data)
993    }
994
995    /// Offset to Device table (non-variable font) / VariationIndex
996    /// table (variable font) for Y coordinate, from beginning of
997    /// Anchor table (may be NULL)
998    pub fn y_device_offset(&self) -> Nullable<Offset16> {
999        self.data.header().y_device_offset.get()
1000    }
1001
1002    /// Attempt to resolve [`y_device_offset`][Self::y_device_offset].
1003    pub fn y_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
1004        let data = self.offset_data();
1005        self.y_device_offset().resolve(data)
1006    }
1007
1008    pub fn anchor_format_byte_range(&self) -> Range<usize> {
1009        let start = 0;
1010        let end = start + u16::RAW_BYTE_LEN;
1011        start..end
1012    }
1013
1014    pub fn x_coordinate_byte_range(&self) -> Range<usize> {
1015        let start = self.anchor_format_byte_range().end;
1016        let end = start + i16::RAW_BYTE_LEN;
1017        start..end
1018    }
1019
1020    pub fn y_coordinate_byte_range(&self) -> Range<usize> {
1021        let start = self.x_coordinate_byte_range().end;
1022        let end = start + i16::RAW_BYTE_LEN;
1023        start..end
1024    }
1025
1026    pub fn x_device_offset_byte_range(&self) -> Range<usize> {
1027        let start = self.y_coordinate_byte_range().end;
1028        let end = start + Offset16::RAW_BYTE_LEN;
1029        start..end
1030    }
1031
1032    pub fn y_device_offset_byte_range(&self) -> Range<usize> {
1033        let start = self.x_device_offset_byte_range().end;
1034        let end = start + Offset16::RAW_BYTE_LEN;
1035        start..end
1036    }
1037}
1038
1039impl<'a> MinByteRange<'a> for MarkArray<'a> {
1040    fn min_byte_range(&self) -> Range<usize> {
1041        0..self.mark_records_byte_range().end
1042    }
1043    fn min_table_bytes(&self) -> &'a [u8] {
1044        let range = self.min_byte_range();
1045        self.offset_data().as_bytes().get(range).unwrap_or_default()
1046    }
1047}
1048
1049impl ReadArgs for MarkArray<'_> {
1050    type Args = ();
1051}
1052
1053impl<'a> FontRead<'a> for MarkArray<'a> {
1054    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1055        #[allow(clippy::absurd_extreme_comparisons)]
1056        if data.len() < Self::MIN_SIZE {
1057            return Err(ReadError::OutOfBounds);
1058        }
1059        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1060            return Err(ReadError::OutOfBounds);
1061        };
1062        Ok(Self { data })
1063    }
1064}
1065
1066/// The fields at the start of the table, which are read directly.
1067#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1068#[repr(C, packed)]
1069#[doc(hidden)]
1070pub struct MarkArrayFixedHeader {
1071    pub mark_count: BigEndian<u16>,
1072}
1073
1074#[allow(clippy::absurd_extreme_comparisons)]
1075const _: () = assert!(std::mem::size_of::<MarkArrayFixedHeader>() <= MarkArray::MIN_SIZE);
1076
1077/// [Mark Array Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table)
1078#[derive(Clone)]
1079pub struct MarkArray<'a> {
1080    data: TableDataRef<'a, MarkArrayFixedHeader>,
1081}
1082
1083#[allow(clippy::needless_lifetimes)]
1084impl<'a> MarkArray<'a> {
1085    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1086    basic_table_impls!(impl_the_methods);
1087
1088    /// Number of MarkRecords
1089    pub fn mark_count(&self) -> u16 {
1090        self.data.header().mark_count.get()
1091    }
1092
1093    /// Array of MarkRecords, ordered by corresponding glyphs in the
1094    /// associated mark Coverage table.
1095    pub fn mark_records(&self) -> &'a [MarkRecord] {
1096        let range = self.mark_records_byte_range();
1097        self.offset_data()
1098            .read_array(range)
1099            .ok()
1100            .unwrap_or_default()
1101    }
1102
1103    pub fn mark_count_byte_range(&self) -> Range<usize> {
1104        let start = 0;
1105        let end = start + u16::RAW_BYTE_LEN;
1106        start..end
1107    }
1108
1109    pub fn mark_records_byte_range(&self) -> Range<usize> {
1110        let mark_count = self.mark_count();
1111        let start = self.mark_count_byte_range().end;
1112        let end =
1113            start + (transforms::to_usize(mark_count)).saturating_mul(MarkRecord::RAW_BYTE_LEN);
1114        start..end
1115    }
1116}
1117
1118const _: () = assert!(FontData::default_data_long_enough(MarkArray::MIN_SIZE));
1119
1120impl Default for MarkArray<'_> {
1121    fn default() -> Self {
1122        Self {
1123            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1124                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1125        }
1126    }
1127}
1128
1129/// Part of [MarkArray]
1130#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
1131#[repr(C)]
1132#[repr(packed)]
1133pub struct MarkRecord {
1134    /// Class defined for the associated mark.
1135    pub mark_class: BigEndian<u16>,
1136    /// Offset to Anchor table, from beginning of MarkArray table.
1137    pub mark_anchor_offset: BigEndian<Offset16>,
1138}
1139
1140impl MarkRecord {
1141    /// Class defined for the associated mark.
1142    pub fn mark_class(&self) -> u16 {
1143        self.mark_class.get()
1144    }
1145
1146    /// Offset to Anchor table, from beginning of MarkArray table.
1147    pub fn mark_anchor_offset(&self) -> Offset16 {
1148        self.mark_anchor_offset.get()
1149    }
1150
1151    /// Offset to Anchor table, from beginning of MarkArray table.
1152    ///
1153    /// The `data` argument should be retrieved from the parent table
1154    /// By calling its `offset_data` method.
1155    pub fn mark_anchor<'a>(&self, data: FontData<'a>) -> Result<AnchorTable<'a>, ReadError> {
1156        self.mark_anchor_offset().resolve(data)
1157    }
1158}
1159
1160impl FixedSize for MarkRecord {
1161    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
1162}
1163
1164/// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable
1165#[derive(Clone)]
1166pub enum SinglePos<'a> {
1167    Format1(SinglePosFormat1<'a>),
1168    Format2(SinglePosFormat2<'a>),
1169    Format3(SinglePosFormat3<'a>),
1170    Format4(SinglePosFormat4<'a>),
1171}
1172
1173impl Default for SinglePos<'_> {
1174    fn default() -> Self {
1175        Self::Format1(Default::default())
1176    }
1177}
1178
1179impl<'a> SinglePos<'a> {
1180    ///Return the `FontData` used to resolve offsets for this table.
1181    pub fn offset_data(&self) -> FontData<'a> {
1182        match self {
1183            Self::Format1(item) => item.offset_data(),
1184            Self::Format2(item) => item.offset_data(),
1185            Self::Format3(item) => item.offset_data(),
1186            Self::Format4(item) => item.offset_data(),
1187        }
1188    }
1189
1190    /// Format identifier: format = 1
1191    pub fn pos_format(&self) -> u16 {
1192        match self {
1193            Self::Format1(item) => item.pos_format(),
1194            Self::Format2(item) => item.pos_format(),
1195            Self::Format3(item) => item.pos_format(),
1196            Self::Format4(item) => item.pos_format(),
1197        }
1198    }
1199
1200    /// Defines the types of data in the ValueRecord.
1201    pub fn value_format(&self) -> ValueFormat {
1202        match self {
1203            Self::Format1(item) => item.value_format(),
1204            Self::Format2(item) => item.value_format(),
1205            Self::Format3(item) => item.value_format(),
1206            Self::Format4(item) => item.value_format(),
1207        }
1208    }
1209}
1210
1211impl ReadArgs for SinglePos<'_> {
1212    type Args = ();
1213}
1214
1215impl<'a> FontRead<'a> for SinglePos<'a> {
1216    #[inline]
1217    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1218        let format: u16 = data.read_at(0usize)?;
1219        match format {
1220            SinglePosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1221            SinglePosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1222            SinglePosFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
1223            SinglePosFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
1224            other => Err(ReadError::InvalidFormat(other.into())),
1225        }
1226    }
1227}
1228
1229impl<'a> MinByteRange<'a> for SinglePos<'a> {
1230    fn min_byte_range(&self) -> Range<usize> {
1231        match self {
1232            Self::Format1(item) => item.min_byte_range(),
1233            Self::Format2(item) => item.min_byte_range(),
1234            Self::Format3(item) => item.min_byte_range(),
1235            Self::Format4(item) => item.min_byte_range(),
1236        }
1237    }
1238    fn min_table_bytes(&self) -> &'a [u8] {
1239        match self {
1240            Self::Format1(item) => item.min_table_bytes(),
1241            Self::Format2(item) => item.min_table_bytes(),
1242            Self::Format3(item) => item.min_table_bytes(),
1243            Self::Format4(item) => item.min_table_bytes(),
1244        }
1245    }
1246}
1247
1248impl Format<u16> for SinglePosFormat1<'_> {
1249    const FORMAT: u16 = 1;
1250}
1251
1252impl<'a> MinByteRange<'a> for SinglePosFormat1<'a> {
1253    fn min_byte_range(&self) -> Range<usize> {
1254        0..self.value_record_byte_range().end
1255    }
1256    fn min_table_bytes(&self) -> &'a [u8] {
1257        let range = self.min_byte_range();
1258        self.offset_data().as_bytes().get(range).unwrap_or_default()
1259    }
1260}
1261
1262impl ReadArgs for SinglePosFormat1<'_> {
1263    type Args = ();
1264}
1265
1266impl<'a> FontRead<'a> for SinglePosFormat1<'a> {
1267    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1268        #[allow(clippy::absurd_extreme_comparisons)]
1269        if data.len() < Self::MIN_SIZE {
1270            return Err(ReadError::OutOfBounds);
1271        }
1272        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1273            return Err(ReadError::OutOfBounds);
1274        };
1275        Ok(Self { data })
1276    }
1277}
1278
1279/// The fields at the start of the table, which are read directly.
1280#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1281#[repr(C, packed)]
1282#[doc(hidden)]
1283pub struct SinglePosFormat1FixedHeader {
1284    pub pos_format: BigEndian<u16>,
1285    pub coverage_offset: BigEndian<Offset16>,
1286    pub value_format: BigEndian<ValueFormat>,
1287}
1288
1289#[allow(clippy::absurd_extreme_comparisons)]
1290const _: () =
1291    assert!(std::mem::size_of::<SinglePosFormat1FixedHeader>() <= SinglePosFormat1::MIN_SIZE);
1292
1293/// [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
1294#[derive(Clone)]
1295pub struct SinglePosFormat1<'a> {
1296    data: TableDataRef<'a, SinglePosFormat1FixedHeader>,
1297}
1298
1299#[allow(clippy::needless_lifetimes)]
1300impl<'a> SinglePosFormat1<'a> {
1301    pub const MIN_SIZE: usize =
1302        (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + ValueFormat::RAW_BYTE_LEN);
1303    basic_table_impls!(impl_the_methods);
1304
1305    /// Format identifier: format = 1
1306    pub fn pos_format(&self) -> u16 {
1307        self.data.header().pos_format.get()
1308    }
1309
1310    /// Offset to Coverage table, from beginning of SinglePos subtable.
1311    pub fn coverage_offset(&self) -> Offset16 {
1312        self.data.header().coverage_offset.get()
1313    }
1314
1315    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1316    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1317        let data = self.offset_data();
1318        self.coverage_offset().resolve(data)
1319    }
1320
1321    /// Defines the types of data in the ValueRecord.
1322    pub fn value_format(&self) -> ValueFormat {
1323        self.data.header().value_format.get()
1324    }
1325
1326    /// Defines positioning value(s) — applied to all glyphs in the
1327    /// Coverage table.
1328    pub fn value_record(&self) -> ValueRecord<'a> {
1329        let range = self.value_record_byte_range();
1330        ValueRecord::read_at(self.offset_data(), range.start, self.value_format())
1331            .unwrap_or_default()
1332    }
1333
1334    pub fn pos_format_byte_range(&self) -> Range<usize> {
1335        let start = 0;
1336        let end = start + u16::RAW_BYTE_LEN;
1337        start..end
1338    }
1339
1340    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1341        let start = self.pos_format_byte_range().end;
1342        let end = start + Offset16::RAW_BYTE_LEN;
1343        start..end
1344    }
1345
1346    pub fn value_format_byte_range(&self) -> Range<usize> {
1347        let start = self.coverage_offset_byte_range().end;
1348        let end = start + ValueFormat::RAW_BYTE_LEN;
1349        start..end
1350    }
1351
1352    pub fn value_record_byte_range(&self) -> Range<usize> {
1353        let start = self.value_format_byte_range().end;
1354        let end =
1355            start + <ValueRecord as ComputeSize>::compute_size(self.value_format()).unwrap_or(0);
1356        start..end
1357    }
1358}
1359
1360const _: () = assert!(FontData::default_data_long_enough(
1361    SinglePosFormat1::MIN_SIZE
1362));
1363
1364impl Default for SinglePosFormat1<'_> {
1365    fn default() -> Self {
1366        Self {
1367            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1368                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1369        }
1370    }
1371}
1372
1373impl Format<u16> for SinglePosFormat2<'_> {
1374    const FORMAT: u16 = 2;
1375}
1376
1377impl<'a> MinByteRange<'a> for SinglePosFormat2<'a> {
1378    fn min_byte_range(&self) -> Range<usize> {
1379        0..self.value_records_byte_range().end
1380    }
1381    fn min_table_bytes(&self) -> &'a [u8] {
1382        let range = self.min_byte_range();
1383        self.offset_data().as_bytes().get(range).unwrap_or_default()
1384    }
1385}
1386
1387impl ReadArgs for SinglePosFormat2<'_> {
1388    type Args = ();
1389}
1390
1391impl<'a> FontRead<'a> for SinglePosFormat2<'a> {
1392    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1393        #[allow(clippy::absurd_extreme_comparisons)]
1394        if data.len() < Self::MIN_SIZE {
1395            return Err(ReadError::OutOfBounds);
1396        }
1397        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1398            return Err(ReadError::OutOfBounds);
1399        };
1400        Ok(Self { data })
1401    }
1402}
1403
1404/// The fields at the start of the table, which are read directly.
1405#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1406#[repr(C, packed)]
1407#[doc(hidden)]
1408pub struct SinglePosFormat2FixedHeader {
1409    pub pos_format: BigEndian<u16>,
1410    pub coverage_offset: BigEndian<Offset16>,
1411    pub value_format: BigEndian<ValueFormat>,
1412    pub value_count: BigEndian<u16>,
1413}
1414
1415#[allow(clippy::absurd_extreme_comparisons)]
1416const _: () =
1417    assert!(std::mem::size_of::<SinglePosFormat2FixedHeader>() <= SinglePosFormat2::MIN_SIZE);
1418
1419/// [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
1420#[derive(Clone)]
1421pub struct SinglePosFormat2<'a> {
1422    data: TableDataRef<'a, SinglePosFormat2FixedHeader>,
1423}
1424
1425#[allow(clippy::needless_lifetimes)]
1426impl<'a> SinglePosFormat2<'a> {
1427    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1428        + Offset16::RAW_BYTE_LEN
1429        + ValueFormat::RAW_BYTE_LEN
1430        + u16::RAW_BYTE_LEN);
1431    basic_table_impls!(impl_the_methods);
1432
1433    /// Format identifier: format = 2
1434    pub fn pos_format(&self) -> u16 {
1435        self.data.header().pos_format.get()
1436    }
1437
1438    /// Offset to Coverage table, from beginning of SinglePos subtable.
1439    pub fn coverage_offset(&self) -> Offset16 {
1440        self.data.header().coverage_offset.get()
1441    }
1442
1443    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1444    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1445        let data = self.offset_data();
1446        self.coverage_offset().resolve(data)
1447    }
1448
1449    /// Defines the types of data in the ValueRecords.
1450    pub fn value_format(&self) -> ValueFormat {
1451        self.data.header().value_format.get()
1452    }
1453
1454    /// Number of ValueRecords — must equal glyphCount in the
1455    /// Coverage table.
1456    pub fn value_count(&self) -> u16 {
1457        self.data.header().value_count.get()
1458    }
1459
1460    /// Array of ValueRecords — positioning values applied to glyphs.
1461    pub fn value_records(&self) -> ComputedArray<'a, ValueRecord<'a>> {
1462        let range = self.value_records_byte_range();
1463        ComputedArray::new(self.offset_data(), range, self.value_format()).unwrap_or_default()
1464    }
1465
1466    pub fn pos_format_byte_range(&self) -> Range<usize> {
1467        let start = 0;
1468        let end = start + u16::RAW_BYTE_LEN;
1469        start..end
1470    }
1471
1472    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1473        let start = self.pos_format_byte_range().end;
1474        let end = start + Offset16::RAW_BYTE_LEN;
1475        start..end
1476    }
1477
1478    pub fn value_format_byte_range(&self) -> Range<usize> {
1479        let start = self.coverage_offset_byte_range().end;
1480        let end = start + ValueFormat::RAW_BYTE_LEN;
1481        start..end
1482    }
1483
1484    pub fn value_count_byte_range(&self) -> Range<usize> {
1485        let start = self.value_format_byte_range().end;
1486        let end = start + u16::RAW_BYTE_LEN;
1487        start..end
1488    }
1489
1490    pub fn value_records_byte_range(&self) -> Range<usize> {
1491        let value_count = self.value_count();
1492        let start = self.value_count_byte_range().end;
1493        let end = start
1494            + (transforms::to_usize(value_count)).saturating_mul(
1495                <ValueRecord as ComputeSize>::compute_size(self.value_format()).unwrap_or(0),
1496            );
1497        start..end
1498    }
1499}
1500
1501/// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable
1502#[derive(Clone)]
1503pub enum PairPos<'a> {
1504    Format1(PairPosFormat1<'a>),
1505    Format2(PairPosFormat2<'a>),
1506    Format3(PairPosFormat3<'a>),
1507    Format4(PairPosFormat4<'a>),
1508}
1509
1510impl Default for PairPos<'_> {
1511    fn default() -> Self {
1512        Self::Format1(Default::default())
1513    }
1514}
1515
1516impl<'a> PairPos<'a> {
1517    ///Return the `FontData` used to resolve offsets for this table.
1518    pub fn offset_data(&self) -> FontData<'a> {
1519        match self {
1520            Self::Format1(item) => item.offset_data(),
1521            Self::Format2(item) => item.offset_data(),
1522            Self::Format3(item) => item.offset_data(),
1523            Self::Format4(item) => item.offset_data(),
1524        }
1525    }
1526
1527    /// Format identifier: format = 1
1528    pub fn pos_format(&self) -> u16 {
1529        match self {
1530            Self::Format1(item) => item.pos_format(),
1531            Self::Format2(item) => item.pos_format(),
1532            Self::Format3(item) => item.pos_format(),
1533            Self::Format4(item) => item.pos_format(),
1534        }
1535    }
1536
1537    /// Defines the types of data in valueRecord1 — for the first
1538    /// glyph in the pair (may be zero).
1539    pub fn value_format1(&self) -> ValueFormat {
1540        match self {
1541            Self::Format1(item) => item.value_format1(),
1542            Self::Format2(item) => item.value_format1(),
1543            Self::Format3(item) => item.value_format1(),
1544            Self::Format4(item) => item.value_format1(),
1545        }
1546    }
1547
1548    /// Defines the types of data in valueRecord2 — for the second
1549    /// glyph in the pair (may be zero).
1550    pub fn value_format2(&self) -> ValueFormat {
1551        match self {
1552            Self::Format1(item) => item.value_format2(),
1553            Self::Format2(item) => item.value_format2(),
1554            Self::Format3(item) => item.value_format2(),
1555            Self::Format4(item) => item.value_format2(),
1556        }
1557    }
1558}
1559
1560impl ReadArgs for PairPos<'_> {
1561    type Args = ();
1562}
1563
1564impl<'a> FontRead<'a> for PairPos<'a> {
1565    #[inline]
1566    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1567        let format: u16 = data.read_at(0usize)?;
1568        match format {
1569            PairPosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
1570            PairPosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
1571            PairPosFormat3::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
1572            PairPosFormat4::FORMAT => Ok(Self::Format4(FontRead::read(data)?)),
1573            other => Err(ReadError::InvalidFormat(other.into())),
1574        }
1575    }
1576}
1577
1578impl<'a> MinByteRange<'a> for PairPos<'a> {
1579    fn min_byte_range(&self) -> Range<usize> {
1580        match self {
1581            Self::Format1(item) => item.min_byte_range(),
1582            Self::Format2(item) => item.min_byte_range(),
1583            Self::Format3(item) => item.min_byte_range(),
1584            Self::Format4(item) => item.min_byte_range(),
1585        }
1586    }
1587    fn min_table_bytes(&self) -> &'a [u8] {
1588        match self {
1589            Self::Format1(item) => item.min_table_bytes(),
1590            Self::Format2(item) => item.min_table_bytes(),
1591            Self::Format3(item) => item.min_table_bytes(),
1592            Self::Format4(item) => item.min_table_bytes(),
1593        }
1594    }
1595}
1596
1597impl Format<u16> for PairPosFormat1<'_> {
1598    const FORMAT: u16 = 1;
1599}
1600
1601impl<'a> MinByteRange<'a> for PairPosFormat1<'a> {
1602    fn min_byte_range(&self) -> Range<usize> {
1603        0..self.pair_set_offsets_byte_range().end
1604    }
1605    fn min_table_bytes(&self) -> &'a [u8] {
1606        let range = self.min_byte_range();
1607        self.offset_data().as_bytes().get(range).unwrap_or_default()
1608    }
1609}
1610
1611impl ReadArgs for PairPosFormat1<'_> {
1612    type Args = ();
1613}
1614
1615impl<'a> FontRead<'a> for PairPosFormat1<'a> {
1616    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1617        #[allow(clippy::absurd_extreme_comparisons)]
1618        if data.len() < Self::MIN_SIZE {
1619            return Err(ReadError::OutOfBounds);
1620        }
1621        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1622            return Err(ReadError::OutOfBounds);
1623        };
1624        Ok(Self { data })
1625    }
1626}
1627
1628/// The fields at the start of the table, which are read directly.
1629#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1630#[repr(C, packed)]
1631#[doc(hidden)]
1632pub struct PairPosFormat1FixedHeader {
1633    pub pos_format: BigEndian<u16>,
1634    pub coverage_offset: BigEndian<Offset16>,
1635    pub value_format1: BigEndian<ValueFormat>,
1636    pub value_format2: BigEndian<ValueFormat>,
1637    pub pair_set_count: BigEndian<u16>,
1638}
1639
1640#[allow(clippy::absurd_extreme_comparisons)]
1641const _: () = assert!(std::mem::size_of::<PairPosFormat1FixedHeader>() <= PairPosFormat1::MIN_SIZE);
1642
1643/// [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
1644#[derive(Clone)]
1645pub struct PairPosFormat1<'a> {
1646    data: TableDataRef<'a, PairPosFormat1FixedHeader>,
1647}
1648
1649#[allow(clippy::needless_lifetimes)]
1650impl<'a> PairPosFormat1<'a> {
1651    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
1652        + Offset16::RAW_BYTE_LEN
1653        + ValueFormat::RAW_BYTE_LEN
1654        + ValueFormat::RAW_BYTE_LEN
1655        + u16::RAW_BYTE_LEN);
1656    basic_table_impls!(impl_the_methods);
1657
1658    /// Format identifier: format = 1
1659    pub fn pos_format(&self) -> u16 {
1660        self.data.header().pos_format.get()
1661    }
1662
1663    /// Offset to Coverage table, from beginning of PairPos subtable.
1664    pub fn coverage_offset(&self) -> Offset16 {
1665        self.data.header().coverage_offset.get()
1666    }
1667
1668    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
1669    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
1670        let data = self.offset_data();
1671        self.coverage_offset().resolve(data)
1672    }
1673
1674    /// Defines the types of data in valueRecord1 — for the first
1675    /// glyph in the pair (may be zero).
1676    pub fn value_format1(&self) -> ValueFormat {
1677        self.data.header().value_format1.get()
1678    }
1679
1680    /// Defines the types of data in valueRecord2 — for the second
1681    /// glyph in the pair (may be zero).
1682    pub fn value_format2(&self) -> ValueFormat {
1683        self.data.header().value_format2.get()
1684    }
1685
1686    /// Number of PairSet tables
1687    pub fn pair_set_count(&self) -> u16 {
1688        self.data.header().pair_set_count.get()
1689    }
1690
1691    /// Array of offsets to PairSet tables. Offsets are from beginning
1692    /// of PairPos subtable, ordered by Coverage Index.
1693    pub fn pair_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
1694        let range = self.pair_set_offsets_byte_range();
1695        self.offset_data()
1696            .read_array(range)
1697            .ok()
1698            .unwrap_or_default()
1699    }
1700
1701    /// A dynamically resolving wrapper for [`pair_set_offsets`][Self::pair_set_offsets].
1702    pub fn pair_sets(&self) -> ArrayOfOffsets<'a, PairSet<'a>, Offset16> {
1703        let data = self.offset_data();
1704        let offsets = self.pair_set_offsets();
1705        let args = (self.value_format1(), self.value_format2());
1706        ArrayOfOffsets::new(offsets, data, args)
1707    }
1708
1709    pub fn pos_format_byte_range(&self) -> Range<usize> {
1710        let start = 0;
1711        let end = start + u16::RAW_BYTE_LEN;
1712        start..end
1713    }
1714
1715    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
1716        let start = self.pos_format_byte_range().end;
1717        let end = start + Offset16::RAW_BYTE_LEN;
1718        start..end
1719    }
1720
1721    pub fn value_format1_byte_range(&self) -> Range<usize> {
1722        let start = self.coverage_offset_byte_range().end;
1723        let end = start + ValueFormat::RAW_BYTE_LEN;
1724        start..end
1725    }
1726
1727    pub fn value_format2_byte_range(&self) -> Range<usize> {
1728        let start = self.value_format1_byte_range().end;
1729        let end = start + ValueFormat::RAW_BYTE_LEN;
1730        start..end
1731    }
1732
1733    pub fn pair_set_count_byte_range(&self) -> Range<usize> {
1734        let start = self.value_format2_byte_range().end;
1735        let end = start + u16::RAW_BYTE_LEN;
1736        start..end
1737    }
1738
1739    pub fn pair_set_offsets_byte_range(&self) -> Range<usize> {
1740        let pair_set_count = self.pair_set_count();
1741        let start = self.pair_set_count_byte_range().end;
1742        let end =
1743            start + (transforms::to_usize(pair_set_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
1744        start..end
1745    }
1746}
1747
1748const _: () = assert!(FontData::default_data_long_enough(PairPosFormat1::MIN_SIZE));
1749
1750impl Default for PairPosFormat1<'_> {
1751    fn default() -> Self {
1752        Self {
1753            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
1754                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1755        }
1756    }
1757}
1758
1759impl<'a> MinByteRange<'a> for PairSet<'a> {
1760    fn min_byte_range(&self) -> Range<usize> {
1761        0..self.pair_value_records_byte_range().end
1762    }
1763    fn min_table_bytes(&self) -> &'a [u8] {
1764        let range = self.min_byte_range();
1765        self.offset_data().as_bytes().get(range).unwrap_or_default()
1766    }
1767}
1768
1769impl ReadArgs for PairSet<'_> {
1770    type Args = (ValueFormat, ValueFormat);
1771}
1772
1773impl<'a> FontRead<'a> for PairSet<'a> {
1774    fn read_with_args(
1775        data: FontData<'a>,
1776        args: (ValueFormat, ValueFormat),
1777    ) -> Result<Self, ReadError> {
1778        let (value_format1, value_format2) = args;
1779
1780        #[allow(clippy::absurd_extreme_comparisons)]
1781        if data.len() < Self::MIN_SIZE {
1782            return Err(ReadError::OutOfBounds);
1783        }
1784        let Some(data) = TableDataRef::new(data.as_bytes()) else {
1785            return Err(ReadError::OutOfBounds);
1786        };
1787        Ok(Self {
1788            data,
1789            value_format1,
1790            value_format2,
1791        })
1792    }
1793}
1794
1795impl<'a> PairSet<'a> {
1796    /// A constructor that requires additional arguments.
1797    ///
1798    /// This type requires some external state in order to be
1799    /// parsed.
1800    pub fn read(
1801        data: FontData<'a>,
1802        value_format1: ValueFormat,
1803        value_format2: ValueFormat,
1804    ) -> Result<Self, ReadError> {
1805        let args = (value_format1, value_format2);
1806        Self::read_with_args(data, args)
1807    }
1808}
1809
1810/// The fields at the start of the table, which are read directly.
1811#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
1812#[repr(C, packed)]
1813#[doc(hidden)]
1814pub struct PairSetFixedHeader {
1815    pub pair_value_count: BigEndian<u16>,
1816}
1817
1818#[allow(clippy::absurd_extreme_comparisons)]
1819const _: () = assert!(std::mem::size_of::<PairSetFixedHeader>() <= PairSet::MIN_SIZE);
1820
1821/// Part of [PairPosFormat1]
1822#[derive(Clone)]
1823pub struct PairSet<'a> {
1824    data: TableDataRef<'a, PairSetFixedHeader>,
1825    value_format1: ValueFormat,
1826    value_format2: ValueFormat,
1827}
1828
1829#[allow(clippy::needless_lifetimes)]
1830impl<'a> PairSet<'a> {
1831    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
1832    basic_table_impls!(impl_the_methods);
1833
1834    /// Number of PairValueRecords
1835    pub fn pair_value_count(&self) -> u16 {
1836        self.data.header().pair_value_count.get()
1837    }
1838
1839    /// Array of PairValueRecords, ordered by glyph ID of the second
1840    /// glyph.
1841    pub fn pair_value_records(&self) -> ComputedArray<'a, PairValueRecord<'a>> {
1842        let range = self.pair_value_records_byte_range();
1843        ComputedArray::new(
1844            self.offset_data(),
1845            range,
1846            (self.value_format1(), self.value_format2()),
1847        )
1848        .unwrap_or_default()
1849    }
1850
1851    pub(crate) fn value_format1(&self) -> ValueFormat {
1852        self.value_format1
1853    }
1854
1855    pub(crate) fn value_format2(&self) -> ValueFormat {
1856        self.value_format2
1857    }
1858
1859    pub fn pair_value_count_byte_range(&self) -> Range<usize> {
1860        let start = 0;
1861        let end = start + u16::RAW_BYTE_LEN;
1862        start..end
1863    }
1864
1865    pub fn pair_value_records_byte_range(&self) -> Range<usize> {
1866        let pair_value_count = self.pair_value_count();
1867        let start = self.pair_value_count_byte_range().end;
1868        let end = start
1869            + (transforms::to_usize(pair_value_count)).saturating_mul(
1870                <PairValueRecord as ComputeSize>::compute_size((
1871                    self.value_format1(),
1872                    self.value_format2(),
1873                ))
1874                .unwrap_or(0),
1875            );
1876        start..end
1877    }
1878}
1879
1880const _: () = assert!(FontData::default_data_long_enough(PairSet::MIN_SIZE));
1881
1882impl Default for PairSet<'_> {
1883    fn default() -> Self {
1884        Self {
1885            data: TableDataRef::new(FontData::default_table_data().as_bytes())
1886                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
1887            value_format1: Default::default(),
1888            value_format2: Default::default(),
1889        }
1890    }
1891}
1892
1893/// Part of [PairSet]
1894#[derive(Clone, Debug)]
1895pub struct PairValueRecord<'a> {
1896    /// Glyph ID of second glyph in the pair (first glyph is listed in
1897    /// the Coverage table).
1898    pub second_glyph: BigEndian<GlyphId16>,
1899    /// Positioning data for the first glyph in the pair.
1900    pub value_record1: ValueRecord<'a>,
1901    /// Positioning data for the second glyph in the pair.
1902    pub value_record2: ValueRecord<'a>,
1903}
1904
1905impl<'a> PairValueRecord<'a> {
1906    /// Glyph ID of second glyph in the pair (first glyph is listed in
1907    /// the Coverage table).
1908    pub fn second_glyph(&self) -> GlyphId16 {
1909        self.second_glyph.get()
1910    }
1911
1912    /// Positioning data for the first glyph in the pair.
1913    pub fn value_record1(&self) -> &ValueRecord<'a> {
1914        &self.value_record1
1915    }
1916
1917    /// Positioning data for the second glyph in the pair.
1918    pub fn value_record2(&self) -> &ValueRecord<'a> {
1919        &self.value_record2
1920    }
1921}
1922
1923impl ReadArgs for PairValueRecord<'_> {
1924    type Args = (ValueFormat, ValueFormat);
1925}
1926
1927impl ComputeSize for PairValueRecord<'_> {
1928    #[allow(clippy::needless_question_mark)]
1929    fn compute_size(args: (ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
1930        let (value_format1, value_format2) = args;
1931        let mut result = 0usize;
1932        result = result
1933            .checked_add(GlyphId16::RAW_BYTE_LEN)
1934            .ok_or(ReadError::OutOfBounds)?;
1935        result = result
1936            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format1).unwrap_or(0))
1937            .ok_or(ReadError::OutOfBounds)?;
1938        result = result
1939            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format2).unwrap_or(0))
1940            .ok_or(ReadError::OutOfBounds)?;
1941        Ok(result)
1942    }
1943}
1944
1945impl<'a> FontReadAt<'a> for PairValueRecord<'a> {
1946    fn read_at(
1947        data: FontData<'a>,
1948        offset: usize,
1949        args: (ValueFormat, ValueFormat),
1950    ) -> Result<Self, ReadError> {
1951        let mut cursor = data.cursor();
1952        cursor.advance_by(offset);
1953        let (value_format1, value_format2) = args;
1954        Ok(Self {
1955            second_glyph: cursor.read_be()?,
1956            value_record1: cursor.read_at_with_args(value_format1)?,
1957            value_record2: cursor.read_at_with_args(value_format2)?,
1958        })
1959    }
1960}
1961
1962#[allow(clippy::needless_lifetimes)]
1963impl<'a> PairValueRecord<'a> {
1964    /// A constructor that requires additional arguments.
1965    ///
1966    /// This type requires some external state in order to be
1967    /// parsed.
1968    pub fn read(
1969        data: FontData<'a>,
1970        offset: usize,
1971        value_format1: ValueFormat,
1972        value_format2: ValueFormat,
1973    ) -> Result<Self, ReadError> {
1974        let args = (value_format1, value_format2);
1975        Self::read_at(data, offset, args)
1976    }
1977}
1978
1979impl Format<u16> for PairPosFormat2<'_> {
1980    const FORMAT: u16 = 2;
1981}
1982
1983impl<'a> MinByteRange<'a> for PairPosFormat2<'a> {
1984    fn min_byte_range(&self) -> Range<usize> {
1985        0..self.class1_records_byte_range().end
1986    }
1987    fn min_table_bytes(&self) -> &'a [u8] {
1988        let range = self.min_byte_range();
1989        self.offset_data().as_bytes().get(range).unwrap_or_default()
1990    }
1991}
1992
1993impl ReadArgs for PairPosFormat2<'_> {
1994    type Args = ();
1995}
1996
1997impl<'a> FontRead<'a> for PairPosFormat2<'a> {
1998    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
1999        #[allow(clippy::absurd_extreme_comparisons)]
2000        if data.len() < Self::MIN_SIZE {
2001            return Err(ReadError::OutOfBounds);
2002        }
2003        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2004            return Err(ReadError::OutOfBounds);
2005        };
2006        Ok(Self { data })
2007    }
2008}
2009
2010/// The fields at the start of the table, which are read directly.
2011#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2012#[repr(C, packed)]
2013#[doc(hidden)]
2014pub struct PairPosFormat2FixedHeader {
2015    pub pos_format: BigEndian<u16>,
2016    pub coverage_offset: BigEndian<Offset16>,
2017    pub value_format1: BigEndian<ValueFormat>,
2018    pub value_format2: BigEndian<ValueFormat>,
2019    pub class_def1_offset: BigEndian<Offset16>,
2020    pub class_def2_offset: BigEndian<Offset16>,
2021    pub class1_count: BigEndian<u16>,
2022    pub class2_count: BigEndian<u16>,
2023}
2024
2025#[allow(clippy::absurd_extreme_comparisons)]
2026const _: () = assert!(std::mem::size_of::<PairPosFormat2FixedHeader>() <= PairPosFormat2::MIN_SIZE);
2027
2028/// [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
2029#[derive(Clone)]
2030pub struct PairPosFormat2<'a> {
2031    data: TableDataRef<'a, PairPosFormat2FixedHeader>,
2032}
2033
2034#[allow(clippy::needless_lifetimes)]
2035impl<'a> PairPosFormat2<'a> {
2036    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2037        + Offset16::RAW_BYTE_LEN
2038        + ValueFormat::RAW_BYTE_LEN
2039        + ValueFormat::RAW_BYTE_LEN
2040        + Offset16::RAW_BYTE_LEN
2041        + Offset16::RAW_BYTE_LEN
2042        + u16::RAW_BYTE_LEN
2043        + u16::RAW_BYTE_LEN);
2044    basic_table_impls!(impl_the_methods);
2045
2046    /// Format identifier: format = 2
2047    pub fn pos_format(&self) -> u16 {
2048        self.data.header().pos_format.get()
2049    }
2050
2051    /// Offset to Coverage table, from beginning of PairPos subtable.
2052    pub fn coverage_offset(&self) -> Offset16 {
2053        self.data.header().coverage_offset.get()
2054    }
2055
2056    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2057    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2058        let data = self.offset_data();
2059        self.coverage_offset().resolve(data)
2060    }
2061
2062    /// ValueRecord definition — for the first glyph of the pair (may
2063    /// be zero).
2064    pub fn value_format1(&self) -> ValueFormat {
2065        self.data.header().value_format1.get()
2066    }
2067
2068    /// ValueRecord definition — for the second glyph of the pair
2069    /// (may be zero).
2070    pub fn value_format2(&self) -> ValueFormat {
2071        self.data.header().value_format2.get()
2072    }
2073
2074    /// Offset to ClassDef table, from beginning of PairPos subtable
2075    /// — for the first glyph of the pair.
2076    pub fn class_def1_offset(&self) -> Offset16 {
2077        self.data.header().class_def1_offset.get()
2078    }
2079
2080    /// Attempt to resolve [`class_def1_offset`][Self::class_def1_offset].
2081    pub fn class_def1(&self) -> Result<ClassDef<'a>, ReadError> {
2082        let data = self.offset_data();
2083        self.class_def1_offset().resolve(data)
2084    }
2085
2086    /// Offset to ClassDef table, from beginning of PairPos subtable
2087    /// — for the second glyph of the pair.
2088    pub fn class_def2_offset(&self) -> Offset16 {
2089        self.data.header().class_def2_offset.get()
2090    }
2091
2092    /// Attempt to resolve [`class_def2_offset`][Self::class_def2_offset].
2093    pub fn class_def2(&self) -> Result<ClassDef<'a>, ReadError> {
2094        let data = self.offset_data();
2095        self.class_def2_offset().resolve(data)
2096    }
2097
2098    /// Number of classes in classDef1 table — includes Class 0.
2099    pub fn class1_count(&self) -> u16 {
2100        self.data.header().class1_count.get()
2101    }
2102
2103    /// Number of classes in classDef2 table — includes Class 0.
2104    pub fn class2_count(&self) -> u16 {
2105        self.data.header().class2_count.get()
2106    }
2107
2108    /// Array of Class1 records, ordered by classes in classDef1.
2109    pub fn class1_records(&self) -> ComputedArray<'a, Class1Record<'a>> {
2110        let range = self.class1_records_byte_range();
2111        ComputedArray::new(
2112            self.offset_data(),
2113            range,
2114            (
2115                self.class2_count(),
2116                self.value_format1(),
2117                self.value_format2(),
2118            ),
2119        )
2120        .unwrap_or_default()
2121    }
2122
2123    pub fn pos_format_byte_range(&self) -> Range<usize> {
2124        let start = 0;
2125        let end = start + u16::RAW_BYTE_LEN;
2126        start..end
2127    }
2128
2129    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2130        let start = self.pos_format_byte_range().end;
2131        let end = start + Offset16::RAW_BYTE_LEN;
2132        start..end
2133    }
2134
2135    pub fn value_format1_byte_range(&self) -> Range<usize> {
2136        let start = self.coverage_offset_byte_range().end;
2137        let end = start + ValueFormat::RAW_BYTE_LEN;
2138        start..end
2139    }
2140
2141    pub fn value_format2_byte_range(&self) -> Range<usize> {
2142        let start = self.value_format1_byte_range().end;
2143        let end = start + ValueFormat::RAW_BYTE_LEN;
2144        start..end
2145    }
2146
2147    pub fn class_def1_offset_byte_range(&self) -> Range<usize> {
2148        let start = self.value_format2_byte_range().end;
2149        let end = start + Offset16::RAW_BYTE_LEN;
2150        start..end
2151    }
2152
2153    pub fn class_def2_offset_byte_range(&self) -> Range<usize> {
2154        let start = self.class_def1_offset_byte_range().end;
2155        let end = start + Offset16::RAW_BYTE_LEN;
2156        start..end
2157    }
2158
2159    pub fn class1_count_byte_range(&self) -> Range<usize> {
2160        let start = self.class_def2_offset_byte_range().end;
2161        let end = start + u16::RAW_BYTE_LEN;
2162        start..end
2163    }
2164
2165    pub fn class2_count_byte_range(&self) -> Range<usize> {
2166        let start = self.class1_count_byte_range().end;
2167        let end = start + u16::RAW_BYTE_LEN;
2168        start..end
2169    }
2170
2171    pub fn class1_records_byte_range(&self) -> Range<usize> {
2172        let class1_count = self.class1_count();
2173        let start = self.class2_count_byte_range().end;
2174        let end = start
2175            + (transforms::to_usize(class1_count)).saturating_mul(
2176                <Class1Record as ComputeSize>::compute_size((
2177                    self.class2_count(),
2178                    self.value_format1(),
2179                    self.value_format2(),
2180                ))
2181                .unwrap_or(0),
2182            );
2183        start..end
2184    }
2185}
2186
2187/// Part of [PairPosFormat2]
2188#[derive(Clone, Debug)]
2189pub struct Class1Record<'a> {
2190    /// Array of Class2 records, ordered by classes in classDef2.
2191    pub class2_records: ComputedArray<'a, Class2Record<'a>>,
2192}
2193
2194impl<'a> Class1Record<'a> {
2195    /// Array of Class2 records, ordered by classes in classDef2.
2196    pub fn class2_records(&self) -> &ComputedArray<'a, Class2Record<'a>> {
2197        &self.class2_records
2198    }
2199}
2200
2201impl ReadArgs for Class1Record<'_> {
2202    type Args = (u16, ValueFormat, ValueFormat);
2203}
2204
2205impl ComputeSize for Class1Record<'_> {
2206    #[allow(clippy::needless_question_mark)]
2207    fn compute_size(args: (u16, ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
2208        let (class2_count, value_format1, value_format2) = args;
2209        Ok((transforms::to_usize(class2_count)).saturating_mul(
2210            <Class2Record as ComputeSize>::compute_size((value_format1, value_format2))
2211                .unwrap_or(0),
2212        ))
2213    }
2214}
2215
2216impl<'a> FontReadAt<'a> for Class1Record<'a> {
2217    fn read_at(
2218        data: FontData<'a>,
2219        offset: usize,
2220        args: (u16, ValueFormat, ValueFormat),
2221    ) -> Result<Self, ReadError> {
2222        let mut cursor = data.cursor();
2223        cursor.advance_by(offset);
2224        let (class2_count, value_format1, value_format2) = args;
2225        Ok(Self {
2226            class2_records: cursor.read_computed_array(
2227                transforms::to_usize(class2_count),
2228                (value_format1, value_format2),
2229            )?,
2230        })
2231    }
2232}
2233
2234#[allow(clippy::needless_lifetimes)]
2235impl<'a> Class1Record<'a> {
2236    /// A constructor that requires additional arguments.
2237    ///
2238    /// This type requires some external state in order to be
2239    /// parsed.
2240    pub fn read(
2241        data: FontData<'a>,
2242        offset: usize,
2243        class2_count: u16,
2244        value_format1: ValueFormat,
2245        value_format2: ValueFormat,
2246    ) -> Result<Self, ReadError> {
2247        let args = (class2_count, value_format1, value_format2);
2248        Self::read_at(data, offset, args)
2249    }
2250}
2251
2252/// Part of [PairPosFormat2]
2253#[derive(Clone, Debug)]
2254pub struct Class2Record<'a> {
2255    /// Positioning for first glyph — empty if valueFormat1 = 0.
2256    pub value_record1: ValueRecord<'a>,
2257    /// Positioning for second glyph — empty if valueFormat2 = 0.
2258    pub value_record2: ValueRecord<'a>,
2259}
2260
2261impl<'a> Class2Record<'a> {
2262    /// Positioning for first glyph — empty if valueFormat1 = 0.
2263    pub fn value_record1(&self) -> &ValueRecord<'a> {
2264        &self.value_record1
2265    }
2266
2267    /// Positioning for second glyph — empty if valueFormat2 = 0.
2268    pub fn value_record2(&self) -> &ValueRecord<'a> {
2269        &self.value_record2
2270    }
2271}
2272
2273impl ReadArgs for Class2Record<'_> {
2274    type Args = (ValueFormat, ValueFormat);
2275}
2276
2277impl ComputeSize for Class2Record<'_> {
2278    #[allow(clippy::needless_question_mark)]
2279    fn compute_size(args: (ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
2280        let (value_format1, value_format2) = args;
2281        let mut result = 0usize;
2282        result = result
2283            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format1).unwrap_or(0))
2284            .ok_or(ReadError::OutOfBounds)?;
2285        result = result
2286            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format2).unwrap_or(0))
2287            .ok_or(ReadError::OutOfBounds)?;
2288        Ok(result)
2289    }
2290}
2291
2292impl<'a> FontReadAt<'a> for Class2Record<'a> {
2293    fn read_at(
2294        data: FontData<'a>,
2295        offset: usize,
2296        args: (ValueFormat, ValueFormat),
2297    ) -> Result<Self, ReadError> {
2298        let mut cursor = data.cursor();
2299        cursor.advance_by(offset);
2300        let (value_format1, value_format2) = args;
2301        Ok(Self {
2302            value_record1: cursor.read_at_with_args(value_format1)?,
2303            value_record2: cursor.read_at_with_args(value_format2)?,
2304        })
2305    }
2306}
2307
2308#[allow(clippy::needless_lifetimes)]
2309impl<'a> Class2Record<'a> {
2310    /// A constructor that requires additional arguments.
2311    ///
2312    /// This type requires some external state in order to be
2313    /// parsed.
2314    pub fn read(
2315        data: FontData<'a>,
2316        offset: usize,
2317        value_format1: ValueFormat,
2318        value_format2: ValueFormat,
2319    ) -> Result<Self, ReadError> {
2320        let args = (value_format1, value_format2);
2321        Self::read_at(data, offset, args)
2322    }
2323}
2324
2325impl Format<u16> for CursivePosFormat1<'_> {
2326    const FORMAT: u16 = 1;
2327}
2328
2329impl<'a> MinByteRange<'a> for CursivePosFormat1<'a> {
2330    fn min_byte_range(&self) -> Range<usize> {
2331        0..self.entry_exit_record_byte_range().end
2332    }
2333    fn min_table_bytes(&self) -> &'a [u8] {
2334        let range = self.min_byte_range();
2335        self.offset_data().as_bytes().get(range).unwrap_or_default()
2336    }
2337}
2338
2339impl ReadArgs for CursivePosFormat1<'_> {
2340    type Args = ();
2341}
2342
2343impl<'a> FontRead<'a> for CursivePosFormat1<'a> {
2344    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2345        #[allow(clippy::absurd_extreme_comparisons)]
2346        if data.len() < Self::MIN_SIZE {
2347            return Err(ReadError::OutOfBounds);
2348        }
2349        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2350            return Err(ReadError::OutOfBounds);
2351        };
2352        Ok(Self { data })
2353    }
2354}
2355
2356/// The fields at the start of the table, which are read directly.
2357#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2358#[repr(C, packed)]
2359#[doc(hidden)]
2360pub struct CursivePosFormat1FixedHeader {
2361    pub pos_format: BigEndian<u16>,
2362    pub coverage_offset: BigEndian<Offset16>,
2363    pub entry_exit_count: BigEndian<u16>,
2364}
2365
2366#[allow(clippy::absurd_extreme_comparisons)]
2367const _: () =
2368    assert!(std::mem::size_of::<CursivePosFormat1FixedHeader>() <= CursivePosFormat1::MIN_SIZE);
2369
2370/// [Cursive Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#cursive-attachment-positioning-format1-cursive-attachment): Cursvie attachment
2371#[derive(Clone)]
2372pub struct CursivePosFormat1<'a> {
2373    data: TableDataRef<'a, CursivePosFormat1FixedHeader>,
2374}
2375
2376#[allow(clippy::needless_lifetimes)]
2377impl<'a> CursivePosFormat1<'a> {
2378    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2379    basic_table_impls!(impl_the_methods);
2380
2381    /// Format identifier: format = 1
2382    pub fn pos_format(&self) -> u16 {
2383        self.data.header().pos_format.get()
2384    }
2385
2386    /// Offset to Coverage table, from beginning of CursivePos subtable.
2387    pub fn coverage_offset(&self) -> Offset16 {
2388        self.data.header().coverage_offset.get()
2389    }
2390
2391    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2392    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2393        let data = self.offset_data();
2394        self.coverage_offset().resolve(data)
2395    }
2396
2397    /// Number of EntryExit records
2398    pub fn entry_exit_count(&self) -> u16 {
2399        self.data.header().entry_exit_count.get()
2400    }
2401
2402    /// Array of EntryExit records, in Coverage index order.
2403    pub fn entry_exit_record(&self) -> &'a [EntryExitRecord] {
2404        let range = self.entry_exit_record_byte_range();
2405        self.offset_data()
2406            .read_array(range)
2407            .ok()
2408            .unwrap_or_default()
2409    }
2410
2411    pub fn pos_format_byte_range(&self) -> Range<usize> {
2412        let start = 0;
2413        let end = start + u16::RAW_BYTE_LEN;
2414        start..end
2415    }
2416
2417    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2418        let start = self.pos_format_byte_range().end;
2419        let end = start + Offset16::RAW_BYTE_LEN;
2420        start..end
2421    }
2422
2423    pub fn entry_exit_count_byte_range(&self) -> Range<usize> {
2424        let start = self.coverage_offset_byte_range().end;
2425        let end = start + u16::RAW_BYTE_LEN;
2426        start..end
2427    }
2428
2429    pub fn entry_exit_record_byte_range(&self) -> Range<usize> {
2430        let entry_exit_count = self.entry_exit_count();
2431        let start = self.entry_exit_count_byte_range().end;
2432        let end = start
2433            + (transforms::to_usize(entry_exit_count))
2434                .saturating_mul(EntryExitRecord::RAW_BYTE_LEN);
2435        start..end
2436    }
2437}
2438
2439const _: () = assert!(FontData::default_data_long_enough(
2440    CursivePosFormat1::MIN_SIZE
2441));
2442
2443impl Default for CursivePosFormat1<'_> {
2444    fn default() -> Self {
2445        Self {
2446            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
2447                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2448        }
2449    }
2450}
2451
2452/// Part of [CursivePosFormat1]
2453#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
2454#[repr(C)]
2455#[repr(packed)]
2456pub struct EntryExitRecord {
2457    /// Offset to entryAnchor table, from beginning of CursivePos
2458    /// subtable (may be NULL).
2459    pub entry_anchor_offset: BigEndian<Nullable<Offset16>>,
2460    /// Offset to exitAnchor table, from beginning of CursivePos
2461    /// subtable (may be NULL).
2462    pub exit_anchor_offset: BigEndian<Nullable<Offset16>>,
2463}
2464
2465impl EntryExitRecord {
2466    /// Offset to entryAnchor table, from beginning of CursivePos
2467    /// subtable (may be NULL).
2468    pub fn entry_anchor_offset(&self) -> Nullable<Offset16> {
2469        self.entry_anchor_offset.get()
2470    }
2471
2472    /// Offset to entryAnchor table, from beginning of CursivePos
2473    /// subtable (may be NULL).
2474    ///
2475    /// The `data` argument should be retrieved from the parent table
2476    /// By calling its `offset_data` method.
2477    pub fn entry_anchor<'a>(
2478        &self,
2479        data: FontData<'a>,
2480    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
2481        self.entry_anchor_offset().resolve(data)
2482    }
2483
2484    /// Offset to exitAnchor table, from beginning of CursivePos
2485    /// subtable (may be NULL).
2486    pub fn exit_anchor_offset(&self) -> Nullable<Offset16> {
2487        self.exit_anchor_offset.get()
2488    }
2489
2490    /// Offset to exitAnchor table, from beginning of CursivePos
2491    /// subtable (may be NULL).
2492    ///
2493    /// The `data` argument should be retrieved from the parent table
2494    /// By calling its `offset_data` method.
2495    pub fn exit_anchor<'a>(
2496        &self,
2497        data: FontData<'a>,
2498    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
2499        self.exit_anchor_offset().resolve(data)
2500    }
2501}
2502
2503impl FixedSize for EntryExitRecord {
2504    const RAW_BYTE_LEN: usize = Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
2505}
2506
2507impl Format<u16> for MarkBasePosFormat1<'_> {
2508    const FORMAT: u16 = 1;
2509}
2510
2511impl<'a> MinByteRange<'a> for MarkBasePosFormat1<'a> {
2512    fn min_byte_range(&self) -> Range<usize> {
2513        0..self.base_array_offset_byte_range().end
2514    }
2515    fn min_table_bytes(&self) -> &'a [u8] {
2516        let range = self.min_byte_range();
2517        self.offset_data().as_bytes().get(range).unwrap_or_default()
2518    }
2519}
2520
2521impl ReadArgs for MarkBasePosFormat1<'_> {
2522    type Args = ();
2523}
2524
2525impl<'a> FontRead<'a> for MarkBasePosFormat1<'a> {
2526    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2527        #[allow(clippy::absurd_extreme_comparisons)]
2528        if data.len() < Self::MIN_SIZE {
2529            return Err(ReadError::OutOfBounds);
2530        }
2531        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2532            return Err(ReadError::OutOfBounds);
2533        };
2534        Ok(Self { data })
2535    }
2536}
2537
2538/// The fields at the start of the table, which are read directly.
2539#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2540#[repr(C, packed)]
2541#[doc(hidden)]
2542pub struct MarkBasePosFormat1FixedHeader {
2543    pub pos_format: BigEndian<u16>,
2544    pub mark_coverage_offset: BigEndian<Offset16>,
2545    pub base_coverage_offset: BigEndian<Offset16>,
2546    pub mark_class_count: BigEndian<u16>,
2547    pub mark_array_offset: BigEndian<Offset16>,
2548    pub base_array_offset: BigEndian<Offset16>,
2549}
2550
2551#[allow(clippy::absurd_extreme_comparisons)]
2552const _: () =
2553    assert!(std::mem::size_of::<MarkBasePosFormat1FixedHeader>() <= MarkBasePosFormat1::MIN_SIZE);
2554
2555/// [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
2556#[derive(Clone)]
2557pub struct MarkBasePosFormat1<'a> {
2558    data: TableDataRef<'a, MarkBasePosFormat1FixedHeader>,
2559}
2560
2561#[allow(clippy::needless_lifetimes)]
2562impl<'a> MarkBasePosFormat1<'a> {
2563    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2564        + Offset16::RAW_BYTE_LEN
2565        + Offset16::RAW_BYTE_LEN
2566        + u16::RAW_BYTE_LEN
2567        + Offset16::RAW_BYTE_LEN
2568        + Offset16::RAW_BYTE_LEN);
2569    basic_table_impls!(impl_the_methods);
2570
2571    /// Format identifier: format = 1
2572    pub fn pos_format(&self) -> u16 {
2573        self.data.header().pos_format.get()
2574    }
2575
2576    /// Offset to markCoverage table, from beginning of MarkBasePos
2577    /// subtable.
2578    pub fn mark_coverage_offset(&self) -> Offset16 {
2579        self.data.header().mark_coverage_offset.get()
2580    }
2581
2582    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
2583    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2584        let data = self.offset_data();
2585        self.mark_coverage_offset().resolve(data)
2586    }
2587
2588    /// Offset to baseCoverage table, from beginning of MarkBasePos
2589    /// subtable.
2590    pub fn base_coverage_offset(&self) -> Offset16 {
2591        self.data.header().base_coverage_offset.get()
2592    }
2593
2594    /// Attempt to resolve [`base_coverage_offset`][Self::base_coverage_offset].
2595    pub fn base_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2596        let data = self.offset_data();
2597        self.base_coverage_offset().resolve(data)
2598    }
2599
2600    /// Number of classes defined for marks
2601    pub fn mark_class_count(&self) -> u16 {
2602        self.data.header().mark_class_count.get()
2603    }
2604
2605    /// Offset to MarkArray table, from beginning of MarkBasePos
2606    /// subtable.
2607    pub fn mark_array_offset(&self) -> Offset16 {
2608        self.data.header().mark_array_offset.get()
2609    }
2610
2611    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
2612    pub fn mark_array(&self) -> Result<MarkArray<'a>, ReadError> {
2613        let data = self.offset_data();
2614        self.mark_array_offset().resolve(data)
2615    }
2616
2617    /// Offset to BaseArray table, from beginning of MarkBasePos
2618    /// subtable.
2619    pub fn base_array_offset(&self) -> Offset16 {
2620        self.data.header().base_array_offset.get()
2621    }
2622
2623    /// Attempt to resolve [`base_array_offset`][Self::base_array_offset].
2624    pub fn base_array(&self) -> Result<BaseArray<'a>, ReadError> {
2625        let data = self.offset_data();
2626        let args = self.mark_class_count();
2627        self.base_array_offset().resolve_with_args(data, args)
2628    }
2629
2630    pub fn pos_format_byte_range(&self) -> Range<usize> {
2631        let start = 0;
2632        let end = start + u16::RAW_BYTE_LEN;
2633        start..end
2634    }
2635
2636    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
2637        let start = self.pos_format_byte_range().end;
2638        let end = start + Offset16::RAW_BYTE_LEN;
2639        start..end
2640    }
2641
2642    pub fn base_coverage_offset_byte_range(&self) -> Range<usize> {
2643        let start = self.mark_coverage_offset_byte_range().end;
2644        let end = start + Offset16::RAW_BYTE_LEN;
2645        start..end
2646    }
2647
2648    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
2649        let start = self.base_coverage_offset_byte_range().end;
2650        let end = start + u16::RAW_BYTE_LEN;
2651        start..end
2652    }
2653
2654    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
2655        let start = self.mark_class_count_byte_range().end;
2656        let end = start + Offset16::RAW_BYTE_LEN;
2657        start..end
2658    }
2659
2660    pub fn base_array_offset_byte_range(&self) -> Range<usize> {
2661        let start = self.mark_array_offset_byte_range().end;
2662        let end = start + Offset16::RAW_BYTE_LEN;
2663        start..end
2664    }
2665}
2666
2667const _: () = assert!(FontData::default_data_long_enough(
2668    MarkBasePosFormat1::MIN_SIZE
2669));
2670
2671impl Default for MarkBasePosFormat1<'_> {
2672    fn default() -> Self {
2673        Self {
2674            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
2675                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2676        }
2677    }
2678}
2679
2680impl<'a> MinByteRange<'a> for BaseArray<'a> {
2681    fn min_byte_range(&self) -> Range<usize> {
2682        0..self.base_records_byte_range().end
2683    }
2684    fn min_table_bytes(&self) -> &'a [u8] {
2685        let range = self.min_byte_range();
2686        self.offset_data().as_bytes().get(range).unwrap_or_default()
2687    }
2688}
2689
2690impl ReadArgs for BaseArray<'_> {
2691    type Args = u16;
2692}
2693
2694impl<'a> FontRead<'a> for BaseArray<'a> {
2695    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
2696        let mark_class_count = args;
2697
2698        #[allow(clippy::absurd_extreme_comparisons)]
2699        if data.len() < Self::MIN_SIZE {
2700            return Err(ReadError::OutOfBounds);
2701        }
2702        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2703            return Err(ReadError::OutOfBounds);
2704        };
2705        Ok(Self {
2706            data,
2707            mark_class_count,
2708        })
2709    }
2710}
2711
2712impl<'a> BaseArray<'a> {
2713    /// A constructor that requires additional arguments.
2714    ///
2715    /// This type requires some external state in order to be
2716    /// parsed.
2717    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
2718        let args = mark_class_count;
2719        Self::read_with_args(data, args)
2720    }
2721}
2722
2723/// The fields at the start of the table, which are read directly.
2724#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2725#[repr(C, packed)]
2726#[doc(hidden)]
2727pub struct BaseArrayFixedHeader {
2728    pub base_count: BigEndian<u16>,
2729}
2730
2731#[allow(clippy::absurd_extreme_comparisons)]
2732const _: () = assert!(std::mem::size_of::<BaseArrayFixedHeader>() <= BaseArray::MIN_SIZE);
2733
2734/// Part of [MarkBasePosFormat1]
2735#[derive(Clone)]
2736pub struct BaseArray<'a> {
2737    data: TableDataRef<'a, BaseArrayFixedHeader>,
2738    mark_class_count: u16,
2739}
2740
2741#[allow(clippy::needless_lifetimes)]
2742impl<'a> BaseArray<'a> {
2743    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2744    basic_table_impls!(impl_the_methods);
2745
2746    /// Number of BaseRecords
2747    pub fn base_count(&self) -> u16 {
2748        self.data.header().base_count.get()
2749    }
2750
2751    /// Array of BaseRecords, in order of baseCoverage Index.
2752    pub fn base_records(&self) -> ComputedArray<'a, BaseRecord<'a>> {
2753        let range = self.base_records_byte_range();
2754        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
2755    }
2756
2757    pub(crate) fn mark_class_count(&self) -> u16 {
2758        self.mark_class_count
2759    }
2760
2761    pub fn base_count_byte_range(&self) -> Range<usize> {
2762        let start = 0;
2763        let end = start + u16::RAW_BYTE_LEN;
2764        start..end
2765    }
2766
2767    pub fn base_records_byte_range(&self) -> Range<usize> {
2768        let base_count = self.base_count();
2769        let start = self.base_count_byte_range().end;
2770        let end = start
2771            + (transforms::to_usize(base_count)).saturating_mul(
2772                <BaseRecord as ComputeSize>::compute_size(self.mark_class_count()).unwrap_or(0),
2773            );
2774        start..end
2775    }
2776}
2777
2778const _: () = assert!(FontData::default_data_long_enough(BaseArray::MIN_SIZE));
2779
2780impl Default for BaseArray<'_> {
2781    fn default() -> Self {
2782        Self {
2783            data: TableDataRef::new(FontData::default_table_data().as_bytes())
2784                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
2785            mark_class_count: Default::default(),
2786        }
2787    }
2788}
2789
2790/// Part of [BaseArray]
2791#[derive(Clone, Debug)]
2792pub struct BaseRecord<'a> {
2793    /// Array of offsets (one per mark class) to Anchor tables. Offsets
2794    /// are from beginning of BaseArray table, ordered by class
2795    /// (offsets may be NULL).
2796    pub base_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
2797}
2798
2799impl<'a> BaseRecord<'a> {
2800    /// Array of offsets (one per mark class) to Anchor tables. Offsets
2801    /// are from beginning of BaseArray table, ordered by class
2802    /// (offsets may be NULL).
2803    pub fn base_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
2804        self.base_anchor_offsets
2805    }
2806
2807    /// Array of offsets (one per mark class) to Anchor tables. Offsets
2808    /// are from beginning of BaseArray table, ordered by class
2809    /// (offsets may be NULL).
2810    ///
2811    /// The `data` argument should be retrieved from the parent table
2812    /// By calling its `offset_data` method.
2813    pub fn base_anchors(
2814        &self,
2815        data: FontData<'a>,
2816    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
2817        let offsets = self.base_anchor_offsets();
2818        ArrayOfNullableOffsets::new(offsets, data, ())
2819    }
2820}
2821
2822impl ReadArgs for BaseRecord<'_> {
2823    type Args = u16;
2824}
2825
2826impl ComputeSize for BaseRecord<'_> {
2827    #[allow(clippy::needless_question_mark)]
2828    fn compute_size(args: u16) -> Result<usize, ReadError> {
2829        let mark_class_count = args;
2830        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
2831    }
2832}
2833
2834impl<'a> FontRead<'a> for BaseRecord<'a> {
2835    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
2836        let mut cursor = data.cursor();
2837        let mark_class_count = args;
2838        Ok(Self {
2839            base_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
2840        })
2841    }
2842}
2843
2844crate::impl_font_read_at!(BaseRecord<'a>);
2845
2846#[allow(clippy::needless_lifetimes)]
2847impl<'a> BaseRecord<'a> {
2848    /// A constructor that requires additional arguments.
2849    ///
2850    /// This type requires some external state in order to be
2851    /// parsed.
2852    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
2853        let args = mark_class_count;
2854        Self::read_with_args(data, args)
2855    }
2856}
2857
2858impl Format<u16> for MarkLigPosFormat1<'_> {
2859    const FORMAT: u16 = 1;
2860}
2861
2862impl<'a> MinByteRange<'a> for MarkLigPosFormat1<'a> {
2863    fn min_byte_range(&self) -> Range<usize> {
2864        0..self.ligature_array_offset_byte_range().end
2865    }
2866    fn min_table_bytes(&self) -> &'a [u8] {
2867        let range = self.min_byte_range();
2868        self.offset_data().as_bytes().get(range).unwrap_or_default()
2869    }
2870}
2871
2872impl ReadArgs for MarkLigPosFormat1<'_> {
2873    type Args = ();
2874}
2875
2876impl<'a> FontRead<'a> for MarkLigPosFormat1<'a> {
2877    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
2878        #[allow(clippy::absurd_extreme_comparisons)]
2879        if data.len() < Self::MIN_SIZE {
2880            return Err(ReadError::OutOfBounds);
2881        }
2882        let Some(data) = TableDataRef::new(data.as_bytes()) else {
2883            return Err(ReadError::OutOfBounds);
2884        };
2885        Ok(Self { data })
2886    }
2887}
2888
2889/// The fields at the start of the table, which are read directly.
2890#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
2891#[repr(C, packed)]
2892#[doc(hidden)]
2893pub struct MarkLigPosFormat1FixedHeader {
2894    pub pos_format: BigEndian<u16>,
2895    pub mark_coverage_offset: BigEndian<Offset16>,
2896    pub ligature_coverage_offset: BigEndian<Offset16>,
2897    pub mark_class_count: BigEndian<u16>,
2898    pub mark_array_offset: BigEndian<Offset16>,
2899    pub ligature_array_offset: BigEndian<Offset16>,
2900}
2901
2902#[allow(clippy::absurd_extreme_comparisons)]
2903const _: () =
2904    assert!(std::mem::size_of::<MarkLigPosFormat1FixedHeader>() <= MarkLigPosFormat1::MIN_SIZE);
2905
2906/// [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
2907#[derive(Clone)]
2908pub struct MarkLigPosFormat1<'a> {
2909    data: TableDataRef<'a, MarkLigPosFormat1FixedHeader>,
2910}
2911
2912#[allow(clippy::needless_lifetimes)]
2913impl<'a> MarkLigPosFormat1<'a> {
2914    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2915        + Offset16::RAW_BYTE_LEN
2916        + Offset16::RAW_BYTE_LEN
2917        + u16::RAW_BYTE_LEN
2918        + Offset16::RAW_BYTE_LEN
2919        + Offset16::RAW_BYTE_LEN);
2920    basic_table_impls!(impl_the_methods);
2921
2922    /// Format identifier: format = 1
2923    pub fn pos_format(&self) -> u16 {
2924        self.data.header().pos_format.get()
2925    }
2926
2927    /// Offset to markCoverage table, from beginning of MarkLigPos
2928    /// subtable.
2929    pub fn mark_coverage_offset(&self) -> Offset16 {
2930        self.data.header().mark_coverage_offset.get()
2931    }
2932
2933    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
2934    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2935        let data = self.offset_data();
2936        self.mark_coverage_offset().resolve(data)
2937    }
2938
2939    /// Offset to ligatureCoverage table, from beginning of MarkLigPos
2940    /// subtable.
2941    pub fn ligature_coverage_offset(&self) -> Offset16 {
2942        self.data.header().ligature_coverage_offset.get()
2943    }
2944
2945    /// Attempt to resolve [`ligature_coverage_offset`][Self::ligature_coverage_offset].
2946    pub fn ligature_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2947        let data = self.offset_data();
2948        self.ligature_coverage_offset().resolve(data)
2949    }
2950
2951    /// Number of defined mark classes
2952    pub fn mark_class_count(&self) -> u16 {
2953        self.data.header().mark_class_count.get()
2954    }
2955
2956    /// Offset to MarkArray table, from beginning of MarkLigPos
2957    /// subtable.
2958    pub fn mark_array_offset(&self) -> Offset16 {
2959        self.data.header().mark_array_offset.get()
2960    }
2961
2962    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
2963    pub fn mark_array(&self) -> Result<MarkArray<'a>, ReadError> {
2964        let data = self.offset_data();
2965        self.mark_array_offset().resolve(data)
2966    }
2967
2968    /// Offset to LigatureArray table, from beginning of MarkLigPos
2969    /// subtable.
2970    pub fn ligature_array_offset(&self) -> Offset16 {
2971        self.data.header().ligature_array_offset.get()
2972    }
2973
2974    /// Attempt to resolve [`ligature_array_offset`][Self::ligature_array_offset].
2975    pub fn ligature_array(&self) -> Result<LigatureArray<'a>, ReadError> {
2976        let data = self.offset_data();
2977        let args = self.mark_class_count();
2978        self.ligature_array_offset().resolve_with_args(data, args)
2979    }
2980
2981    pub fn pos_format_byte_range(&self) -> Range<usize> {
2982        let start = 0;
2983        let end = start + u16::RAW_BYTE_LEN;
2984        start..end
2985    }
2986
2987    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
2988        let start = self.pos_format_byte_range().end;
2989        let end = start + Offset16::RAW_BYTE_LEN;
2990        start..end
2991    }
2992
2993    pub fn ligature_coverage_offset_byte_range(&self) -> Range<usize> {
2994        let start = self.mark_coverage_offset_byte_range().end;
2995        let end = start + Offset16::RAW_BYTE_LEN;
2996        start..end
2997    }
2998
2999    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
3000        let start = self.ligature_coverage_offset_byte_range().end;
3001        let end = start + u16::RAW_BYTE_LEN;
3002        start..end
3003    }
3004
3005    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
3006        let start = self.mark_class_count_byte_range().end;
3007        let end = start + Offset16::RAW_BYTE_LEN;
3008        start..end
3009    }
3010
3011    pub fn ligature_array_offset_byte_range(&self) -> Range<usize> {
3012        let start = self.mark_array_offset_byte_range().end;
3013        let end = start + Offset16::RAW_BYTE_LEN;
3014        start..end
3015    }
3016}
3017
3018const _: () = assert!(FontData::default_data_long_enough(
3019    MarkLigPosFormat1::MIN_SIZE
3020));
3021
3022impl Default for MarkLigPosFormat1<'_> {
3023    fn default() -> Self {
3024        Self {
3025            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
3026                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3027        }
3028    }
3029}
3030
3031impl<'a> MinByteRange<'a> for LigatureArray<'a> {
3032    fn min_byte_range(&self) -> Range<usize> {
3033        0..self.ligature_attach_offsets_byte_range().end
3034    }
3035    fn min_table_bytes(&self) -> &'a [u8] {
3036        let range = self.min_byte_range();
3037        self.offset_data().as_bytes().get(range).unwrap_or_default()
3038    }
3039}
3040
3041impl ReadArgs for LigatureArray<'_> {
3042    type Args = u16;
3043}
3044
3045impl<'a> FontRead<'a> for LigatureArray<'a> {
3046    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3047        let mark_class_count = args;
3048
3049        #[allow(clippy::absurd_extreme_comparisons)]
3050        if data.len() < Self::MIN_SIZE {
3051            return Err(ReadError::OutOfBounds);
3052        }
3053        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3054            return Err(ReadError::OutOfBounds);
3055        };
3056        Ok(Self {
3057            data,
3058            mark_class_count,
3059        })
3060    }
3061}
3062
3063impl<'a> LigatureArray<'a> {
3064    /// A constructor that requires additional arguments.
3065    ///
3066    /// This type requires some external state in order to be
3067    /// parsed.
3068    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3069        let args = mark_class_count;
3070        Self::read_with_args(data, args)
3071    }
3072}
3073
3074/// The fields at the start of the table, which are read directly.
3075#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3076#[repr(C, packed)]
3077#[doc(hidden)]
3078pub struct LigatureArrayFixedHeader {
3079    pub ligature_count: BigEndian<u16>,
3080}
3081
3082#[allow(clippy::absurd_extreme_comparisons)]
3083const _: () = assert!(std::mem::size_of::<LigatureArrayFixedHeader>() <= LigatureArray::MIN_SIZE);
3084
3085/// Part of [MarkLigPosFormat1]
3086#[derive(Clone)]
3087pub struct LigatureArray<'a> {
3088    data: TableDataRef<'a, LigatureArrayFixedHeader>,
3089    mark_class_count: u16,
3090}
3091
3092#[allow(clippy::needless_lifetimes)]
3093impl<'a> LigatureArray<'a> {
3094    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3095    basic_table_impls!(impl_the_methods);
3096
3097    /// Number of LigatureAttach table offsets
3098    pub fn ligature_count(&self) -> u16 {
3099        self.data.header().ligature_count.get()
3100    }
3101
3102    /// Array of offsets to LigatureAttach tables. Offsets are from
3103    /// beginning of LigatureArray table, ordered by ligatureCoverage
3104    /// index.
3105    pub fn ligature_attach_offsets(&self) -> &'a [BigEndian<Offset16>] {
3106        let range = self.ligature_attach_offsets_byte_range();
3107        self.offset_data()
3108            .read_array(range)
3109            .ok()
3110            .unwrap_or_default()
3111    }
3112
3113    /// A dynamically resolving wrapper for [`ligature_attach_offsets`][Self::ligature_attach_offsets].
3114    pub fn ligature_attaches(&self) -> ArrayOfOffsets<'a, LigatureAttach<'a>, Offset16> {
3115        let data = self.offset_data();
3116        let offsets = self.ligature_attach_offsets();
3117        let args = self.mark_class_count();
3118        ArrayOfOffsets::new(offsets, data, args)
3119    }
3120
3121    pub(crate) fn mark_class_count(&self) -> u16 {
3122        self.mark_class_count
3123    }
3124
3125    pub fn ligature_count_byte_range(&self) -> Range<usize> {
3126        let start = 0;
3127        let end = start + u16::RAW_BYTE_LEN;
3128        start..end
3129    }
3130
3131    pub fn ligature_attach_offsets_byte_range(&self) -> Range<usize> {
3132        let ligature_count = self.ligature_count();
3133        let start = self.ligature_count_byte_range().end;
3134        let end =
3135            start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN);
3136        start..end
3137    }
3138}
3139
3140const _: () = assert!(FontData::default_data_long_enough(LigatureArray::MIN_SIZE));
3141
3142impl Default for LigatureArray<'_> {
3143    fn default() -> Self {
3144        Self {
3145            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3146                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3147            mark_class_count: Default::default(),
3148        }
3149    }
3150}
3151
3152impl<'a> MinByteRange<'a> for LigatureAttach<'a> {
3153    fn min_byte_range(&self) -> Range<usize> {
3154        0..self.component_records_byte_range().end
3155    }
3156    fn min_table_bytes(&self) -> &'a [u8] {
3157        let range = self.min_byte_range();
3158        self.offset_data().as_bytes().get(range).unwrap_or_default()
3159    }
3160}
3161
3162impl ReadArgs for LigatureAttach<'_> {
3163    type Args = u16;
3164}
3165
3166impl<'a> FontRead<'a> for LigatureAttach<'a> {
3167    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3168        let mark_class_count = args;
3169
3170        #[allow(clippy::absurd_extreme_comparisons)]
3171        if data.len() < Self::MIN_SIZE {
3172            return Err(ReadError::OutOfBounds);
3173        }
3174        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3175            return Err(ReadError::OutOfBounds);
3176        };
3177        Ok(Self {
3178            data,
3179            mark_class_count,
3180        })
3181    }
3182}
3183
3184impl<'a> LigatureAttach<'a> {
3185    /// A constructor that requires additional arguments.
3186    ///
3187    /// This type requires some external state in order to be
3188    /// parsed.
3189    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3190        let args = mark_class_count;
3191        Self::read_with_args(data, args)
3192    }
3193}
3194
3195/// The fields at the start of the table, which are read directly.
3196#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3197#[repr(C, packed)]
3198#[doc(hidden)]
3199pub struct LigatureAttachFixedHeader {
3200    pub component_count: BigEndian<u16>,
3201}
3202
3203#[allow(clippy::absurd_extreme_comparisons)]
3204const _: () = assert!(std::mem::size_of::<LigatureAttachFixedHeader>() <= LigatureAttach::MIN_SIZE);
3205
3206/// Part of [MarkLigPosFormat1]
3207#[derive(Clone)]
3208pub struct LigatureAttach<'a> {
3209    data: TableDataRef<'a, LigatureAttachFixedHeader>,
3210    mark_class_count: u16,
3211}
3212
3213#[allow(clippy::needless_lifetimes)]
3214impl<'a> LigatureAttach<'a> {
3215    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3216    basic_table_impls!(impl_the_methods);
3217
3218    /// Number of ComponentRecords in this ligature
3219    pub fn component_count(&self) -> u16 {
3220        self.data.header().component_count.get()
3221    }
3222
3223    /// Array of Component records, ordered in writing direction.
3224    pub fn component_records(&self) -> ComputedArray<'a, ComponentRecord<'a>> {
3225        let range = self.component_records_byte_range();
3226        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
3227    }
3228
3229    pub(crate) fn mark_class_count(&self) -> u16 {
3230        self.mark_class_count
3231    }
3232
3233    pub fn component_count_byte_range(&self) -> Range<usize> {
3234        let start = 0;
3235        let end = start + u16::RAW_BYTE_LEN;
3236        start..end
3237    }
3238
3239    pub fn component_records_byte_range(&self) -> Range<usize> {
3240        let component_count = self.component_count();
3241        let start = self.component_count_byte_range().end;
3242        let end = start
3243            + (transforms::to_usize(component_count)).saturating_mul(
3244                <ComponentRecord as ComputeSize>::compute_size(self.mark_class_count())
3245                    .unwrap_or(0),
3246            );
3247        start..end
3248    }
3249}
3250
3251const _: () = assert!(FontData::default_data_long_enough(LigatureAttach::MIN_SIZE));
3252
3253impl Default for LigatureAttach<'_> {
3254    fn default() -> Self {
3255        Self {
3256            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3257                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3258            mark_class_count: Default::default(),
3259        }
3260    }
3261}
3262
3263/// Part of [MarkLigPosFormat1]
3264#[derive(Clone, Debug)]
3265pub struct ComponentRecord<'a> {
3266    /// Array of offsets (one per class) to Anchor tables. Offsets are
3267    /// from beginning of LigatureAttach table, ordered by class
3268    /// (offsets may be NULL).
3269    pub ligature_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3270}
3271
3272impl<'a> ComponentRecord<'a> {
3273    /// Array of offsets (one per class) to Anchor tables. Offsets are
3274    /// from beginning of LigatureAttach table, ordered by class
3275    /// (offsets may be NULL).
3276    pub fn ligature_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3277        self.ligature_anchor_offsets
3278    }
3279
3280    /// Array of offsets (one per class) to Anchor tables. Offsets are
3281    /// from beginning of LigatureAttach table, ordered by class
3282    /// (offsets may be NULL).
3283    ///
3284    /// The `data` argument should be retrieved from the parent table
3285    /// By calling its `offset_data` method.
3286    pub fn ligature_anchors(
3287        &self,
3288        data: FontData<'a>,
3289    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3290        let offsets = self.ligature_anchor_offsets();
3291        ArrayOfNullableOffsets::new(offsets, data, ())
3292    }
3293}
3294
3295impl ReadArgs for ComponentRecord<'_> {
3296    type Args = u16;
3297}
3298
3299impl ComputeSize for ComponentRecord<'_> {
3300    #[allow(clippy::needless_question_mark)]
3301    fn compute_size(args: u16) -> Result<usize, ReadError> {
3302        let mark_class_count = args;
3303        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
3304    }
3305}
3306
3307impl<'a> FontRead<'a> for ComponentRecord<'a> {
3308    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3309        let mut cursor = data.cursor();
3310        let mark_class_count = args;
3311        Ok(Self {
3312            ligature_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
3313        })
3314    }
3315}
3316
3317crate::impl_font_read_at!(ComponentRecord<'a>);
3318
3319#[allow(clippy::needless_lifetimes)]
3320impl<'a> ComponentRecord<'a> {
3321    /// A constructor that requires additional arguments.
3322    ///
3323    /// This type requires some external state in order to be
3324    /// parsed.
3325    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3326        let args = mark_class_count;
3327        Self::read_with_args(data, args)
3328    }
3329}
3330
3331impl Format<u16> for MarkMarkPosFormat1<'_> {
3332    const FORMAT: u16 = 1;
3333}
3334
3335impl<'a> MinByteRange<'a> for MarkMarkPosFormat1<'a> {
3336    fn min_byte_range(&self) -> Range<usize> {
3337        0..self.mark2_array_offset_byte_range().end
3338    }
3339    fn min_table_bytes(&self) -> &'a [u8] {
3340        let range = self.min_byte_range();
3341        self.offset_data().as_bytes().get(range).unwrap_or_default()
3342    }
3343}
3344
3345impl ReadArgs for MarkMarkPosFormat1<'_> {
3346    type Args = ();
3347}
3348
3349impl<'a> FontRead<'a> for MarkMarkPosFormat1<'a> {
3350    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3351        #[allow(clippy::absurd_extreme_comparisons)]
3352        if data.len() < Self::MIN_SIZE {
3353            return Err(ReadError::OutOfBounds);
3354        }
3355        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3356            return Err(ReadError::OutOfBounds);
3357        };
3358        Ok(Self { data })
3359    }
3360}
3361
3362/// The fields at the start of the table, which are read directly.
3363#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3364#[repr(C, packed)]
3365#[doc(hidden)]
3366pub struct MarkMarkPosFormat1FixedHeader {
3367    pub pos_format: BigEndian<u16>,
3368    pub mark1_coverage_offset: BigEndian<Offset16>,
3369    pub mark2_coverage_offset: BigEndian<Offset16>,
3370    pub mark_class_count: BigEndian<u16>,
3371    pub mark1_array_offset: BigEndian<Offset16>,
3372    pub mark2_array_offset: BigEndian<Offset16>,
3373}
3374
3375#[allow(clippy::absurd_extreme_comparisons)]
3376const _: () =
3377    assert!(std::mem::size_of::<MarkMarkPosFormat1FixedHeader>() <= MarkMarkPosFormat1::MIN_SIZE);
3378
3379/// [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
3380#[derive(Clone)]
3381pub struct MarkMarkPosFormat1<'a> {
3382    data: TableDataRef<'a, MarkMarkPosFormat1FixedHeader>,
3383}
3384
3385#[allow(clippy::needless_lifetimes)]
3386impl<'a> MarkMarkPosFormat1<'a> {
3387    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3388        + Offset16::RAW_BYTE_LEN
3389        + Offset16::RAW_BYTE_LEN
3390        + u16::RAW_BYTE_LEN
3391        + Offset16::RAW_BYTE_LEN
3392        + Offset16::RAW_BYTE_LEN);
3393    basic_table_impls!(impl_the_methods);
3394
3395    /// Format identifier: format = 1
3396    pub fn pos_format(&self) -> u16 {
3397        self.data.header().pos_format.get()
3398    }
3399
3400    /// Offset to Combining Mark Coverage table, from beginning of
3401    /// MarkMarkPos subtable.
3402    pub fn mark1_coverage_offset(&self) -> Offset16 {
3403        self.data.header().mark1_coverage_offset.get()
3404    }
3405
3406    /// Attempt to resolve [`mark1_coverage_offset`][Self::mark1_coverage_offset].
3407    pub fn mark1_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3408        let data = self.offset_data();
3409        self.mark1_coverage_offset().resolve(data)
3410    }
3411
3412    /// Offset to Base Mark Coverage table, from beginning of
3413    /// MarkMarkPos subtable.
3414    pub fn mark2_coverage_offset(&self) -> Offset16 {
3415        self.data.header().mark2_coverage_offset.get()
3416    }
3417
3418    /// Attempt to resolve [`mark2_coverage_offset`][Self::mark2_coverage_offset].
3419    pub fn mark2_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3420        let data = self.offset_data();
3421        self.mark2_coverage_offset().resolve(data)
3422    }
3423
3424    /// Number of Combining Mark classes defined
3425    pub fn mark_class_count(&self) -> u16 {
3426        self.data.header().mark_class_count.get()
3427    }
3428
3429    /// Offset to MarkArray table for mark1, from beginning of
3430    /// MarkMarkPos subtable.
3431    pub fn mark1_array_offset(&self) -> Offset16 {
3432        self.data.header().mark1_array_offset.get()
3433    }
3434
3435    /// Attempt to resolve [`mark1_array_offset`][Self::mark1_array_offset].
3436    pub fn mark1_array(&self) -> Result<MarkArray<'a>, ReadError> {
3437        let data = self.offset_data();
3438        self.mark1_array_offset().resolve(data)
3439    }
3440
3441    /// Offset to Mark2Array table for mark2, from beginning of
3442    /// MarkMarkPos subtable.
3443    pub fn mark2_array_offset(&self) -> Offset16 {
3444        self.data.header().mark2_array_offset.get()
3445    }
3446
3447    /// Attempt to resolve [`mark2_array_offset`][Self::mark2_array_offset].
3448    pub fn mark2_array(&self) -> Result<Mark2Array<'a>, ReadError> {
3449        let data = self.offset_data();
3450        let args = self.mark_class_count();
3451        self.mark2_array_offset().resolve_with_args(data, args)
3452    }
3453
3454    pub fn pos_format_byte_range(&self) -> Range<usize> {
3455        let start = 0;
3456        let end = start + u16::RAW_BYTE_LEN;
3457        start..end
3458    }
3459
3460    pub fn mark1_coverage_offset_byte_range(&self) -> Range<usize> {
3461        let start = self.pos_format_byte_range().end;
3462        let end = start + Offset16::RAW_BYTE_LEN;
3463        start..end
3464    }
3465
3466    pub fn mark2_coverage_offset_byte_range(&self) -> Range<usize> {
3467        let start = self.mark1_coverage_offset_byte_range().end;
3468        let end = start + Offset16::RAW_BYTE_LEN;
3469        start..end
3470    }
3471
3472    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
3473        let start = self.mark2_coverage_offset_byte_range().end;
3474        let end = start + u16::RAW_BYTE_LEN;
3475        start..end
3476    }
3477
3478    pub fn mark1_array_offset_byte_range(&self) -> Range<usize> {
3479        let start = self.mark_class_count_byte_range().end;
3480        let end = start + Offset16::RAW_BYTE_LEN;
3481        start..end
3482    }
3483
3484    pub fn mark2_array_offset_byte_range(&self) -> Range<usize> {
3485        let start = self.mark1_array_offset_byte_range().end;
3486        let end = start + Offset16::RAW_BYTE_LEN;
3487        start..end
3488    }
3489}
3490
3491const _: () = assert!(FontData::default_data_long_enough(
3492    MarkMarkPosFormat1::MIN_SIZE
3493));
3494
3495impl Default for MarkMarkPosFormat1<'_> {
3496    fn default() -> Self {
3497        Self {
3498            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
3499                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3500        }
3501    }
3502}
3503
3504impl<'a> MinByteRange<'a> for Mark2Array<'a> {
3505    fn min_byte_range(&self) -> Range<usize> {
3506        0..self.mark2_records_byte_range().end
3507    }
3508    fn min_table_bytes(&self) -> &'a [u8] {
3509        let range = self.min_byte_range();
3510        self.offset_data().as_bytes().get(range).unwrap_or_default()
3511    }
3512}
3513
3514impl ReadArgs for Mark2Array<'_> {
3515    type Args = u16;
3516}
3517
3518impl<'a> FontRead<'a> for Mark2Array<'a> {
3519    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3520        let mark_class_count = args;
3521
3522        #[allow(clippy::absurd_extreme_comparisons)]
3523        if data.len() < Self::MIN_SIZE {
3524            return Err(ReadError::OutOfBounds);
3525        }
3526        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3527            return Err(ReadError::OutOfBounds);
3528        };
3529        Ok(Self {
3530            data,
3531            mark_class_count,
3532        })
3533    }
3534}
3535
3536impl<'a> Mark2Array<'a> {
3537    /// A constructor that requires additional arguments.
3538    ///
3539    /// This type requires some external state in order to be
3540    /// parsed.
3541    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3542        let args = mark_class_count;
3543        Self::read_with_args(data, args)
3544    }
3545}
3546
3547/// The fields at the start of the table, which are read directly.
3548#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3549#[repr(C, packed)]
3550#[doc(hidden)]
3551pub struct Mark2ArrayFixedHeader {
3552    pub mark2_count: BigEndian<u16>,
3553}
3554
3555#[allow(clippy::absurd_extreme_comparisons)]
3556const _: () = assert!(std::mem::size_of::<Mark2ArrayFixedHeader>() <= Mark2Array::MIN_SIZE);
3557
3558/// Part of [MarkMarkPosFormat1]Class2Record
3559#[derive(Clone)]
3560pub struct Mark2Array<'a> {
3561    data: TableDataRef<'a, Mark2ArrayFixedHeader>,
3562    mark_class_count: u16,
3563}
3564
3565#[allow(clippy::needless_lifetimes)]
3566impl<'a> Mark2Array<'a> {
3567    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3568    basic_table_impls!(impl_the_methods);
3569
3570    /// Number of Mark2 records
3571    pub fn mark2_count(&self) -> u16 {
3572        self.data.header().mark2_count.get()
3573    }
3574
3575    /// Array of Mark2Records, in Coverage order.
3576    pub fn mark2_records(&self) -> ComputedArray<'a, Mark2Record<'a>> {
3577        let range = self.mark2_records_byte_range();
3578        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
3579    }
3580
3581    pub(crate) fn mark_class_count(&self) -> u16 {
3582        self.mark_class_count
3583    }
3584
3585    pub fn mark2_count_byte_range(&self) -> Range<usize> {
3586        let start = 0;
3587        let end = start + u16::RAW_BYTE_LEN;
3588        start..end
3589    }
3590
3591    pub fn mark2_records_byte_range(&self) -> Range<usize> {
3592        let mark2_count = self.mark2_count();
3593        let start = self.mark2_count_byte_range().end;
3594        let end = start
3595            + (transforms::to_usize(mark2_count)).saturating_mul(
3596                <Mark2Record as ComputeSize>::compute_size(self.mark_class_count()).unwrap_or(0),
3597            );
3598        start..end
3599    }
3600}
3601
3602const _: () = assert!(FontData::default_data_long_enough(Mark2Array::MIN_SIZE));
3603
3604impl Default for Mark2Array<'_> {
3605    fn default() -> Self {
3606        Self {
3607            data: TableDataRef::new(FontData::default_table_data().as_bytes())
3608                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3609            mark_class_count: Default::default(),
3610        }
3611    }
3612}
3613
3614/// Part of [MarkMarkPosFormat1]
3615#[derive(Clone, Debug)]
3616pub struct Mark2Record<'a> {
3617    /// Array of offsets (one per class) to Anchor tables. Offsets are
3618    /// from beginning of Mark2Array table, in class order (offsets may
3619    /// be NULL).
3620    pub mark2_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3621}
3622
3623impl<'a> Mark2Record<'a> {
3624    /// Array of offsets (one per class) to Anchor tables. Offsets are
3625    /// from beginning of Mark2Array table, in class order (offsets may
3626    /// be NULL).
3627    pub fn mark2_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3628        self.mark2_anchor_offsets
3629    }
3630
3631    /// Array of offsets (one per class) to Anchor tables. Offsets are
3632    /// from beginning of Mark2Array table, in class order (offsets may
3633    /// be NULL).
3634    ///
3635    /// The `data` argument should be retrieved from the parent table
3636    /// By calling its `offset_data` method.
3637    pub fn mark2_anchors(
3638        &self,
3639        data: FontData<'a>,
3640    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3641        let offsets = self.mark2_anchor_offsets();
3642        ArrayOfNullableOffsets::new(offsets, data, ())
3643    }
3644}
3645
3646impl ReadArgs for Mark2Record<'_> {
3647    type Args = u16;
3648}
3649
3650impl ComputeSize for Mark2Record<'_> {
3651    #[allow(clippy::needless_question_mark)]
3652    fn compute_size(args: u16) -> Result<usize, ReadError> {
3653        let mark_class_count = args;
3654        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
3655    }
3656}
3657
3658impl<'a> FontRead<'a> for Mark2Record<'a> {
3659    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
3660        let mut cursor = data.cursor();
3661        let mark_class_count = args;
3662        Ok(Self {
3663            mark2_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
3664        })
3665    }
3666}
3667
3668crate::impl_font_read_at!(Mark2Record<'a>);
3669
3670#[allow(clippy::needless_lifetimes)]
3671impl<'a> Mark2Record<'a> {
3672    /// A constructor that requires additional arguments.
3673    ///
3674    /// This type requires some external state in order to be
3675    /// parsed.
3676    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3677        let args = mark_class_count;
3678        Self::read_with_args(data, args)
3679    }
3680}
3681
3682impl Format<u16> for ExtensionPosFormat1<'_> {
3683    const FORMAT: u16 = 1;
3684}
3685
3686impl Discriminant for ExtensionPosFormat1<'_, ()> {
3687    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
3688        data.read_at(u16::RAW_BYTE_LEN)
3689    }
3690}
3691
3692impl<'a, T> MinByteRange<'a> for ExtensionPosFormat1<'a, T> {
3693    fn min_byte_range(&self) -> Range<usize> {
3694        0..self.extension_offset_byte_range().end
3695    }
3696    fn min_table_bytes(&self) -> &'a [u8] {
3697        let range = self.min_byte_range();
3698        self.offset_data().as_bytes().get(range).unwrap_or_default()
3699    }
3700}
3701
3702impl<T> ReadArgs for ExtensionPosFormat1<'_, T> {
3703    type Args = ();
3704}
3705
3706impl<'a, T> FontRead<'a> for ExtensionPosFormat1<'a, T> {
3707    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3708        #[allow(clippy::absurd_extreme_comparisons)]
3709        if data.len() < Self::MIN_SIZE {
3710            return Err(ReadError::OutOfBounds);
3711        }
3712        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3713            return Err(ReadError::OutOfBounds);
3714        };
3715        Ok(Self {
3716            data,
3717            offset_type: std::marker::PhantomData,
3718        })
3719    }
3720}
3721
3722impl<'a, T> ExtensionPosFormat1<'a, T> {
3723    #[allow(dead_code)]
3724    /// Replace the specific generic type on this implementation with `()`
3725    pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> {
3726        ExtensionPosFormat1 {
3727            data: self.data,
3728            offset_type: std::marker::PhantomData,
3729        }
3730    }
3731}
3732
3733/// The fields at the start of the table, which are read directly.
3734#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3735#[repr(C, packed)]
3736#[doc(hidden)]
3737pub struct ExtensionPosFormat1FixedHeader {
3738    pub pos_format: BigEndian<u16>,
3739    pub extension_lookup_type: BigEndian<u16>,
3740    pub extension_offset: BigEndian<Offset32>,
3741}
3742
3743#[allow(clippy::absurd_extreme_comparisons)]
3744const _: () = assert!(
3745    std::mem::size_of::<ExtensionPosFormat1FixedHeader>() <= ExtensionPosFormat1::<()>::MIN_SIZE
3746);
3747
3748/// [Extension Positioning Subtable Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#extension-positioning-subtable-format-1)
3749#[derive(Clone)]
3750pub struct ExtensionPosFormat1<'a, T = ()> {
3751    data: TableDataRef<'a, ExtensionPosFormat1FixedHeader>,
3752    offset_type: std::marker::PhantomData<*const T>,
3753}
3754
3755#[allow(clippy::needless_lifetimes)]
3756impl<'a, T> ExtensionPosFormat1<'a, T> {
3757    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
3758    basic_table_impls!(impl_the_methods);
3759
3760    /// Format identifier: format = 1
3761    pub fn pos_format(&self) -> u16 {
3762        self.data.header().pos_format.get()
3763    }
3764
3765    /// Lookup type of subtable referenced by extensionOffset (i.e. the
3766    /// extension subtable).
3767    pub fn extension_lookup_type(&self) -> u16 {
3768        self.data.header().extension_lookup_type.get()
3769    }
3770
3771    /// Offset to the extension subtable, of lookup type
3772    /// extensionLookupType, relative to the start of the
3773    /// ExtensionPosFormat1 subtable.
3774    pub fn extension_offset(&self) -> Offset32 {
3775        self.data.header().extension_offset.get()
3776    }
3777
3778    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
3779    pub fn extension(&self) -> Result<T, ReadError>
3780    where
3781        T: FontRead<'a, Args = ()>,
3782    {
3783        let data = self.offset_data();
3784        self.extension_offset().resolve(data)
3785    }
3786
3787    pub fn pos_format_byte_range(&self) -> Range<usize> {
3788        let start = 0;
3789        let end = start + u16::RAW_BYTE_LEN;
3790        start..end
3791    }
3792
3793    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
3794        let start = self.pos_format_byte_range().end;
3795        let end = start + u16::RAW_BYTE_LEN;
3796        start..end
3797    }
3798
3799    pub fn extension_offset_byte_range(&self) -> Range<usize> {
3800        let start = self.extension_lookup_type_byte_range().end;
3801        let end = start + Offset32::RAW_BYTE_LEN;
3802        start..end
3803    }
3804}
3805
3806const _: () = assert!(FontData::default_data_long_enough(
3807    ExtensionPosFormat1::<()>::MIN_SIZE
3808));
3809
3810impl<T> Default for ExtensionPosFormat1<'_, T> {
3811    fn default() -> Self {
3812        Self {
3813            data: TableDataRef::new(FontData::default_format_1_u16_table_data().as_bytes())
3814                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
3815            offset_type: std::marker::PhantomData,
3816        }
3817    }
3818}
3819
3820/// A [GPOS Extension Positioning](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#lookuptype-9-extension-positioning) subtable
3821pub enum ExtensionSubtable<'a> {
3822    Single(ExtensionPosFormat1<'a, SinglePos<'a>>),
3823    Pair(ExtensionPosFormat1<'a, PairPos<'a>>),
3824    Cursive(ExtensionPosFormat1<'a, CursivePos<'a>>),
3825    MarkToBase(ExtensionPosFormat1<'a, MarkBasePos<'a>>),
3826    MarkToLig(ExtensionPosFormat1<'a, MarkLigPos<'a>>),
3827    MarkToMark(ExtensionPosFormat1<'a, MarkMarkPos<'a>>),
3828    Contextual(ExtensionPosFormat1<'a, PositionSequenceContext<'a>>),
3829    ChainContextual(ExtensionPosFormat1<'a, PositionChainContext<'a>>),
3830}
3831
3832impl Default for ExtensionSubtable<'_> {
3833    fn default() -> Self {
3834        Self::Single(Default::default())
3835    }
3836}
3837
3838impl ReadArgs for ExtensionSubtable<'_> {
3839    type Args = ();
3840}
3841
3842impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
3843    fn read_with_args(bytes: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3844        let discriminant = ExtensionPosFormat1::read_discriminant(bytes)?;
3845        match discriminant {
3846            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
3847            2 => Ok(ExtensionSubtable::Pair(FontRead::read(bytes)?)),
3848            3 => Ok(ExtensionSubtable::Cursive(FontRead::read(bytes)?)),
3849            4 => Ok(ExtensionSubtable::MarkToBase(FontRead::read(bytes)?)),
3850            5 => Ok(ExtensionSubtable::MarkToLig(FontRead::read(bytes)?)),
3851            6 => Ok(ExtensionSubtable::MarkToMark(FontRead::read(bytes)?)),
3852            7 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
3853            8 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
3854            other => Err(ReadError::InvalidFormat(other.into())),
3855        }
3856    }
3857}
3858
3859impl<'a> ExtensionSubtable<'a> {
3860    #[allow(dead_code)]
3861    /// Return the inner table, removing the specific generics.
3862    ///
3863    /// This lets us return a single concrete type we can call methods on.
3864    pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> {
3865        match self {
3866            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
3867            ExtensionSubtable::Pair(inner) => inner.of_unit_type(),
3868            ExtensionSubtable::Cursive(inner) => inner.of_unit_type(),
3869            ExtensionSubtable::MarkToBase(inner) => inner.of_unit_type(),
3870            ExtensionSubtable::MarkToLig(inner) => inner.of_unit_type(),
3871            ExtensionSubtable::MarkToMark(inner) => inner.of_unit_type(),
3872            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
3873            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
3874        }
3875    }
3876}
3877
3878impl Format<u16> for SinglePosFormat3<'_> {
3879    const FORMAT: u16 = 3;
3880}
3881
3882impl<'a> MinByteRange<'a> for SinglePosFormat3<'a> {
3883    fn min_byte_range(&self) -> Range<usize> {
3884        0..self.value_record_byte_range().end
3885    }
3886    fn min_table_bytes(&self) -> &'a [u8] {
3887        let range = self.min_byte_range();
3888        self.offset_data().as_bytes().get(range).unwrap_or_default()
3889    }
3890}
3891
3892impl ReadArgs for SinglePosFormat3<'_> {
3893    type Args = ();
3894}
3895
3896impl<'a> FontRead<'a> for SinglePosFormat3<'a> {
3897    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
3898        #[allow(clippy::absurd_extreme_comparisons)]
3899        if data.len() < Self::MIN_SIZE {
3900            return Err(ReadError::OutOfBounds);
3901        }
3902        let Some(data) = TableDataRef::new(data.as_bytes()) else {
3903            return Err(ReadError::OutOfBounds);
3904        };
3905        Ok(Self { data })
3906    }
3907}
3908
3909/// The fields at the start of the table, which are read directly.
3910#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
3911#[repr(C, packed)]
3912#[doc(hidden)]
3913pub struct SinglePosFormat3FixedHeader {
3914    pub pos_format: BigEndian<u16>,
3915    pub coverage_offset: BigEndian<Offset32>,
3916    pub value_format: BigEndian<ValueFormat>,
3917}
3918
3919#[allow(clippy::absurd_extreme_comparisons)]
3920const _: () =
3921    assert!(std::mem::size_of::<SinglePosFormat3FixedHeader>() <= SinglePosFormat3::MIN_SIZE);
3922
3923/// SinglePosFormat3 (ISO/IEC 14496-22:2026).
3924#[derive(Clone)]
3925pub struct SinglePosFormat3<'a> {
3926    data: TableDataRef<'a, SinglePosFormat3FixedHeader>,
3927}
3928
3929#[allow(clippy::needless_lifetimes)]
3930impl<'a> SinglePosFormat3<'a> {
3931    pub const MIN_SIZE: usize =
3932        (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + ValueFormat::RAW_BYTE_LEN);
3933    basic_table_impls!(impl_the_methods);
3934
3935    /// Format identifier: format = 3
3936    pub fn pos_format(&self) -> u16 {
3937        self.data.header().pos_format.get()
3938    }
3939
3940    /// Offset to Coverage table, from beginning of SinglePos subtable.
3941    pub fn coverage_offset(&self) -> Offset32 {
3942        self.data.header().coverage_offset.get()
3943    }
3944
3945    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
3946    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3947        let data = self.offset_data();
3948        self.coverage_offset().resolve(data)
3949    }
3950
3951    /// Defines the types of data in the ValueRecord.
3952    pub fn value_format(&self) -> ValueFormat {
3953        self.data.header().value_format.get()
3954    }
3955
3956    /// Defines positioning value(s) — applied to all glyphs in the
3957    /// Coverage table.
3958    pub fn value_record(&self) -> ValueRecord<'a> {
3959        let range = self.value_record_byte_range();
3960        ValueRecord::read_at(self.offset_data(), range.start, self.value_format())
3961            .unwrap_or_default()
3962    }
3963
3964    pub fn pos_format_byte_range(&self) -> Range<usize> {
3965        let start = 0;
3966        let end = start + u16::RAW_BYTE_LEN;
3967        start..end
3968    }
3969
3970    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
3971        let start = self.pos_format_byte_range().end;
3972        let end = start + Offset32::RAW_BYTE_LEN;
3973        start..end
3974    }
3975
3976    pub fn value_format_byte_range(&self) -> Range<usize> {
3977        let start = self.coverage_offset_byte_range().end;
3978        let end = start + ValueFormat::RAW_BYTE_LEN;
3979        start..end
3980    }
3981
3982    pub fn value_record_byte_range(&self) -> Range<usize> {
3983        let start = self.value_format_byte_range().end;
3984        let end =
3985            start + <ValueRecord as ComputeSize>::compute_size(self.value_format()).unwrap_or(0);
3986        start..end
3987    }
3988}
3989
3990impl Format<u16> for SinglePosFormat4<'_> {
3991    const FORMAT: u16 = 4;
3992}
3993
3994impl<'a> MinByteRange<'a> for SinglePosFormat4<'a> {
3995    fn min_byte_range(&self) -> Range<usize> {
3996        0..self.value_records_byte_range().end
3997    }
3998    fn min_table_bytes(&self) -> &'a [u8] {
3999        let range = self.min_byte_range();
4000        self.offset_data().as_bytes().get(range).unwrap_or_default()
4001    }
4002}
4003
4004impl ReadArgs for SinglePosFormat4<'_> {
4005    type Args = ();
4006}
4007
4008impl<'a> FontRead<'a> for SinglePosFormat4<'a> {
4009    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4010        #[allow(clippy::absurd_extreme_comparisons)]
4011        if data.len() < Self::MIN_SIZE {
4012            return Err(ReadError::OutOfBounds);
4013        }
4014        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4015            return Err(ReadError::OutOfBounds);
4016        };
4017        Ok(Self { data })
4018    }
4019}
4020
4021/// The fields at the start of the table, which are read directly.
4022#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4023#[repr(C, packed)]
4024#[doc(hidden)]
4025pub struct SinglePosFormat4FixedHeader {
4026    pub pos_format: BigEndian<u16>,
4027    pub coverage_offset: BigEndian<Offset32>,
4028    pub value_format: BigEndian<ValueFormat>,
4029    pub value_count: BigEndian<Uint24>,
4030}
4031
4032#[allow(clippy::absurd_extreme_comparisons)]
4033const _: () =
4034    assert!(std::mem::size_of::<SinglePosFormat4FixedHeader>() <= SinglePosFormat4::MIN_SIZE);
4035
4036/// SinglePosFormat4 (ISO/IEC 14496-22:2026).
4037#[derive(Clone)]
4038pub struct SinglePosFormat4<'a> {
4039    data: TableDataRef<'a, SinglePosFormat4FixedHeader>,
4040}
4041
4042#[allow(clippy::needless_lifetimes)]
4043impl<'a> SinglePosFormat4<'a> {
4044    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
4045        + Offset32::RAW_BYTE_LEN
4046        + ValueFormat::RAW_BYTE_LEN
4047        + Uint24::RAW_BYTE_LEN);
4048    basic_table_impls!(impl_the_methods);
4049
4050    /// Format identifier: format = 4
4051    pub fn pos_format(&self) -> u16 {
4052        self.data.header().pos_format.get()
4053    }
4054
4055    /// Offset to Coverage table, from beginning of SinglePos subtable.
4056    pub fn coverage_offset(&self) -> Offset32 {
4057        self.data.header().coverage_offset.get()
4058    }
4059
4060    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
4061    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
4062        let data = self.offset_data();
4063        self.coverage_offset().resolve(data)
4064    }
4065
4066    /// Defines the types of data in the ValueRecords.
4067    pub fn value_format(&self) -> ValueFormat {
4068        self.data.header().value_format.get()
4069    }
4070
4071    /// Number of ValueRecords — must equal glyphCount in the
4072    /// Coverage table.
4073    pub fn value_count(&self) -> Uint24 {
4074        self.data.header().value_count.get()
4075    }
4076
4077    /// Array of ValueRecords — positioning values applied to glyphs.
4078    pub fn value_records(&self) -> ComputedArray<'a, ValueRecord<'a>> {
4079        let range = self.value_records_byte_range();
4080        ComputedArray::new(self.offset_data(), range, self.value_format()).unwrap_or_default()
4081    }
4082
4083    pub fn pos_format_byte_range(&self) -> Range<usize> {
4084        let start = 0;
4085        let end = start + u16::RAW_BYTE_LEN;
4086        start..end
4087    }
4088
4089    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
4090        let start = self.pos_format_byte_range().end;
4091        let end = start + Offset32::RAW_BYTE_LEN;
4092        start..end
4093    }
4094
4095    pub fn value_format_byte_range(&self) -> Range<usize> {
4096        let start = self.coverage_offset_byte_range().end;
4097        let end = start + ValueFormat::RAW_BYTE_LEN;
4098        start..end
4099    }
4100
4101    pub fn value_count_byte_range(&self) -> Range<usize> {
4102        let start = self.value_format_byte_range().end;
4103        let end = start + Uint24::RAW_BYTE_LEN;
4104        start..end
4105    }
4106
4107    pub fn value_records_byte_range(&self) -> Range<usize> {
4108        let value_count = self.value_count();
4109        let start = self.value_count_byte_range().end;
4110        let end = start
4111            + (transforms::to_usize(value_count)).saturating_mul(
4112                <ValueRecord as ComputeSize>::compute_size(self.value_format()).unwrap_or(0),
4113            );
4114        start..end
4115    }
4116}
4117
4118impl Format<u16> for PairPosFormat3<'_> {
4119    const FORMAT: u16 = 3;
4120}
4121
4122impl<'a> MinByteRange<'a> for PairPosFormat3<'a> {
4123    fn min_byte_range(&self) -> Range<usize> {
4124        0..self.pair_set_offsets_byte_range().end
4125    }
4126    fn min_table_bytes(&self) -> &'a [u8] {
4127        let range = self.min_byte_range();
4128        self.offset_data().as_bytes().get(range).unwrap_or_default()
4129    }
4130}
4131
4132impl ReadArgs for PairPosFormat3<'_> {
4133    type Args = ();
4134}
4135
4136impl<'a> FontRead<'a> for PairPosFormat3<'a> {
4137    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4138        #[allow(clippy::absurd_extreme_comparisons)]
4139        if data.len() < Self::MIN_SIZE {
4140            return Err(ReadError::OutOfBounds);
4141        }
4142        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4143            return Err(ReadError::OutOfBounds);
4144        };
4145        Ok(Self { data })
4146    }
4147}
4148
4149/// The fields at the start of the table, which are read directly.
4150#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4151#[repr(C, packed)]
4152#[doc(hidden)]
4153pub struct PairPosFormat3FixedHeader {
4154    pub pos_format: BigEndian<u16>,
4155    pub coverage_offset: BigEndian<Offset32>,
4156    pub value_format1: BigEndian<ValueFormat>,
4157    pub value_format2: BigEndian<ValueFormat>,
4158    pub pair_set_count: BigEndian<Uint24>,
4159}
4160
4161#[allow(clippy::absurd_extreme_comparisons)]
4162const _: () = assert!(std::mem::size_of::<PairPosFormat3FixedHeader>() <= PairPosFormat3::MIN_SIZE);
4163
4164/// PairPosFormat3 (ISO/IEC 14496-22:2026).
4165#[derive(Clone)]
4166pub struct PairPosFormat3<'a> {
4167    data: TableDataRef<'a, PairPosFormat3FixedHeader>,
4168}
4169
4170#[allow(clippy::needless_lifetimes)]
4171impl<'a> PairPosFormat3<'a> {
4172    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
4173        + Offset32::RAW_BYTE_LEN
4174        + ValueFormat::RAW_BYTE_LEN
4175        + ValueFormat::RAW_BYTE_LEN
4176        + Uint24::RAW_BYTE_LEN);
4177    basic_table_impls!(impl_the_methods);
4178
4179    /// Format identifier: format = 3
4180    pub fn pos_format(&self) -> u16 {
4181        self.data.header().pos_format.get()
4182    }
4183
4184    /// Offset to Coverage table, from beginning of PairPos subtable.
4185    pub fn coverage_offset(&self) -> Offset32 {
4186        self.data.header().coverage_offset.get()
4187    }
4188
4189    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
4190    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
4191        let data = self.offset_data();
4192        self.coverage_offset().resolve(data)
4193    }
4194
4195    /// Defines the types of data in valueRecord1 — for the first
4196    /// glyph in the pair (may be zero).
4197    pub fn value_format1(&self) -> ValueFormat {
4198        self.data.header().value_format1.get()
4199    }
4200
4201    /// Defines the types of data in valueRecord2 — for the second
4202    /// glyph in the pair (may be zero).
4203    pub fn value_format2(&self) -> ValueFormat {
4204        self.data.header().value_format2.get()
4205    }
4206
4207    /// Number of PairSet tables
4208    pub fn pair_set_count(&self) -> Uint24 {
4209        self.data.header().pair_set_count.get()
4210    }
4211
4212    /// Array of offsets to PairSet tables. Offsets are from beginning
4213    /// of PairPos subtable, ordered by Coverage Index.
4214    pub fn pair_set_offsets(&self) -> &'a [BigEndian<Offset24>] {
4215        let range = self.pair_set_offsets_byte_range();
4216        self.offset_data()
4217            .read_array(range)
4218            .ok()
4219            .unwrap_or_default()
4220    }
4221
4222    /// A dynamically resolving wrapper for [`pair_set_offsets`][Self::pair_set_offsets].
4223    pub fn pair_sets(&self) -> ArrayOfOffsets<'a, PairSet2<'a>, Offset24> {
4224        let data = self.offset_data();
4225        let offsets = self.pair_set_offsets();
4226        let args = (self.value_format1(), self.value_format2());
4227        ArrayOfOffsets::new(offsets, data, args)
4228    }
4229
4230    pub fn pos_format_byte_range(&self) -> Range<usize> {
4231        let start = 0;
4232        let end = start + u16::RAW_BYTE_LEN;
4233        start..end
4234    }
4235
4236    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
4237        let start = self.pos_format_byte_range().end;
4238        let end = start + Offset32::RAW_BYTE_LEN;
4239        start..end
4240    }
4241
4242    pub fn value_format1_byte_range(&self) -> Range<usize> {
4243        let start = self.coverage_offset_byte_range().end;
4244        let end = start + ValueFormat::RAW_BYTE_LEN;
4245        start..end
4246    }
4247
4248    pub fn value_format2_byte_range(&self) -> Range<usize> {
4249        let start = self.value_format1_byte_range().end;
4250        let end = start + ValueFormat::RAW_BYTE_LEN;
4251        start..end
4252    }
4253
4254    pub fn pair_set_count_byte_range(&self) -> Range<usize> {
4255        let start = self.value_format2_byte_range().end;
4256        let end = start + Uint24::RAW_BYTE_LEN;
4257        start..end
4258    }
4259
4260    pub fn pair_set_offsets_byte_range(&self) -> Range<usize> {
4261        let pair_set_count = self.pair_set_count();
4262        let start = self.pair_set_count_byte_range().end;
4263        let end =
4264            start + (transforms::to_usize(pair_set_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
4265        start..end
4266    }
4267}
4268
4269impl<'a> MinByteRange<'a> for PairSet2<'a> {
4270    fn min_byte_range(&self) -> Range<usize> {
4271        0..self.pair_value_records_byte_range().end
4272    }
4273    fn min_table_bytes(&self) -> &'a [u8] {
4274        let range = self.min_byte_range();
4275        self.offset_data().as_bytes().get(range).unwrap_or_default()
4276    }
4277}
4278
4279impl ReadArgs for PairSet2<'_> {
4280    type Args = (ValueFormat, ValueFormat);
4281}
4282
4283impl<'a> FontRead<'a> for PairSet2<'a> {
4284    fn read_with_args(
4285        data: FontData<'a>,
4286        args: (ValueFormat, ValueFormat),
4287    ) -> Result<Self, ReadError> {
4288        let (value_format1, value_format2) = args;
4289
4290        #[allow(clippy::absurd_extreme_comparisons)]
4291        if data.len() < Self::MIN_SIZE {
4292            return Err(ReadError::OutOfBounds);
4293        }
4294        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4295            return Err(ReadError::OutOfBounds);
4296        };
4297        Ok(Self {
4298            data,
4299            value_format1,
4300            value_format2,
4301        })
4302    }
4303}
4304
4305impl<'a> PairSet2<'a> {
4306    /// A constructor that requires additional arguments.
4307    ///
4308    /// This type requires some external state in order to be
4309    /// parsed.
4310    pub fn read(
4311        data: FontData<'a>,
4312        value_format1: ValueFormat,
4313        value_format2: ValueFormat,
4314    ) -> Result<Self, ReadError> {
4315        let args = (value_format1, value_format2);
4316        Self::read_with_args(data, args)
4317    }
4318}
4319
4320/// The fields at the start of the table, which are read directly.
4321#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4322#[repr(C, packed)]
4323#[doc(hidden)]
4324pub struct PairSet2FixedHeader {
4325    pub pair_value_count: BigEndian<Uint24>,
4326}
4327
4328#[allow(clippy::absurd_extreme_comparisons)]
4329const _: () = assert!(std::mem::size_of::<PairSet2FixedHeader>() <= PairSet2::MIN_SIZE);
4330
4331/// PairSet2 (ISO/IEC 14496-22:2026).
4332#[derive(Clone)]
4333pub struct PairSet2<'a> {
4334    data: TableDataRef<'a, PairSet2FixedHeader>,
4335    value_format1: ValueFormat,
4336    value_format2: ValueFormat,
4337}
4338
4339#[allow(clippy::needless_lifetimes)]
4340impl<'a> PairSet2<'a> {
4341    pub const MIN_SIZE: usize = Uint24::RAW_BYTE_LEN;
4342    basic_table_impls!(impl_the_methods);
4343
4344    /// Number of PairValueRecords
4345    pub fn pair_value_count(&self) -> Uint24 {
4346        self.data.header().pair_value_count.get()
4347    }
4348
4349    /// Array of PairValueRecords, ordered by glyph ID of the second
4350    /// glyph.
4351    pub fn pair_value_records(&self) -> ComputedArray<'a, PairValueRecord2<'a>> {
4352        let range = self.pair_value_records_byte_range();
4353        ComputedArray::new(
4354            self.offset_data(),
4355            range,
4356            (self.value_format1(), self.value_format2()),
4357        )
4358        .unwrap_or_default()
4359    }
4360
4361    pub(crate) fn value_format1(&self) -> ValueFormat {
4362        self.value_format1
4363    }
4364
4365    pub(crate) fn value_format2(&self) -> ValueFormat {
4366        self.value_format2
4367    }
4368
4369    pub fn pair_value_count_byte_range(&self) -> Range<usize> {
4370        let start = 0;
4371        let end = start + Uint24::RAW_BYTE_LEN;
4372        start..end
4373    }
4374
4375    pub fn pair_value_records_byte_range(&self) -> Range<usize> {
4376        let pair_value_count = self.pair_value_count();
4377        let start = self.pair_value_count_byte_range().end;
4378        let end = start
4379            + (transforms::to_usize(pair_value_count)).saturating_mul(
4380                <PairValueRecord2 as ComputeSize>::compute_size((
4381                    self.value_format1(),
4382                    self.value_format2(),
4383                ))
4384                .unwrap_or(0),
4385            );
4386        start..end
4387    }
4388}
4389
4390const _: () = assert!(FontData::default_data_long_enough(PairSet2::MIN_SIZE));
4391
4392impl Default for PairSet2<'_> {
4393    fn default() -> Self {
4394        Self {
4395            data: TableDataRef::new(FontData::default_table_data().as_bytes())
4396                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
4397            value_format1: Default::default(),
4398            value_format2: Default::default(),
4399        }
4400    }
4401}
4402
4403/// PairValueRecord2 (ISO/IEC 14496-22:2026).
4404#[derive(Clone, Debug)]
4405pub struct PairValueRecord2<'a> {
4406    /// Glyph ID of second glyph in the pair (first glyph is listed in
4407    /// the Coverage table).
4408    pub second_glyph: BigEndian<GlyphId24>,
4409    /// Positioning data for the first glyph in the pair.
4410    pub value_record1: ValueRecord<'a>,
4411    /// Positioning data for the second glyph in the pair.
4412    pub value_record2: ValueRecord<'a>,
4413}
4414
4415impl<'a> PairValueRecord2<'a> {
4416    /// Glyph ID of second glyph in the pair (first glyph is listed in
4417    /// the Coverage table).
4418    pub fn second_glyph(&self) -> GlyphId24 {
4419        self.second_glyph.get()
4420    }
4421
4422    /// Positioning data for the first glyph in the pair.
4423    pub fn value_record1(&self) -> &ValueRecord<'a> {
4424        &self.value_record1
4425    }
4426
4427    /// Positioning data for the second glyph in the pair.
4428    pub fn value_record2(&self) -> &ValueRecord<'a> {
4429        &self.value_record2
4430    }
4431}
4432
4433impl ReadArgs for PairValueRecord2<'_> {
4434    type Args = (ValueFormat, ValueFormat);
4435}
4436
4437impl ComputeSize for PairValueRecord2<'_> {
4438    #[allow(clippy::needless_question_mark)]
4439    fn compute_size(args: (ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
4440        let (value_format1, value_format2) = args;
4441        let mut result = 0usize;
4442        result = result
4443            .checked_add(GlyphId24::RAW_BYTE_LEN)
4444            .ok_or(ReadError::OutOfBounds)?;
4445        result = result
4446            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format1).unwrap_or(0))
4447            .ok_or(ReadError::OutOfBounds)?;
4448        result = result
4449            .checked_add(<ValueRecord as ComputeSize>::compute_size(value_format2).unwrap_or(0))
4450            .ok_or(ReadError::OutOfBounds)?;
4451        Ok(result)
4452    }
4453}
4454
4455impl<'a> FontReadAt<'a> for PairValueRecord2<'a> {
4456    fn read_at(
4457        data: FontData<'a>,
4458        offset: usize,
4459        args: (ValueFormat, ValueFormat),
4460    ) -> Result<Self, ReadError> {
4461        let mut cursor = data.cursor();
4462        cursor.advance_by(offset);
4463        let (value_format1, value_format2) = args;
4464        Ok(Self {
4465            second_glyph: cursor.read_be()?,
4466            value_record1: cursor.read_at_with_args(value_format1)?,
4467            value_record2: cursor.read_at_with_args(value_format2)?,
4468        })
4469    }
4470}
4471
4472#[allow(clippy::needless_lifetimes)]
4473impl<'a> PairValueRecord2<'a> {
4474    /// A constructor that requires additional arguments.
4475    ///
4476    /// This type requires some external state in order to be
4477    /// parsed.
4478    pub fn read(
4479        data: FontData<'a>,
4480        offset: usize,
4481        value_format1: ValueFormat,
4482        value_format2: ValueFormat,
4483    ) -> Result<Self, ReadError> {
4484        let args = (value_format1, value_format2);
4485        Self::read_at(data, offset, args)
4486    }
4487}
4488
4489impl Format<u16> for PairPosFormat4<'_> {
4490    const FORMAT: u16 = 4;
4491}
4492
4493impl<'a> MinByteRange<'a> for PairPosFormat4<'a> {
4494    fn min_byte_range(&self) -> Range<usize> {
4495        0..self.class1_records_byte_range().end
4496    }
4497    fn min_table_bytes(&self) -> &'a [u8] {
4498        let range = self.min_byte_range();
4499        self.offset_data().as_bytes().get(range).unwrap_or_default()
4500    }
4501}
4502
4503impl ReadArgs for PairPosFormat4<'_> {
4504    type Args = ();
4505}
4506
4507impl<'a> FontRead<'a> for PairPosFormat4<'a> {
4508    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4509        #[allow(clippy::absurd_extreme_comparisons)]
4510        if data.len() < Self::MIN_SIZE {
4511            return Err(ReadError::OutOfBounds);
4512        }
4513        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4514            return Err(ReadError::OutOfBounds);
4515        };
4516        Ok(Self { data })
4517    }
4518}
4519
4520/// The fields at the start of the table, which are read directly.
4521#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4522#[repr(C, packed)]
4523#[doc(hidden)]
4524pub struct PairPosFormat4FixedHeader {
4525    pub pos_format: BigEndian<u16>,
4526    pub coverage_offset: BigEndian<Offset32>,
4527    pub value_format1: BigEndian<ValueFormat>,
4528    pub value_format2: BigEndian<ValueFormat>,
4529    pub class_def1_offset: BigEndian<Offset32>,
4530    pub class_def2_offset: BigEndian<Offset32>,
4531    pub class1_count: BigEndian<u16>,
4532    pub class2_count: BigEndian<u16>,
4533}
4534
4535#[allow(clippy::absurd_extreme_comparisons)]
4536const _: () = assert!(std::mem::size_of::<PairPosFormat4FixedHeader>() <= PairPosFormat4::MIN_SIZE);
4537
4538/// PairPosFormat4 (ISO/IEC 14496-22:2026).
4539#[derive(Clone)]
4540pub struct PairPosFormat4<'a> {
4541    data: TableDataRef<'a, PairPosFormat4FixedHeader>,
4542}
4543
4544#[allow(clippy::needless_lifetimes)]
4545impl<'a> PairPosFormat4<'a> {
4546    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
4547        + Offset32::RAW_BYTE_LEN
4548        + ValueFormat::RAW_BYTE_LEN
4549        + ValueFormat::RAW_BYTE_LEN
4550        + Offset32::RAW_BYTE_LEN
4551        + Offset32::RAW_BYTE_LEN
4552        + u16::RAW_BYTE_LEN
4553        + u16::RAW_BYTE_LEN);
4554    basic_table_impls!(impl_the_methods);
4555
4556    /// Format identifier: format = 4
4557    pub fn pos_format(&self) -> u16 {
4558        self.data.header().pos_format.get()
4559    }
4560
4561    /// Offset to Coverage table, from beginning of PairPos subtable.
4562    pub fn coverage_offset(&self) -> Offset32 {
4563        self.data.header().coverage_offset.get()
4564    }
4565
4566    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
4567    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
4568        let data = self.offset_data();
4569        self.coverage_offset().resolve(data)
4570    }
4571
4572    /// ValueRecord definition — for the first glyph of the pair (may
4573    /// be zero).
4574    pub fn value_format1(&self) -> ValueFormat {
4575        self.data.header().value_format1.get()
4576    }
4577
4578    /// ValueRecord definition — for the second glyph of the pair
4579    /// (may be zero).
4580    pub fn value_format2(&self) -> ValueFormat {
4581        self.data.header().value_format2.get()
4582    }
4583
4584    /// Offset to ClassDef table, from beginning of PairPos subtable
4585    /// — for the first glyph of the pair.
4586    pub fn class_def1_offset(&self) -> Offset32 {
4587        self.data.header().class_def1_offset.get()
4588    }
4589
4590    /// Attempt to resolve [`class_def1_offset`][Self::class_def1_offset].
4591    pub fn class_def1(&self) -> Result<ClassDef<'a>, ReadError> {
4592        let data = self.offset_data();
4593        self.class_def1_offset().resolve(data)
4594    }
4595
4596    /// Offset to ClassDef table, from beginning of PairPos subtable
4597    /// — for the second glyph of the pair.
4598    pub fn class_def2_offset(&self) -> Offset32 {
4599        self.data.header().class_def2_offset.get()
4600    }
4601
4602    /// Attempt to resolve [`class_def2_offset`][Self::class_def2_offset].
4603    pub fn class_def2(&self) -> Result<ClassDef<'a>, ReadError> {
4604        let data = self.offset_data();
4605        self.class_def2_offset().resolve(data)
4606    }
4607
4608    /// Number of classes in classDef1 table — includes Class 0.
4609    pub fn class1_count(&self) -> u16 {
4610        self.data.header().class1_count.get()
4611    }
4612
4613    /// Number of classes in classDef2 table — includes Class 0.
4614    pub fn class2_count(&self) -> u16 {
4615        self.data.header().class2_count.get()
4616    }
4617
4618    /// Array of Class1 records, ordered by classes in classDef1.
4619    pub fn class1_records(&self) -> ComputedArray<'a, Class1Record<'a>> {
4620        let range = self.class1_records_byte_range();
4621        ComputedArray::new(
4622            self.offset_data(),
4623            range,
4624            (
4625                self.class2_count(),
4626                self.value_format1(),
4627                self.value_format2(),
4628            ),
4629        )
4630        .unwrap_or_default()
4631    }
4632
4633    pub fn pos_format_byte_range(&self) -> Range<usize> {
4634        let start = 0;
4635        let end = start + u16::RAW_BYTE_LEN;
4636        start..end
4637    }
4638
4639    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
4640        let start = self.pos_format_byte_range().end;
4641        let end = start + Offset32::RAW_BYTE_LEN;
4642        start..end
4643    }
4644
4645    pub fn value_format1_byte_range(&self) -> Range<usize> {
4646        let start = self.coverage_offset_byte_range().end;
4647        let end = start + ValueFormat::RAW_BYTE_LEN;
4648        start..end
4649    }
4650
4651    pub fn value_format2_byte_range(&self) -> Range<usize> {
4652        let start = self.value_format1_byte_range().end;
4653        let end = start + ValueFormat::RAW_BYTE_LEN;
4654        start..end
4655    }
4656
4657    pub fn class_def1_offset_byte_range(&self) -> Range<usize> {
4658        let start = self.value_format2_byte_range().end;
4659        let end = start + Offset32::RAW_BYTE_LEN;
4660        start..end
4661    }
4662
4663    pub fn class_def2_offset_byte_range(&self) -> Range<usize> {
4664        let start = self.class_def1_offset_byte_range().end;
4665        let end = start + Offset32::RAW_BYTE_LEN;
4666        start..end
4667    }
4668
4669    pub fn class1_count_byte_range(&self) -> Range<usize> {
4670        let start = self.class_def2_offset_byte_range().end;
4671        let end = start + u16::RAW_BYTE_LEN;
4672        start..end
4673    }
4674
4675    pub fn class2_count_byte_range(&self) -> Range<usize> {
4676        let start = self.class1_count_byte_range().end;
4677        let end = start + u16::RAW_BYTE_LEN;
4678        start..end
4679    }
4680
4681    pub fn class1_records_byte_range(&self) -> Range<usize> {
4682        let class1_count = self.class1_count();
4683        let start = self.class2_count_byte_range().end;
4684        let end = start
4685            + (transforms::to_usize(class1_count)).saturating_mul(
4686                <Class1Record as ComputeSize>::compute_size((
4687                    self.class2_count(),
4688                    self.value_format1(),
4689                    self.value_format2(),
4690                ))
4691                .unwrap_or(0),
4692            );
4693        start..end
4694    }
4695}
4696
4697impl Format<u16> for CursivePosFormat2<'_> {
4698    const FORMAT: u16 = 2;
4699}
4700
4701impl<'a> MinByteRange<'a> for CursivePosFormat2<'a> {
4702    fn min_byte_range(&self) -> Range<usize> {
4703        0..self.entry_exit_record_byte_range().end
4704    }
4705    fn min_table_bytes(&self) -> &'a [u8] {
4706        let range = self.min_byte_range();
4707        self.offset_data().as_bytes().get(range).unwrap_or_default()
4708    }
4709}
4710
4711impl ReadArgs for CursivePosFormat2<'_> {
4712    type Args = ();
4713}
4714
4715impl<'a> FontRead<'a> for CursivePosFormat2<'a> {
4716    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4717        #[allow(clippy::absurd_extreme_comparisons)]
4718        if data.len() < Self::MIN_SIZE {
4719            return Err(ReadError::OutOfBounds);
4720        }
4721        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4722            return Err(ReadError::OutOfBounds);
4723        };
4724        Ok(Self { data })
4725    }
4726}
4727
4728/// The fields at the start of the table, which are read directly.
4729#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4730#[repr(C, packed)]
4731#[doc(hidden)]
4732pub struct CursivePosFormat2FixedHeader {
4733    pub pos_format: BigEndian<u16>,
4734    pub coverage_offset: BigEndian<Offset32>,
4735    pub entry_exit_count: BigEndian<Uint24>,
4736}
4737
4738#[allow(clippy::absurd_extreme_comparisons)]
4739const _: () =
4740    assert!(std::mem::size_of::<CursivePosFormat2FixedHeader>() <= CursivePosFormat2::MIN_SIZE);
4741
4742/// CursivePosFormat2 (ISO/IEC 14496-22:2026).
4743#[derive(Clone)]
4744pub struct CursivePosFormat2<'a> {
4745    data: TableDataRef<'a, CursivePosFormat2FixedHeader>,
4746}
4747
4748#[allow(clippy::needless_lifetimes)]
4749impl<'a> CursivePosFormat2<'a> {
4750    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN + Uint24::RAW_BYTE_LEN);
4751    basic_table_impls!(impl_the_methods);
4752
4753    /// Format identifier: format = 2
4754    pub fn pos_format(&self) -> u16 {
4755        self.data.header().pos_format.get()
4756    }
4757
4758    /// Offset to Coverage table, from beginning of CursivePos subtable.
4759    pub fn coverage_offset(&self) -> Offset32 {
4760        self.data.header().coverage_offset.get()
4761    }
4762
4763    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
4764    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
4765        let data = self.offset_data();
4766        self.coverage_offset().resolve(data)
4767    }
4768
4769    /// Number of EntryExit records
4770    pub fn entry_exit_count(&self) -> Uint24 {
4771        self.data.header().entry_exit_count.get()
4772    }
4773
4774    /// Array of EntryExit records, in Coverage index order.
4775    pub fn entry_exit_record(&self) -> &'a [EntryExitRecord2] {
4776        let range = self.entry_exit_record_byte_range();
4777        self.offset_data()
4778            .read_array(range)
4779            .ok()
4780            .unwrap_or_default()
4781    }
4782
4783    pub fn pos_format_byte_range(&self) -> Range<usize> {
4784        let start = 0;
4785        let end = start + u16::RAW_BYTE_LEN;
4786        start..end
4787    }
4788
4789    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
4790        let start = self.pos_format_byte_range().end;
4791        let end = start + Offset32::RAW_BYTE_LEN;
4792        start..end
4793    }
4794
4795    pub fn entry_exit_count_byte_range(&self) -> Range<usize> {
4796        let start = self.coverage_offset_byte_range().end;
4797        let end = start + Uint24::RAW_BYTE_LEN;
4798        start..end
4799    }
4800
4801    pub fn entry_exit_record_byte_range(&self) -> Range<usize> {
4802        let entry_exit_count = self.entry_exit_count();
4803        let start = self.entry_exit_count_byte_range().end;
4804        let end = start
4805            + (transforms::to_usize(entry_exit_count))
4806                .saturating_mul(EntryExitRecord2::RAW_BYTE_LEN);
4807        start..end
4808    }
4809}
4810
4811/// EntryExitRecord2 (ISO/IEC 14496-22:2026).
4812#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
4813#[repr(C)]
4814#[repr(packed)]
4815pub struct EntryExitRecord2 {
4816    /// Offset to entryAnchor table, from beginning of CursivePos
4817    /// subtable (may be NULL).
4818    pub entry_anchor_offset: BigEndian<Nullable<Offset24>>,
4819    /// Offset to exitAnchor table, from beginning of CursivePos
4820    /// subtable (may be NULL).
4821    pub exit_anchor_offset: BigEndian<Nullable<Offset24>>,
4822}
4823
4824impl EntryExitRecord2 {
4825    /// Offset to entryAnchor table, from beginning of CursivePos
4826    /// subtable (may be NULL).
4827    pub fn entry_anchor_offset(&self) -> Nullable<Offset24> {
4828        self.entry_anchor_offset.get()
4829    }
4830
4831    /// Offset to entryAnchor table, from beginning of CursivePos
4832    /// subtable (may be NULL).
4833    ///
4834    /// The `data` argument should be retrieved from the parent table
4835    /// By calling its `offset_data` method.
4836    pub fn entry_anchor<'a>(
4837        &self,
4838        data: FontData<'a>,
4839    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
4840        self.entry_anchor_offset().resolve(data)
4841    }
4842
4843    /// Offset to exitAnchor table, from beginning of CursivePos
4844    /// subtable (may be NULL).
4845    pub fn exit_anchor_offset(&self) -> Nullable<Offset24> {
4846        self.exit_anchor_offset.get()
4847    }
4848
4849    /// Offset to exitAnchor table, from beginning of CursivePos
4850    /// subtable (may be NULL).
4851    ///
4852    /// The `data` argument should be retrieved from the parent table
4853    /// By calling its `offset_data` method.
4854    pub fn exit_anchor<'a>(
4855        &self,
4856        data: FontData<'a>,
4857    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
4858        self.exit_anchor_offset().resolve(data)
4859    }
4860}
4861
4862impl FixedSize for EntryExitRecord2 {
4863    const RAW_BYTE_LEN: usize = Offset24::RAW_BYTE_LEN + Offset24::RAW_BYTE_LEN;
4864}
4865
4866impl Format<u16> for MarkBasePosFormat2<'_> {
4867    const FORMAT: u16 = 2;
4868}
4869
4870impl<'a> MinByteRange<'a> for MarkBasePosFormat2<'a> {
4871    fn min_byte_range(&self) -> Range<usize> {
4872        0..self.base_array_offset_byte_range().end
4873    }
4874    fn min_table_bytes(&self) -> &'a [u8] {
4875        let range = self.min_byte_range();
4876        self.offset_data().as_bytes().get(range).unwrap_or_default()
4877    }
4878}
4879
4880impl ReadArgs for MarkBasePosFormat2<'_> {
4881    type Args = ();
4882}
4883
4884impl<'a> FontRead<'a> for MarkBasePosFormat2<'a> {
4885    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
4886        #[allow(clippy::absurd_extreme_comparisons)]
4887        if data.len() < Self::MIN_SIZE {
4888            return Err(ReadError::OutOfBounds);
4889        }
4890        let Some(data) = TableDataRef::new(data.as_bytes()) else {
4891            return Err(ReadError::OutOfBounds);
4892        };
4893        Ok(Self { data })
4894    }
4895}
4896
4897/// The fields at the start of the table, which are read directly.
4898#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
4899#[repr(C, packed)]
4900#[doc(hidden)]
4901pub struct MarkBasePosFormat2FixedHeader {
4902    pub pos_format: BigEndian<u16>,
4903    pub mark_coverage_offset: BigEndian<Offset32>,
4904    pub base_coverage_offset: BigEndian<Offset32>,
4905    pub mark_class_count: BigEndian<u16>,
4906    pub mark_array_offset: BigEndian<Offset32>,
4907    pub base_array_offset: BigEndian<Offset32>,
4908}
4909
4910#[allow(clippy::absurd_extreme_comparisons)]
4911const _: () =
4912    assert!(std::mem::size_of::<MarkBasePosFormat2FixedHeader>() <= MarkBasePosFormat2::MIN_SIZE);
4913
4914/// MarkBasePosFormat2 (ISO/IEC 14496-22:2026).
4915#[derive(Clone)]
4916pub struct MarkBasePosFormat2<'a> {
4917    data: TableDataRef<'a, MarkBasePosFormat2FixedHeader>,
4918}
4919
4920#[allow(clippy::needless_lifetimes)]
4921impl<'a> MarkBasePosFormat2<'a> {
4922    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
4923        + Offset32::RAW_BYTE_LEN
4924        + Offset32::RAW_BYTE_LEN
4925        + u16::RAW_BYTE_LEN
4926        + Offset32::RAW_BYTE_LEN
4927        + Offset32::RAW_BYTE_LEN);
4928    basic_table_impls!(impl_the_methods);
4929
4930    /// Format identifier: format = 2
4931    pub fn pos_format(&self) -> u16 {
4932        self.data.header().pos_format.get()
4933    }
4934
4935    /// Offset to markCoverage table, from beginning of MarkBasePos
4936    /// subtable.
4937    pub fn mark_coverage_offset(&self) -> Offset32 {
4938        self.data.header().mark_coverage_offset.get()
4939    }
4940
4941    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
4942    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
4943        let data = self.offset_data();
4944        self.mark_coverage_offset().resolve(data)
4945    }
4946
4947    /// Offset to baseCoverage table, from beginning of MarkBasePos
4948    /// subtable.
4949    pub fn base_coverage_offset(&self) -> Offset32 {
4950        self.data.header().base_coverage_offset.get()
4951    }
4952
4953    /// Attempt to resolve [`base_coverage_offset`][Self::base_coverage_offset].
4954    pub fn base_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
4955        let data = self.offset_data();
4956        self.base_coverage_offset().resolve(data)
4957    }
4958
4959    /// Number of classes defined for marks
4960    pub fn mark_class_count(&self) -> u16 {
4961        self.data.header().mark_class_count.get()
4962    }
4963
4964    /// Offset to MarkArray table, from beginning of MarkBasePos
4965    /// subtable.
4966    pub fn mark_array_offset(&self) -> Offset32 {
4967        self.data.header().mark_array_offset.get()
4968    }
4969
4970    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
4971    pub fn mark_array(&self) -> Result<MarkArray2<'a>, ReadError> {
4972        let data = self.offset_data();
4973        self.mark_array_offset().resolve(data)
4974    }
4975
4976    /// Offset to BaseArray table, from beginning of MarkBasePos
4977    /// subtable.
4978    pub fn base_array_offset(&self) -> Offset32 {
4979        self.data.header().base_array_offset.get()
4980    }
4981
4982    /// Attempt to resolve [`base_array_offset`][Self::base_array_offset].
4983    pub fn base_array(&self) -> Result<BaseArray2<'a>, ReadError> {
4984        let data = self.offset_data();
4985        let args = self.mark_class_count();
4986        self.base_array_offset().resolve_with_args(data, args)
4987    }
4988
4989    pub fn pos_format_byte_range(&self) -> Range<usize> {
4990        let start = 0;
4991        let end = start + u16::RAW_BYTE_LEN;
4992        start..end
4993    }
4994
4995    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
4996        let start = self.pos_format_byte_range().end;
4997        let end = start + Offset32::RAW_BYTE_LEN;
4998        start..end
4999    }
5000
5001    pub fn base_coverage_offset_byte_range(&self) -> Range<usize> {
5002        let start = self.mark_coverage_offset_byte_range().end;
5003        let end = start + Offset32::RAW_BYTE_LEN;
5004        start..end
5005    }
5006
5007    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
5008        let start = self.base_coverage_offset_byte_range().end;
5009        let end = start + u16::RAW_BYTE_LEN;
5010        start..end
5011    }
5012
5013    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
5014        let start = self.mark_class_count_byte_range().end;
5015        let end = start + Offset32::RAW_BYTE_LEN;
5016        start..end
5017    }
5018
5019    pub fn base_array_offset_byte_range(&self) -> Range<usize> {
5020        let start = self.mark_array_offset_byte_range().end;
5021        let end = start + Offset32::RAW_BYTE_LEN;
5022        start..end
5023    }
5024}
5025
5026impl<'a> MinByteRange<'a> for BaseArray2<'a> {
5027    fn min_byte_range(&self) -> Range<usize> {
5028        0..self.base_records_byte_range().end
5029    }
5030    fn min_table_bytes(&self) -> &'a [u8] {
5031        let range = self.min_byte_range();
5032        self.offset_data().as_bytes().get(range).unwrap_or_default()
5033    }
5034}
5035
5036impl ReadArgs for BaseArray2<'_> {
5037    type Args = u16;
5038}
5039
5040impl<'a> FontRead<'a> for BaseArray2<'a> {
5041    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5042        let mark_class_count = args;
5043
5044        #[allow(clippy::absurd_extreme_comparisons)]
5045        if data.len() < Self::MIN_SIZE {
5046            return Err(ReadError::OutOfBounds);
5047        }
5048        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5049            return Err(ReadError::OutOfBounds);
5050        };
5051        Ok(Self {
5052            data,
5053            mark_class_count,
5054        })
5055    }
5056}
5057
5058impl<'a> BaseArray2<'a> {
5059    /// A constructor that requires additional arguments.
5060    ///
5061    /// This type requires some external state in order to be
5062    /// parsed.
5063    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
5064        let args = mark_class_count;
5065        Self::read_with_args(data, args)
5066    }
5067}
5068
5069/// The fields at the start of the table, which are read directly.
5070#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5071#[repr(C, packed)]
5072#[doc(hidden)]
5073pub struct BaseArray2FixedHeader {
5074    pub base_count: BigEndian<Uint24>,
5075}
5076
5077#[allow(clippy::absurd_extreme_comparisons)]
5078const _: () = assert!(std::mem::size_of::<BaseArray2FixedHeader>() <= BaseArray2::MIN_SIZE);
5079
5080/// BaseArray2 (ISO/IEC 14496-22:2026).
5081#[derive(Clone)]
5082pub struct BaseArray2<'a> {
5083    data: TableDataRef<'a, BaseArray2FixedHeader>,
5084    mark_class_count: u16,
5085}
5086
5087#[allow(clippy::needless_lifetimes)]
5088impl<'a> BaseArray2<'a> {
5089    pub const MIN_SIZE: usize = Uint24::RAW_BYTE_LEN;
5090    basic_table_impls!(impl_the_methods);
5091
5092    /// Number of BaseRecords
5093    pub fn base_count(&self) -> Uint24 {
5094        self.data.header().base_count.get()
5095    }
5096
5097    /// Array of BaseRecords, in order of baseCoverage Index.
5098    pub fn base_records(&self) -> ComputedArray<'a, BaseRecord2<'a>> {
5099        let range = self.base_records_byte_range();
5100        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
5101    }
5102
5103    pub(crate) fn mark_class_count(&self) -> u16 {
5104        self.mark_class_count
5105    }
5106
5107    pub fn base_count_byte_range(&self) -> Range<usize> {
5108        let start = 0;
5109        let end = start + Uint24::RAW_BYTE_LEN;
5110        start..end
5111    }
5112
5113    pub fn base_records_byte_range(&self) -> Range<usize> {
5114        let base_count = self.base_count();
5115        let start = self.base_count_byte_range().end;
5116        let end = start
5117            + (transforms::to_usize(base_count)).saturating_mul(
5118                <BaseRecord2 as ComputeSize>::compute_size(self.mark_class_count()).unwrap_or(0),
5119            );
5120        start..end
5121    }
5122}
5123
5124const _: () = assert!(FontData::default_data_long_enough(BaseArray2::MIN_SIZE));
5125
5126impl Default for BaseArray2<'_> {
5127    fn default() -> Self {
5128        Self {
5129            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5130                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5131            mark_class_count: Default::default(),
5132        }
5133    }
5134}
5135
5136/// BaseRecord2 (ISO/IEC 14496-22:2026).
5137#[derive(Clone, Debug)]
5138pub struct BaseRecord2<'a> {
5139    /// Array of offsets (one per mark class) to Anchor tables. Offsets
5140    /// are from beginning of BaseArray table, ordered by class
5141    /// (offsets may be NULL).
5142    pub base_anchor_offsets: &'a [BigEndian<Nullable<Offset24>>],
5143}
5144
5145impl<'a> BaseRecord2<'a> {
5146    /// Array of offsets (one per mark class) to Anchor tables. Offsets
5147    /// are from beginning of BaseArray table, ordered by class
5148    /// (offsets may be NULL).
5149    pub fn base_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
5150        self.base_anchor_offsets
5151    }
5152
5153    /// Array of offsets (one per mark class) to Anchor tables. Offsets
5154    /// are from beginning of BaseArray table, ordered by class
5155    /// (offsets may be NULL).
5156    ///
5157    /// The `data` argument should be retrieved from the parent table
5158    /// By calling its `offset_data` method.
5159    pub fn base_anchors(
5160        &self,
5161        data: FontData<'a>,
5162    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset24> {
5163        let offsets = self.base_anchor_offsets();
5164        ArrayOfNullableOffsets::new(offsets, data, ())
5165    }
5166}
5167
5168impl ReadArgs for BaseRecord2<'_> {
5169    type Args = u16;
5170}
5171
5172impl ComputeSize for BaseRecord2<'_> {
5173    #[allow(clippy::needless_question_mark)]
5174    fn compute_size(args: u16) -> Result<usize, ReadError> {
5175        let mark_class_count = args;
5176        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset24::RAW_BYTE_LEN))
5177    }
5178}
5179
5180impl<'a> FontRead<'a> for BaseRecord2<'a> {
5181    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5182        let mut cursor = data.cursor();
5183        let mark_class_count = args;
5184        Ok(Self {
5185            base_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
5186        })
5187    }
5188}
5189
5190crate::impl_font_read_at!(BaseRecord2<'a>);
5191
5192#[allow(clippy::needless_lifetimes)]
5193impl<'a> BaseRecord2<'a> {
5194    /// A constructor that requires additional arguments.
5195    ///
5196    /// This type requires some external state in order to be
5197    /// parsed.
5198    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
5199        let args = mark_class_count;
5200        Self::read_with_args(data, args)
5201    }
5202}
5203
5204impl Format<u16> for MarkLigPosFormat2<'_> {
5205    const FORMAT: u16 = 2;
5206}
5207
5208impl<'a> MinByteRange<'a> for MarkLigPosFormat2<'a> {
5209    fn min_byte_range(&self) -> Range<usize> {
5210        0..self.ligature_array_offset_byte_range().end
5211    }
5212    fn min_table_bytes(&self) -> &'a [u8] {
5213        let range = self.min_byte_range();
5214        self.offset_data().as_bytes().get(range).unwrap_or_default()
5215    }
5216}
5217
5218impl ReadArgs for MarkLigPosFormat2<'_> {
5219    type Args = ();
5220}
5221
5222impl<'a> FontRead<'a> for MarkLigPosFormat2<'a> {
5223    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5224        #[allow(clippy::absurd_extreme_comparisons)]
5225        if data.len() < Self::MIN_SIZE {
5226            return Err(ReadError::OutOfBounds);
5227        }
5228        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5229            return Err(ReadError::OutOfBounds);
5230        };
5231        Ok(Self { data })
5232    }
5233}
5234
5235/// The fields at the start of the table, which are read directly.
5236#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5237#[repr(C, packed)]
5238#[doc(hidden)]
5239pub struct MarkLigPosFormat2FixedHeader {
5240    pub pos_format: BigEndian<u16>,
5241    pub mark_coverage_offset: BigEndian<Offset32>,
5242    pub ligature_coverage_offset: BigEndian<Offset32>,
5243    pub mark_class_count: BigEndian<u16>,
5244    pub mark_array_offset: BigEndian<Offset32>,
5245    pub ligature_array_offset: BigEndian<Offset32>,
5246}
5247
5248#[allow(clippy::absurd_extreme_comparisons)]
5249const _: () =
5250    assert!(std::mem::size_of::<MarkLigPosFormat2FixedHeader>() <= MarkLigPosFormat2::MIN_SIZE);
5251
5252/// MarkLigPosFormat2 (ISO/IEC 14496-22:2026).
5253#[derive(Clone)]
5254pub struct MarkLigPosFormat2<'a> {
5255    data: TableDataRef<'a, MarkLigPosFormat2FixedHeader>,
5256}
5257
5258#[allow(clippy::needless_lifetimes)]
5259impl<'a> MarkLigPosFormat2<'a> {
5260    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5261        + Offset32::RAW_BYTE_LEN
5262        + Offset32::RAW_BYTE_LEN
5263        + u16::RAW_BYTE_LEN
5264        + Offset32::RAW_BYTE_LEN
5265        + Offset32::RAW_BYTE_LEN);
5266    basic_table_impls!(impl_the_methods);
5267
5268    /// Format identifier: format = 2
5269    pub fn pos_format(&self) -> u16 {
5270        self.data.header().pos_format.get()
5271    }
5272
5273    /// Offset to markCoverage table, from beginning of MarkLigPos
5274    /// subtable.
5275    pub fn mark_coverage_offset(&self) -> Offset32 {
5276        self.data.header().mark_coverage_offset.get()
5277    }
5278
5279    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
5280    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
5281        let data = self.offset_data();
5282        self.mark_coverage_offset().resolve(data)
5283    }
5284
5285    /// Offset to ligatureCoverage table, from beginning of MarkLigPos
5286    /// subtable.
5287    pub fn ligature_coverage_offset(&self) -> Offset32 {
5288        self.data.header().ligature_coverage_offset.get()
5289    }
5290
5291    /// Attempt to resolve [`ligature_coverage_offset`][Self::ligature_coverage_offset].
5292    pub fn ligature_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
5293        let data = self.offset_data();
5294        self.ligature_coverage_offset().resolve(data)
5295    }
5296
5297    /// Number of defined mark classes
5298    pub fn mark_class_count(&self) -> u16 {
5299        self.data.header().mark_class_count.get()
5300    }
5301
5302    /// Offset to MarkArray table, from beginning of MarkLigPos
5303    /// subtable.
5304    pub fn mark_array_offset(&self) -> Offset32 {
5305        self.data.header().mark_array_offset.get()
5306    }
5307
5308    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
5309    pub fn mark_array(&self) -> Result<MarkArray2<'a>, ReadError> {
5310        let data = self.offset_data();
5311        self.mark_array_offset().resolve(data)
5312    }
5313
5314    /// Offset to LigatureArray table, from beginning of MarkLigPos
5315    /// subtable.
5316    pub fn ligature_array_offset(&self) -> Offset32 {
5317        self.data.header().ligature_array_offset.get()
5318    }
5319
5320    /// Attempt to resolve [`ligature_array_offset`][Self::ligature_array_offset].
5321    pub fn ligature_array(&self) -> Result<LigatureArray2<'a>, ReadError> {
5322        let data = self.offset_data();
5323        let args = self.mark_class_count();
5324        self.ligature_array_offset().resolve_with_args(data, args)
5325    }
5326
5327    pub fn pos_format_byte_range(&self) -> Range<usize> {
5328        let start = 0;
5329        let end = start + u16::RAW_BYTE_LEN;
5330        start..end
5331    }
5332
5333    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
5334        let start = self.pos_format_byte_range().end;
5335        let end = start + Offset32::RAW_BYTE_LEN;
5336        start..end
5337    }
5338
5339    pub fn ligature_coverage_offset_byte_range(&self) -> Range<usize> {
5340        let start = self.mark_coverage_offset_byte_range().end;
5341        let end = start + Offset32::RAW_BYTE_LEN;
5342        start..end
5343    }
5344
5345    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
5346        let start = self.ligature_coverage_offset_byte_range().end;
5347        let end = start + u16::RAW_BYTE_LEN;
5348        start..end
5349    }
5350
5351    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
5352        let start = self.mark_class_count_byte_range().end;
5353        let end = start + Offset32::RAW_BYTE_LEN;
5354        start..end
5355    }
5356
5357    pub fn ligature_array_offset_byte_range(&self) -> Range<usize> {
5358        let start = self.mark_array_offset_byte_range().end;
5359        let end = start + Offset32::RAW_BYTE_LEN;
5360        start..end
5361    }
5362}
5363
5364impl<'a> MinByteRange<'a> for LigatureArray2<'a> {
5365    fn min_byte_range(&self) -> Range<usize> {
5366        0..self.ligature_attach_offsets_byte_range().end
5367    }
5368    fn min_table_bytes(&self) -> &'a [u8] {
5369        let range = self.min_byte_range();
5370        self.offset_data().as_bytes().get(range).unwrap_or_default()
5371    }
5372}
5373
5374impl ReadArgs for LigatureArray2<'_> {
5375    type Args = u16;
5376}
5377
5378impl<'a> FontRead<'a> for LigatureArray2<'a> {
5379    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5380        let mark_class_count = args;
5381
5382        #[allow(clippy::absurd_extreme_comparisons)]
5383        if data.len() < Self::MIN_SIZE {
5384            return Err(ReadError::OutOfBounds);
5385        }
5386        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5387            return Err(ReadError::OutOfBounds);
5388        };
5389        Ok(Self {
5390            data,
5391            mark_class_count,
5392        })
5393    }
5394}
5395
5396impl<'a> LigatureArray2<'a> {
5397    /// A constructor that requires additional arguments.
5398    ///
5399    /// This type requires some external state in order to be
5400    /// parsed.
5401    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
5402        let args = mark_class_count;
5403        Self::read_with_args(data, args)
5404    }
5405}
5406
5407/// The fields at the start of the table, which are read directly.
5408#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5409#[repr(C, packed)]
5410#[doc(hidden)]
5411pub struct LigatureArray2FixedHeader {
5412    pub ligature_count: BigEndian<Uint24>,
5413}
5414
5415#[allow(clippy::absurd_extreme_comparisons)]
5416const _: () = assert!(std::mem::size_of::<LigatureArray2FixedHeader>() <= LigatureArray2::MIN_SIZE);
5417
5418/// LigatureArray2 (ISO/IEC 14496-22:2026).
5419#[derive(Clone)]
5420pub struct LigatureArray2<'a> {
5421    data: TableDataRef<'a, LigatureArray2FixedHeader>,
5422    mark_class_count: u16,
5423}
5424
5425#[allow(clippy::needless_lifetimes)]
5426impl<'a> LigatureArray2<'a> {
5427    pub const MIN_SIZE: usize = Uint24::RAW_BYTE_LEN;
5428    basic_table_impls!(impl_the_methods);
5429
5430    /// Number of LigatureAttach table offsets
5431    pub fn ligature_count(&self) -> Uint24 {
5432        self.data.header().ligature_count.get()
5433    }
5434
5435    /// Array of offsets to LigatureAttach tables. Offsets are from
5436    /// beginning of LigatureArray2 table, ordered by ligatureCoverage
5437    /// index.
5438    pub fn ligature_attach_offsets(&self) -> &'a [BigEndian<Offset24>] {
5439        let range = self.ligature_attach_offsets_byte_range();
5440        self.offset_data()
5441            .read_array(range)
5442            .ok()
5443            .unwrap_or_default()
5444    }
5445
5446    /// A dynamically resolving wrapper for [`ligature_attach_offsets`][Self::ligature_attach_offsets].
5447    pub fn ligature_attaches(&self) -> ArrayOfOffsets<'a, LigatureAttach2<'a>, Offset24> {
5448        let data = self.offset_data();
5449        let offsets = self.ligature_attach_offsets();
5450        let args = self.mark_class_count();
5451        ArrayOfOffsets::new(offsets, data, args)
5452    }
5453
5454    pub(crate) fn mark_class_count(&self) -> u16 {
5455        self.mark_class_count
5456    }
5457
5458    pub fn ligature_count_byte_range(&self) -> Range<usize> {
5459        let start = 0;
5460        let end = start + Uint24::RAW_BYTE_LEN;
5461        start..end
5462    }
5463
5464    pub fn ligature_attach_offsets_byte_range(&self) -> Range<usize> {
5465        let ligature_count = self.ligature_count();
5466        let start = self.ligature_count_byte_range().end;
5467        let end =
5468            start + (transforms::to_usize(ligature_count)).saturating_mul(Offset24::RAW_BYTE_LEN);
5469        start..end
5470    }
5471}
5472
5473const _: () = assert!(FontData::default_data_long_enough(LigatureArray2::MIN_SIZE));
5474
5475impl Default for LigatureArray2<'_> {
5476    fn default() -> Self {
5477        Self {
5478            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5479                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5480            mark_class_count: Default::default(),
5481        }
5482    }
5483}
5484
5485impl<'a> MinByteRange<'a> for LigatureAttach2<'a> {
5486    fn min_byte_range(&self) -> Range<usize> {
5487        0..self.component_records_byte_range().end
5488    }
5489    fn min_table_bytes(&self) -> &'a [u8] {
5490        let range = self.min_byte_range();
5491        self.offset_data().as_bytes().get(range).unwrap_or_default()
5492    }
5493}
5494
5495impl ReadArgs for LigatureAttach2<'_> {
5496    type Args = u16;
5497}
5498
5499impl<'a> FontRead<'a> for LigatureAttach2<'a> {
5500    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5501        let mark_class_count = args;
5502
5503        #[allow(clippy::absurd_extreme_comparisons)]
5504        if data.len() < Self::MIN_SIZE {
5505            return Err(ReadError::OutOfBounds);
5506        }
5507        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5508            return Err(ReadError::OutOfBounds);
5509        };
5510        Ok(Self {
5511            data,
5512            mark_class_count,
5513        })
5514    }
5515}
5516
5517impl<'a> LigatureAttach2<'a> {
5518    /// A constructor that requires additional arguments.
5519    ///
5520    /// This type requires some external state in order to be
5521    /// parsed.
5522    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
5523        let args = mark_class_count;
5524        Self::read_with_args(data, args)
5525    }
5526}
5527
5528/// The fields at the start of the table, which are read directly.
5529#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5530#[repr(C, packed)]
5531#[doc(hidden)]
5532pub struct LigatureAttach2FixedHeader {
5533    pub component_count: BigEndian<u16>,
5534}
5535
5536#[allow(clippy::absurd_extreme_comparisons)]
5537const _: () =
5538    assert!(std::mem::size_of::<LigatureAttach2FixedHeader>() <= LigatureAttach2::MIN_SIZE);
5539
5540/// LigatureAttach2 (ISO/IEC 14496-22:2026).
5541#[derive(Clone)]
5542pub struct LigatureAttach2<'a> {
5543    data: TableDataRef<'a, LigatureAttach2FixedHeader>,
5544    mark_class_count: u16,
5545}
5546
5547#[allow(clippy::needless_lifetimes)]
5548impl<'a> LigatureAttach2<'a> {
5549    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
5550    basic_table_impls!(impl_the_methods);
5551
5552    /// Number of ComponentRecords in this ligature
5553    pub fn component_count(&self) -> u16 {
5554        self.data.header().component_count.get()
5555    }
5556
5557    /// Array of Component records, ordered in writing direction.
5558    pub fn component_records(&self) -> ComputedArray<'a, ComponentRecord2<'a>> {
5559        let range = self.component_records_byte_range();
5560        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
5561    }
5562
5563    pub(crate) fn mark_class_count(&self) -> u16 {
5564        self.mark_class_count
5565    }
5566
5567    pub fn component_count_byte_range(&self) -> Range<usize> {
5568        let start = 0;
5569        let end = start + u16::RAW_BYTE_LEN;
5570        start..end
5571    }
5572
5573    pub fn component_records_byte_range(&self) -> Range<usize> {
5574        let component_count = self.component_count();
5575        let start = self.component_count_byte_range().end;
5576        let end = start
5577            + (transforms::to_usize(component_count)).saturating_mul(
5578                <ComponentRecord2 as ComputeSize>::compute_size(self.mark_class_count())
5579                    .unwrap_or(0),
5580            );
5581        start..end
5582    }
5583}
5584
5585const _: () = assert!(FontData::default_data_long_enough(
5586    LigatureAttach2::MIN_SIZE
5587));
5588
5589impl Default for LigatureAttach2<'_> {
5590    fn default() -> Self {
5591        Self {
5592            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5593                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5594            mark_class_count: Default::default(),
5595        }
5596    }
5597}
5598
5599/// ComponentRecord2 (ISO/IEC 14496-22:2026).
5600#[derive(Clone, Debug)]
5601pub struct ComponentRecord2<'a> {
5602    /// Array of offsets (one per class) to Anchor tables. Offsets are
5603    /// from beginning of LigatureAttach table, ordered by class
5604    /// (offsets may be NULL).
5605    pub ligature_anchor_offsets: &'a [BigEndian<Nullable<Offset24>>],
5606}
5607
5608impl<'a> ComponentRecord2<'a> {
5609    /// Array of offsets (one per class) to Anchor tables. Offsets are
5610    /// from beginning of LigatureAttach table, ordered by class
5611    /// (offsets may be NULL).
5612    pub fn ligature_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
5613        self.ligature_anchor_offsets
5614    }
5615
5616    /// Array of offsets (one per class) to Anchor tables. Offsets are
5617    /// from beginning of LigatureAttach table, ordered by class
5618    /// (offsets may be NULL).
5619    ///
5620    /// The `data` argument should be retrieved from the parent table
5621    /// By calling its `offset_data` method.
5622    pub fn ligature_anchors(
5623        &self,
5624        data: FontData<'a>,
5625    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset24> {
5626        let offsets = self.ligature_anchor_offsets();
5627        ArrayOfNullableOffsets::new(offsets, data, ())
5628    }
5629}
5630
5631impl ReadArgs for ComponentRecord2<'_> {
5632    type Args = u16;
5633}
5634
5635impl ComputeSize for ComponentRecord2<'_> {
5636    #[allow(clippy::needless_question_mark)]
5637    fn compute_size(args: u16) -> Result<usize, ReadError> {
5638        let mark_class_count = args;
5639        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset24::RAW_BYTE_LEN))
5640    }
5641}
5642
5643impl<'a> FontRead<'a> for ComponentRecord2<'a> {
5644    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5645        let mut cursor = data.cursor();
5646        let mark_class_count = args;
5647        Ok(Self {
5648            ligature_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
5649        })
5650    }
5651}
5652
5653crate::impl_font_read_at!(ComponentRecord2<'a>);
5654
5655#[allow(clippy::needless_lifetimes)]
5656impl<'a> ComponentRecord2<'a> {
5657    /// A constructor that requires additional arguments.
5658    ///
5659    /// This type requires some external state in order to be
5660    /// parsed.
5661    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
5662        let args = mark_class_count;
5663        Self::read_with_args(data, args)
5664    }
5665}
5666
5667impl Format<u16> for MarkMarkPosFormat2<'_> {
5668    const FORMAT: u16 = 2;
5669}
5670
5671impl<'a> MinByteRange<'a> for MarkMarkPosFormat2<'a> {
5672    fn min_byte_range(&self) -> Range<usize> {
5673        0..self.mark2_array_offset_byte_range().end
5674    }
5675    fn min_table_bytes(&self) -> &'a [u8] {
5676        let range = self.min_byte_range();
5677        self.offset_data().as_bytes().get(range).unwrap_or_default()
5678    }
5679}
5680
5681impl ReadArgs for MarkMarkPosFormat2<'_> {
5682    type Args = ();
5683}
5684
5685impl<'a> FontRead<'a> for MarkMarkPosFormat2<'a> {
5686    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
5687        #[allow(clippy::absurd_extreme_comparisons)]
5688        if data.len() < Self::MIN_SIZE {
5689            return Err(ReadError::OutOfBounds);
5690        }
5691        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5692            return Err(ReadError::OutOfBounds);
5693        };
5694        Ok(Self { data })
5695    }
5696}
5697
5698/// The fields at the start of the table, which are read directly.
5699#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5700#[repr(C, packed)]
5701#[doc(hidden)]
5702pub struct MarkMarkPosFormat2FixedHeader {
5703    pub pos_format: BigEndian<u16>,
5704    pub mark1_coverage_offset: BigEndian<Offset32>,
5705    pub mark2_coverage_offset: BigEndian<Offset32>,
5706    pub mark_class_count: BigEndian<u16>,
5707    pub mark1_array_offset: BigEndian<Offset32>,
5708    pub mark2_array_offset: BigEndian<Offset32>,
5709}
5710
5711#[allow(clippy::absurd_extreme_comparisons)]
5712const _: () =
5713    assert!(std::mem::size_of::<MarkMarkPosFormat2FixedHeader>() <= MarkMarkPosFormat2::MIN_SIZE);
5714
5715/// MarkMarkPosFormat2 (ISO/IEC 14496-22:2026).
5716#[derive(Clone)]
5717pub struct MarkMarkPosFormat2<'a> {
5718    data: TableDataRef<'a, MarkMarkPosFormat2FixedHeader>,
5719}
5720
5721#[allow(clippy::needless_lifetimes)]
5722impl<'a> MarkMarkPosFormat2<'a> {
5723    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
5724        + Offset32::RAW_BYTE_LEN
5725        + Offset32::RAW_BYTE_LEN
5726        + u16::RAW_BYTE_LEN
5727        + Offset32::RAW_BYTE_LEN
5728        + Offset32::RAW_BYTE_LEN);
5729    basic_table_impls!(impl_the_methods);
5730
5731    /// Format identifier: format = 2
5732    pub fn pos_format(&self) -> u16 {
5733        self.data.header().pos_format.get()
5734    }
5735
5736    /// Offset to Combining Mark Coverage table, from beginning of
5737    /// MarkMarkPos subtable.
5738    pub fn mark1_coverage_offset(&self) -> Offset32 {
5739        self.data.header().mark1_coverage_offset.get()
5740    }
5741
5742    /// Attempt to resolve [`mark1_coverage_offset`][Self::mark1_coverage_offset].
5743    pub fn mark1_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
5744        let data = self.offset_data();
5745        self.mark1_coverage_offset().resolve(data)
5746    }
5747
5748    /// Offset to Base Mark Coverage table, from beginning of
5749    /// MarkMarkPos subtable.
5750    pub fn mark2_coverage_offset(&self) -> Offset32 {
5751        self.data.header().mark2_coverage_offset.get()
5752    }
5753
5754    /// Attempt to resolve [`mark2_coverage_offset`][Self::mark2_coverage_offset].
5755    pub fn mark2_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
5756        let data = self.offset_data();
5757        self.mark2_coverage_offset().resolve(data)
5758    }
5759
5760    /// Number of Combining Mark classes defined
5761    pub fn mark_class_count(&self) -> u16 {
5762        self.data.header().mark_class_count.get()
5763    }
5764
5765    /// Offset to MarkArray table for mark1, from beginning of
5766    /// MarkMarkPos subtable.
5767    pub fn mark1_array_offset(&self) -> Offset32 {
5768        self.data.header().mark1_array_offset.get()
5769    }
5770
5771    /// Attempt to resolve [`mark1_array_offset`][Self::mark1_array_offset].
5772    pub fn mark1_array(&self) -> Result<MarkArray2<'a>, ReadError> {
5773        let data = self.offset_data();
5774        self.mark1_array_offset().resolve(data)
5775    }
5776
5777    /// Offset to Mark2Array table for mark2, from beginning of
5778    /// MarkMarkPos subtable.
5779    pub fn mark2_array_offset(&self) -> Offset32 {
5780        self.data.header().mark2_array_offset.get()
5781    }
5782
5783    /// Attempt to resolve [`mark2_array_offset`][Self::mark2_array_offset].
5784    pub fn mark2_array(&self) -> Result<Mark2Array2<'a>, ReadError> {
5785        let data = self.offset_data();
5786        let args = self.mark_class_count();
5787        self.mark2_array_offset().resolve_with_args(data, args)
5788    }
5789
5790    pub fn pos_format_byte_range(&self) -> Range<usize> {
5791        let start = 0;
5792        let end = start + u16::RAW_BYTE_LEN;
5793        start..end
5794    }
5795
5796    pub fn mark1_coverage_offset_byte_range(&self) -> Range<usize> {
5797        let start = self.pos_format_byte_range().end;
5798        let end = start + Offset32::RAW_BYTE_LEN;
5799        start..end
5800    }
5801
5802    pub fn mark2_coverage_offset_byte_range(&self) -> Range<usize> {
5803        let start = self.mark1_coverage_offset_byte_range().end;
5804        let end = start + Offset32::RAW_BYTE_LEN;
5805        start..end
5806    }
5807
5808    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
5809        let start = self.mark2_coverage_offset_byte_range().end;
5810        let end = start + u16::RAW_BYTE_LEN;
5811        start..end
5812    }
5813
5814    pub fn mark1_array_offset_byte_range(&self) -> Range<usize> {
5815        let start = self.mark_class_count_byte_range().end;
5816        let end = start + Offset32::RAW_BYTE_LEN;
5817        start..end
5818    }
5819
5820    pub fn mark2_array_offset_byte_range(&self) -> Range<usize> {
5821        let start = self.mark1_array_offset_byte_range().end;
5822        let end = start + Offset32::RAW_BYTE_LEN;
5823        start..end
5824    }
5825}
5826
5827impl<'a> MinByteRange<'a> for Mark2Array2<'a> {
5828    fn min_byte_range(&self) -> Range<usize> {
5829        0..self.mark2_records_byte_range().end
5830    }
5831    fn min_table_bytes(&self) -> &'a [u8] {
5832        let range = self.min_byte_range();
5833        self.offset_data().as_bytes().get(range).unwrap_or_default()
5834    }
5835}
5836
5837impl ReadArgs for Mark2Array2<'_> {
5838    type Args = u16;
5839}
5840
5841impl<'a> FontRead<'a> for Mark2Array2<'a> {
5842    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5843        let mark_class_count = args;
5844
5845        #[allow(clippy::absurd_extreme_comparisons)]
5846        if data.len() < Self::MIN_SIZE {
5847            return Err(ReadError::OutOfBounds);
5848        }
5849        let Some(data) = TableDataRef::new(data.as_bytes()) else {
5850            return Err(ReadError::OutOfBounds);
5851        };
5852        Ok(Self {
5853            data,
5854            mark_class_count,
5855        })
5856    }
5857}
5858
5859impl<'a> Mark2Array2<'a> {
5860    /// A constructor that requires additional arguments.
5861    ///
5862    /// This type requires some external state in order to be
5863    /// parsed.
5864    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
5865        let args = mark_class_count;
5866        Self::read_with_args(data, args)
5867    }
5868}
5869
5870/// The fields at the start of the table, which are read directly.
5871#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
5872#[repr(C, packed)]
5873#[doc(hidden)]
5874pub struct Mark2Array2FixedHeader {
5875    pub mark2_count: BigEndian<u16>,
5876}
5877
5878#[allow(clippy::absurd_extreme_comparisons)]
5879const _: () = assert!(std::mem::size_of::<Mark2Array2FixedHeader>() <= Mark2Array2::MIN_SIZE);
5880
5881/// Mark2Array2 (ISO/IEC 14496-22:2026).
5882#[derive(Clone)]
5883pub struct Mark2Array2<'a> {
5884    data: TableDataRef<'a, Mark2Array2FixedHeader>,
5885    mark_class_count: u16,
5886}
5887
5888#[allow(clippy::needless_lifetimes)]
5889impl<'a> Mark2Array2<'a> {
5890    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
5891    basic_table_impls!(impl_the_methods);
5892
5893    /// Number of Mark2 records
5894    pub fn mark2_count(&self) -> u16 {
5895        self.data.header().mark2_count.get()
5896    }
5897
5898    /// Array of Mark2Records, in Coverage order.
5899    pub fn mark2_records(&self) -> ComputedArray<'a, Mark2Record2<'a>> {
5900        let range = self.mark2_records_byte_range();
5901        ComputedArray::new(self.offset_data(), range, self.mark_class_count()).unwrap_or_default()
5902    }
5903
5904    pub(crate) fn mark_class_count(&self) -> u16 {
5905        self.mark_class_count
5906    }
5907
5908    pub fn mark2_count_byte_range(&self) -> Range<usize> {
5909        let start = 0;
5910        let end = start + u16::RAW_BYTE_LEN;
5911        start..end
5912    }
5913
5914    pub fn mark2_records_byte_range(&self) -> Range<usize> {
5915        let mark2_count = self.mark2_count();
5916        let start = self.mark2_count_byte_range().end;
5917        let end = start
5918            + (transforms::to_usize(mark2_count)).saturating_mul(
5919                <Mark2Record2 as ComputeSize>::compute_size(self.mark_class_count()).unwrap_or(0),
5920            );
5921        start..end
5922    }
5923}
5924
5925const _: () = assert!(FontData::default_data_long_enough(Mark2Array2::MIN_SIZE));
5926
5927impl Default for Mark2Array2<'_> {
5928    fn default() -> Self {
5929        Self {
5930            data: TableDataRef::new(FontData::default_table_data().as_bytes())
5931                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
5932            mark_class_count: Default::default(),
5933        }
5934    }
5935}
5936
5937/// Mark2Record2 (ISO/IEC 14496-22:2026).
5938#[derive(Clone, Debug)]
5939pub struct Mark2Record2<'a> {
5940    /// Array of offsets (one per class) to Anchor tables. Offsets are
5941    /// from beginning of Mark2Array table, in class order (offsets may
5942    /// be NULL).
5943    pub mark2_anchor_offsets: &'a [BigEndian<Nullable<Offset24>>],
5944}
5945
5946impl<'a> Mark2Record2<'a> {
5947    /// Array of offsets (one per class) to Anchor tables. Offsets are
5948    /// from beginning of Mark2Array table, in class order (offsets may
5949    /// be NULL).
5950    pub fn mark2_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset24>>] {
5951        self.mark2_anchor_offsets
5952    }
5953
5954    /// Array of offsets (one per class) to Anchor tables. Offsets are
5955    /// from beginning of Mark2Array table, in class order (offsets may
5956    /// be NULL).
5957    ///
5958    /// The `data` argument should be retrieved from the parent table
5959    /// By calling its `offset_data` method.
5960    pub fn mark2_anchors(
5961        &self,
5962        data: FontData<'a>,
5963    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset24> {
5964        let offsets = self.mark2_anchor_offsets();
5965        ArrayOfNullableOffsets::new(offsets, data, ())
5966    }
5967}
5968
5969impl ReadArgs for Mark2Record2<'_> {
5970    type Args = u16;
5971}
5972
5973impl ComputeSize for Mark2Record2<'_> {
5974    #[allow(clippy::needless_question_mark)]
5975    fn compute_size(args: u16) -> Result<usize, ReadError> {
5976        let mark_class_count = args;
5977        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset24::RAW_BYTE_LEN))
5978    }
5979}
5980
5981impl<'a> FontRead<'a> for Mark2Record2<'a> {
5982    fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
5983        let mut cursor = data.cursor();
5984        let mark_class_count = args;
5985        Ok(Self {
5986            mark2_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
5987        })
5988    }
5989}
5990
5991crate::impl_font_read_at!(Mark2Record2<'a>);
5992
5993#[allow(clippy::needless_lifetimes)]
5994impl<'a> Mark2Record2<'a> {
5995    /// A constructor that requires additional arguments.
5996    ///
5997    /// This type requires some external state in order to be
5998    /// parsed.
5999    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
6000        let args = mark_class_count;
6001        Self::read_with_args(data, args)
6002    }
6003}
6004
6005impl<'a> MinByteRange<'a> for MarkArray2<'a> {
6006    fn min_byte_range(&self) -> Range<usize> {
6007        0..self.mark_records_byte_range().end
6008    }
6009    fn min_table_bytes(&self) -> &'a [u8] {
6010        let range = self.min_byte_range();
6011        self.offset_data().as_bytes().get(range).unwrap_or_default()
6012    }
6013}
6014
6015impl ReadArgs for MarkArray2<'_> {
6016    type Args = ();
6017}
6018
6019impl<'a> FontRead<'a> for MarkArray2<'a> {
6020    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6021        #[allow(clippy::absurd_extreme_comparisons)]
6022        if data.len() < Self::MIN_SIZE {
6023            return Err(ReadError::OutOfBounds);
6024        }
6025        let Some(data) = TableDataRef::new(data.as_bytes()) else {
6026            return Err(ReadError::OutOfBounds);
6027        };
6028        Ok(Self { data })
6029    }
6030}
6031
6032/// The fields at the start of the table, which are read directly.
6033#[derive(Clone, Copy, bytemuck :: AnyBitPattern)]
6034#[repr(C, packed)]
6035#[doc(hidden)]
6036pub struct MarkArray2FixedHeader {
6037    pub mark_count: BigEndian<Uint24>,
6038}
6039
6040#[allow(clippy::absurd_extreme_comparisons)]
6041const _: () = assert!(std::mem::size_of::<MarkArray2FixedHeader>() <= MarkArray2::MIN_SIZE);
6042
6043/// MarkArray2 (ISO/IEC 14496-22:2026).
6044#[derive(Clone)]
6045pub struct MarkArray2<'a> {
6046    data: TableDataRef<'a, MarkArray2FixedHeader>,
6047}
6048
6049#[allow(clippy::needless_lifetimes)]
6050impl<'a> MarkArray2<'a> {
6051    pub const MIN_SIZE: usize = Uint24::RAW_BYTE_LEN;
6052    basic_table_impls!(impl_the_methods);
6053
6054    /// Number of MarkRecords
6055    pub fn mark_count(&self) -> Uint24 {
6056        self.data.header().mark_count.get()
6057    }
6058
6059    /// Array of MarkRecords, ordered by corresponding glyphs in the
6060    /// associated mark Coverage table.
6061    pub fn mark_records(&self) -> &'a [MarkRecord2] {
6062        let range = self.mark_records_byte_range();
6063        self.offset_data()
6064            .read_array(range)
6065            .ok()
6066            .unwrap_or_default()
6067    }
6068
6069    pub fn mark_count_byte_range(&self) -> Range<usize> {
6070        let start = 0;
6071        let end = start + Uint24::RAW_BYTE_LEN;
6072        start..end
6073    }
6074
6075    pub fn mark_records_byte_range(&self) -> Range<usize> {
6076        let mark_count = self.mark_count();
6077        let start = self.mark_count_byte_range().end;
6078        let end =
6079            start + (transforms::to_usize(mark_count)).saturating_mul(MarkRecord2::RAW_BYTE_LEN);
6080        start..end
6081    }
6082}
6083
6084const _: () = assert!(FontData::default_data_long_enough(MarkArray2::MIN_SIZE));
6085
6086impl Default for MarkArray2<'_> {
6087    fn default() -> Self {
6088        Self {
6089            data: TableDataRef::new(FontData::default_table_data().as_bytes())
6090                .unwrap_or_else(|| unreachable!("the default data covers MIN_SIZE")),
6091        }
6092    }
6093}
6094
6095/// MarkRecord2 (ISO/IEC 14496-22:2026).
6096#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
6097#[repr(C)]
6098#[repr(packed)]
6099pub struct MarkRecord2 {
6100    /// Class defined for the associated mark.
6101    pub mark_class: BigEndian<u16>,
6102    /// Offset to Anchor table, from beginning of MarkArray table.
6103    pub mark_anchor_offset: BigEndian<Offset24>,
6104}
6105
6106impl MarkRecord2 {
6107    /// Class defined for the associated mark.
6108    pub fn mark_class(&self) -> u16 {
6109        self.mark_class.get()
6110    }
6111
6112    /// Offset to Anchor table, from beginning of MarkArray table.
6113    pub fn mark_anchor_offset(&self) -> Offset24 {
6114        self.mark_anchor_offset.get()
6115    }
6116
6117    /// Offset to Anchor table, from beginning of MarkArray table.
6118    ///
6119    /// The `data` argument should be retrieved from the parent table
6120    /// By calling its `offset_data` method.
6121    pub fn mark_anchor<'a>(&self, data: FontData<'a>) -> Result<AnchorTable<'a>, ReadError> {
6122        self.mark_anchor_offset().resolve(data)
6123    }
6124}
6125
6126impl FixedSize for MarkRecord2 {
6127    const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset24::RAW_BYTE_LEN;
6128}
6129
6130/// CursivePos (ISO/IEC 14496-22:2026).
6131#[derive(Clone)]
6132pub enum CursivePos<'a> {
6133    Format1(CursivePosFormat1<'a>),
6134    Format2(CursivePosFormat2<'a>),
6135}
6136
6137impl Default for CursivePos<'_> {
6138    fn default() -> Self {
6139        Self::Format1(Default::default())
6140    }
6141}
6142
6143impl<'a> CursivePos<'a> {
6144    ///Return the `FontData` used to resolve offsets for this table.
6145    pub fn offset_data(&self) -> FontData<'a> {
6146        match self {
6147            Self::Format1(item) => item.offset_data(),
6148            Self::Format2(item) => item.offset_data(),
6149        }
6150    }
6151
6152    /// Format identifier: format = 1
6153    pub fn pos_format(&self) -> u16 {
6154        match self {
6155            Self::Format1(item) => item.pos_format(),
6156            Self::Format2(item) => item.pos_format(),
6157        }
6158    }
6159}
6160
6161impl ReadArgs for CursivePos<'_> {
6162    type Args = ();
6163}
6164
6165impl<'a> FontRead<'a> for CursivePos<'a> {
6166    #[inline]
6167    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6168        let format: u16 = data.read_at(0usize)?;
6169        match format {
6170            CursivePosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
6171            CursivePosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
6172            other => Err(ReadError::InvalidFormat(other.into())),
6173        }
6174    }
6175}
6176
6177impl<'a> MinByteRange<'a> for CursivePos<'a> {
6178    fn min_byte_range(&self) -> Range<usize> {
6179        match self {
6180            Self::Format1(item) => item.min_byte_range(),
6181            Self::Format2(item) => item.min_byte_range(),
6182        }
6183    }
6184    fn min_table_bytes(&self) -> &'a [u8] {
6185        match self {
6186            Self::Format1(item) => item.min_table_bytes(),
6187            Self::Format2(item) => item.min_table_bytes(),
6188        }
6189    }
6190}
6191
6192/// MarkBasePos (ISO/IEC 14496-22:2026).
6193#[derive(Clone)]
6194pub enum MarkBasePos<'a> {
6195    Format1(MarkBasePosFormat1<'a>),
6196    Format2(MarkBasePosFormat2<'a>),
6197}
6198
6199impl Default for MarkBasePos<'_> {
6200    fn default() -> Self {
6201        Self::Format1(Default::default())
6202    }
6203}
6204
6205impl<'a> MarkBasePos<'a> {
6206    ///Return the `FontData` used to resolve offsets for this table.
6207    pub fn offset_data(&self) -> FontData<'a> {
6208        match self {
6209            Self::Format1(item) => item.offset_data(),
6210            Self::Format2(item) => item.offset_data(),
6211        }
6212    }
6213
6214    /// Format identifier: format = 1
6215    pub fn pos_format(&self) -> u16 {
6216        match self {
6217            Self::Format1(item) => item.pos_format(),
6218            Self::Format2(item) => item.pos_format(),
6219        }
6220    }
6221
6222    /// Number of classes defined for marks
6223    pub fn mark_class_count(&self) -> u16 {
6224        match self {
6225            Self::Format1(item) => item.mark_class_count(),
6226            Self::Format2(item) => item.mark_class_count(),
6227        }
6228    }
6229}
6230
6231impl ReadArgs for MarkBasePos<'_> {
6232    type Args = ();
6233}
6234
6235impl<'a> FontRead<'a> for MarkBasePos<'a> {
6236    #[inline]
6237    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6238        let format: u16 = data.read_at(0usize)?;
6239        match format {
6240            MarkBasePosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
6241            MarkBasePosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
6242            other => Err(ReadError::InvalidFormat(other.into())),
6243        }
6244    }
6245}
6246
6247impl<'a> MinByteRange<'a> for MarkBasePos<'a> {
6248    fn min_byte_range(&self) -> Range<usize> {
6249        match self {
6250            Self::Format1(item) => item.min_byte_range(),
6251            Self::Format2(item) => item.min_byte_range(),
6252        }
6253    }
6254    fn min_table_bytes(&self) -> &'a [u8] {
6255        match self {
6256            Self::Format1(item) => item.min_table_bytes(),
6257            Self::Format2(item) => item.min_table_bytes(),
6258        }
6259    }
6260}
6261
6262/// MarkLigPos (ISO/IEC 14496-22:2026).
6263#[derive(Clone)]
6264pub enum MarkLigPos<'a> {
6265    Format1(MarkLigPosFormat1<'a>),
6266    Format2(MarkLigPosFormat2<'a>),
6267}
6268
6269impl Default for MarkLigPos<'_> {
6270    fn default() -> Self {
6271        Self::Format1(Default::default())
6272    }
6273}
6274
6275impl<'a> MarkLigPos<'a> {
6276    ///Return the `FontData` used to resolve offsets for this table.
6277    pub fn offset_data(&self) -> FontData<'a> {
6278        match self {
6279            Self::Format1(item) => item.offset_data(),
6280            Self::Format2(item) => item.offset_data(),
6281        }
6282    }
6283
6284    /// Format identifier: format = 1
6285    pub fn pos_format(&self) -> u16 {
6286        match self {
6287            Self::Format1(item) => item.pos_format(),
6288            Self::Format2(item) => item.pos_format(),
6289        }
6290    }
6291
6292    /// Number of defined mark classes
6293    pub fn mark_class_count(&self) -> u16 {
6294        match self {
6295            Self::Format1(item) => item.mark_class_count(),
6296            Self::Format2(item) => item.mark_class_count(),
6297        }
6298    }
6299}
6300
6301impl ReadArgs for MarkLigPos<'_> {
6302    type Args = ();
6303}
6304
6305impl<'a> FontRead<'a> for MarkLigPos<'a> {
6306    #[inline]
6307    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6308        let format: u16 = data.read_at(0usize)?;
6309        match format {
6310            MarkLigPosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
6311            MarkLigPosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
6312            other => Err(ReadError::InvalidFormat(other.into())),
6313        }
6314    }
6315}
6316
6317impl<'a> MinByteRange<'a> for MarkLigPos<'a> {
6318    fn min_byte_range(&self) -> Range<usize> {
6319        match self {
6320            Self::Format1(item) => item.min_byte_range(),
6321            Self::Format2(item) => item.min_byte_range(),
6322        }
6323    }
6324    fn min_table_bytes(&self) -> &'a [u8] {
6325        match self {
6326            Self::Format1(item) => item.min_table_bytes(),
6327            Self::Format2(item) => item.min_table_bytes(),
6328        }
6329    }
6330}
6331
6332/// MarkMarkPos (ISO/IEC 14496-22:2026).
6333#[derive(Clone)]
6334pub enum MarkMarkPos<'a> {
6335    Format1(MarkMarkPosFormat1<'a>),
6336    Format2(MarkMarkPosFormat2<'a>),
6337}
6338
6339impl Default for MarkMarkPos<'_> {
6340    fn default() -> Self {
6341        Self::Format1(Default::default())
6342    }
6343}
6344
6345impl<'a> MarkMarkPos<'a> {
6346    ///Return the `FontData` used to resolve offsets for this table.
6347    pub fn offset_data(&self) -> FontData<'a> {
6348        match self {
6349            Self::Format1(item) => item.offset_data(),
6350            Self::Format2(item) => item.offset_data(),
6351        }
6352    }
6353
6354    /// Format identifier: format = 1
6355    pub fn pos_format(&self) -> u16 {
6356        match self {
6357            Self::Format1(item) => item.pos_format(),
6358            Self::Format2(item) => item.pos_format(),
6359        }
6360    }
6361
6362    /// Number of Combining Mark classes defined
6363    pub fn mark_class_count(&self) -> u16 {
6364        match self {
6365            Self::Format1(item) => item.mark_class_count(),
6366            Self::Format2(item) => item.mark_class_count(),
6367        }
6368    }
6369}
6370
6371impl ReadArgs for MarkMarkPos<'_> {
6372    type Args = ();
6373}
6374
6375impl<'a> FontRead<'a> for MarkMarkPos<'a> {
6376    #[inline]
6377    fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
6378        let format: u16 = data.read_at(0usize)?;
6379        match format {
6380            MarkMarkPosFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
6381            MarkMarkPosFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
6382            other => Err(ReadError::InvalidFormat(other.into())),
6383        }
6384    }
6385}
6386
6387impl<'a> MinByteRange<'a> for MarkMarkPos<'a> {
6388    fn min_byte_range(&self) -> Range<usize> {
6389        match self {
6390            Self::Format1(item) => item.min_byte_range(),
6391            Self::Format2(item) => item.min_byte_range(),
6392        }
6393    }
6394    fn min_table_bytes(&self) -> &'a [u8] {
6395        match self {
6396            Self::Format1(item) => item.min_table_bytes(),
6397            Self::Format2(item) => item.min_table_bytes(),
6398        }
6399    }
6400}