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