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 + (transforms::to_usize(mark_count)).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                + (transforms::to_usize(value_count)).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 + (transforms::to_usize(pair_set_count)).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                + (transforms::to_usize(pair_value_count)).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                + (transforms::to_usize(class1_count)).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((transforms::to_usize(class2_count)).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.read_computed_array(
2390                transforms::to_usize(class2_count),
2391                &(value_format1, value_format2),
2392            )?,
2393        })
2394    }
2395}
2396
2397#[allow(clippy::needless_lifetimes)]
2398impl<'a> Class1Record<'a> {
2399    /// A constructor that requires additional arguments.
2400    ///
2401    /// This type requires some external state in order to be
2402    /// parsed.
2403    pub fn read(
2404        data: FontData<'a>,
2405        class2_count: u16,
2406        value_format1: ValueFormat,
2407        value_format2: ValueFormat,
2408    ) -> Result<Self, ReadError> {
2409        let args = (class2_count, value_format1, value_format2);
2410        Self::read_with_args(data, &args)
2411    }
2412}
2413
2414#[cfg(feature = "experimental_traverse")]
2415impl<'a> SomeRecord<'a> for Class1Record<'a> {
2416    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
2417        RecordResolver {
2418            name: "Class1Record",
2419            get_field: Box::new(move |idx, _data| match idx {
2420                0usize => Some(Field::new(
2421                    "class2_records",
2422                    traversal::FieldType::computed_array(
2423                        "Class2Record",
2424                        self.class2_records().clone(),
2425                        FontData::new(&[]),
2426                    ),
2427                )),
2428                _ => None,
2429            }),
2430            data,
2431        }
2432    }
2433}
2434
2435/// Part of [PairPosFormat2]
2436#[derive(Clone, Debug)]
2437pub struct Class2Record {
2438    /// Positioning for first glyph — empty if valueFormat1 = 0.
2439    pub value_record1: ValueRecord,
2440    /// Positioning for second glyph — empty if valueFormat2 = 0.
2441    pub value_record2: ValueRecord,
2442}
2443
2444impl Class2Record {
2445    /// Positioning for first glyph — empty if valueFormat1 = 0.
2446    pub fn value_record1(&self) -> &ValueRecord {
2447        &self.value_record1
2448    }
2449
2450    /// Positioning for second glyph — empty if valueFormat2 = 0.
2451    pub fn value_record2(&self) -> &ValueRecord {
2452        &self.value_record2
2453    }
2454}
2455
2456impl ReadArgs for Class2Record {
2457    type Args = (ValueFormat, ValueFormat);
2458}
2459
2460impl ComputeSize for Class2Record {
2461    #[allow(clippy::needless_question_mark)]
2462    fn compute_size(args: &(ValueFormat, ValueFormat)) -> Result<usize, ReadError> {
2463        let (value_format1, value_format2) = *args;
2464        let mut result = 0usize;
2465        result = result
2466            .checked_add(<ValueRecord as ComputeSize>::compute_size(&value_format1).unwrap_or(0))
2467            .ok_or(ReadError::OutOfBounds)?;
2468        result = result
2469            .checked_add(<ValueRecord as ComputeSize>::compute_size(&value_format2).unwrap_or(0))
2470            .ok_or(ReadError::OutOfBounds)?;
2471        Ok(result)
2472    }
2473}
2474
2475impl<'a> FontReadWithArgs<'a> for Class2Record {
2476    fn read_with_args(
2477        data: FontData<'a>,
2478        args: &(ValueFormat, ValueFormat),
2479    ) -> Result<Self, ReadError> {
2480        let mut cursor = data.cursor();
2481        let (value_format1, value_format2) = *args;
2482        Ok(Self {
2483            value_record1: cursor.read_with_args(&value_format1)?,
2484            value_record2: cursor.read_with_args(&value_format2)?,
2485        })
2486    }
2487}
2488
2489#[allow(clippy::needless_lifetimes)]
2490impl<'a> Class2Record {
2491    /// A constructor that requires additional arguments.
2492    ///
2493    /// This type requires some external state in order to be
2494    /// parsed.
2495    pub fn read(
2496        data: FontData<'a>,
2497        value_format1: ValueFormat,
2498        value_format2: ValueFormat,
2499    ) -> Result<Self, ReadError> {
2500        let args = (value_format1, value_format2);
2501        Self::read_with_args(data, &args)
2502    }
2503}
2504
2505#[cfg(feature = "experimental_traverse")]
2506impl<'a> SomeRecord<'a> for Class2Record {
2507    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
2508        RecordResolver {
2509            name: "Class2Record",
2510            get_field: Box::new(move |idx, _data| match idx {
2511                0usize => Some(Field::new(
2512                    "value_record1",
2513                    self.value_record1().traversal_type(_data),
2514                )),
2515                1usize => Some(Field::new(
2516                    "value_record2",
2517                    self.value_record2().traversal_type(_data),
2518                )),
2519                _ => None,
2520            }),
2521            data,
2522        }
2523    }
2524}
2525
2526impl Format<u16> for CursivePosFormat1<'_> {
2527    const FORMAT: u16 = 1;
2528}
2529
2530impl<'a> MinByteRange<'a> for CursivePosFormat1<'a> {
2531    fn min_byte_range(&self) -> Range<usize> {
2532        0..self.entry_exit_record_byte_range().end
2533    }
2534    fn min_table_bytes(&self) -> &'a [u8] {
2535        let range = self.min_byte_range();
2536        self.data.as_bytes().get(range).unwrap_or_default()
2537    }
2538}
2539
2540impl<'a> FontRead<'a> for CursivePosFormat1<'a> {
2541    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2542        #[allow(clippy::absurd_extreme_comparisons)]
2543        if data.len() < Self::MIN_SIZE {
2544            return Err(ReadError::OutOfBounds);
2545        }
2546        Ok(Self { data })
2547    }
2548}
2549
2550/// [Cursive Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#cursive-attachment-positioning-format1-cursive-attachment): Cursvie attachment
2551#[derive(Clone)]
2552pub struct CursivePosFormat1<'a> {
2553    data: FontData<'a>,
2554}
2555
2556#[allow(clippy::needless_lifetimes)]
2557impl<'a> CursivePosFormat1<'a> {
2558    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
2559    basic_table_impls!(impl_the_methods);
2560
2561    /// Format identifier: format = 1
2562    pub fn pos_format(&self) -> u16 {
2563        let range = self.pos_format_byte_range();
2564        self.data.read_at(range.start).ok().unwrap()
2565    }
2566
2567    /// Offset to Coverage table, from beginning of CursivePos subtable.
2568    pub fn coverage_offset(&self) -> Offset16 {
2569        let range = self.coverage_offset_byte_range();
2570        self.data.read_at(range.start).ok().unwrap()
2571    }
2572
2573    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
2574    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2575        let data = self.data;
2576        self.coverage_offset().resolve(data)
2577    }
2578
2579    /// Number of EntryExit records
2580    pub fn entry_exit_count(&self) -> u16 {
2581        let range = self.entry_exit_count_byte_range();
2582        self.data.read_at(range.start).ok().unwrap()
2583    }
2584
2585    /// Array of EntryExit records, in Coverage index order.
2586    pub fn entry_exit_record(&self) -> &'a [EntryExitRecord] {
2587        let range = self.entry_exit_record_byte_range();
2588        self.data.read_array(range).ok().unwrap_or_default()
2589    }
2590
2591    pub fn pos_format_byte_range(&self) -> Range<usize> {
2592        let start = 0;
2593        start..start + u16::RAW_BYTE_LEN
2594    }
2595
2596    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
2597        let start = self.pos_format_byte_range().end;
2598        start..start + Offset16::RAW_BYTE_LEN
2599    }
2600
2601    pub fn entry_exit_count_byte_range(&self) -> Range<usize> {
2602        let start = self.coverage_offset_byte_range().end;
2603        start..start + u16::RAW_BYTE_LEN
2604    }
2605
2606    pub fn entry_exit_record_byte_range(&self) -> Range<usize> {
2607        let entry_exit_count = self.entry_exit_count();
2608        let start = self.entry_exit_count_byte_range().end;
2609        start
2610            ..start
2611                + (transforms::to_usize(entry_exit_count))
2612                    .saturating_mul(EntryExitRecord::RAW_BYTE_LEN)
2613    }
2614}
2615
2616const _: () = assert!(FontData::default_data_long_enough(
2617    CursivePosFormat1::MIN_SIZE
2618));
2619
2620impl Default for CursivePosFormat1<'_> {
2621    fn default() -> Self {
2622        Self {
2623            data: FontData::default_format_1_u16_table_data(),
2624        }
2625    }
2626}
2627
2628#[cfg(feature = "experimental_traverse")]
2629impl<'a> SomeTable<'a> for CursivePosFormat1<'a> {
2630    fn type_name(&self) -> &str {
2631        "CursivePosFormat1"
2632    }
2633    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2634        match idx {
2635            0usize => Some(Field::new("pos_format", self.pos_format())),
2636            1usize => Some(Field::new(
2637                "coverage_offset",
2638                FieldType::offset(self.coverage_offset(), self.coverage()),
2639            )),
2640            2usize => Some(Field::new("entry_exit_count", self.entry_exit_count())),
2641            3usize => Some(Field::new(
2642                "entry_exit_record",
2643                traversal::FieldType::array_of_records(
2644                    stringify!(EntryExitRecord),
2645                    self.entry_exit_record(),
2646                    self.offset_data(),
2647                ),
2648            )),
2649            _ => None,
2650        }
2651    }
2652}
2653
2654#[cfg(feature = "experimental_traverse")]
2655#[allow(clippy::needless_lifetimes)]
2656impl<'a> std::fmt::Debug for CursivePosFormat1<'a> {
2657    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2658        (self as &dyn SomeTable<'a>).fmt(f)
2659    }
2660}
2661
2662/// Part of [CursivePosFormat1]
2663#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
2664#[repr(C)]
2665#[repr(packed)]
2666pub struct EntryExitRecord {
2667    /// Offset to entryAnchor table, from beginning of CursivePos
2668    /// subtable (may be NULL).
2669    pub entry_anchor_offset: BigEndian<Nullable<Offset16>>,
2670    /// Offset to exitAnchor table, from beginning of CursivePos
2671    /// subtable (may be NULL).
2672    pub exit_anchor_offset: BigEndian<Nullable<Offset16>>,
2673}
2674
2675impl EntryExitRecord {
2676    /// Offset to entryAnchor table, from beginning of CursivePos
2677    /// subtable (may be NULL).
2678    pub fn entry_anchor_offset(&self) -> Nullable<Offset16> {
2679        self.entry_anchor_offset.get()
2680    }
2681
2682    /// Offset to entryAnchor table, from beginning of CursivePos
2683    /// subtable (may be NULL).
2684    ///
2685    /// The `data` argument should be retrieved from the parent table
2686    /// By calling its `offset_data` method.
2687    pub fn entry_anchor<'a>(
2688        &self,
2689        data: FontData<'a>,
2690    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
2691        self.entry_anchor_offset().resolve(data)
2692    }
2693
2694    /// Offset to exitAnchor table, from beginning of CursivePos
2695    /// subtable (may be NULL).
2696    pub fn exit_anchor_offset(&self) -> Nullable<Offset16> {
2697        self.exit_anchor_offset.get()
2698    }
2699
2700    /// Offset to exitAnchor table, from beginning of CursivePos
2701    /// subtable (may be NULL).
2702    ///
2703    /// The `data` argument should be retrieved from the parent table
2704    /// By calling its `offset_data` method.
2705    pub fn exit_anchor<'a>(
2706        &self,
2707        data: FontData<'a>,
2708    ) -> Option<Result<AnchorTable<'a>, ReadError>> {
2709        self.exit_anchor_offset().resolve(data)
2710    }
2711}
2712
2713impl FixedSize for EntryExitRecord {
2714    const RAW_BYTE_LEN: usize = Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
2715}
2716
2717#[cfg(feature = "experimental_traverse")]
2718impl<'a> SomeRecord<'a> for EntryExitRecord {
2719    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
2720        RecordResolver {
2721            name: "EntryExitRecord",
2722            get_field: Box::new(move |idx, _data| match idx {
2723                0usize => Some(Field::new(
2724                    "entry_anchor_offset",
2725                    FieldType::offset(self.entry_anchor_offset(), self.entry_anchor(_data)),
2726                )),
2727                1usize => Some(Field::new(
2728                    "exit_anchor_offset",
2729                    FieldType::offset(self.exit_anchor_offset(), self.exit_anchor(_data)),
2730                )),
2731                _ => None,
2732            }),
2733            data,
2734        }
2735    }
2736}
2737
2738impl Format<u16> for MarkBasePosFormat1<'_> {
2739    const FORMAT: u16 = 1;
2740}
2741
2742impl<'a> MinByteRange<'a> for MarkBasePosFormat1<'a> {
2743    fn min_byte_range(&self) -> Range<usize> {
2744        0..self.base_array_offset_byte_range().end
2745    }
2746    fn min_table_bytes(&self) -> &'a [u8] {
2747        let range = self.min_byte_range();
2748        self.data.as_bytes().get(range).unwrap_or_default()
2749    }
2750}
2751
2752impl<'a> FontRead<'a> for MarkBasePosFormat1<'a> {
2753    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2754        #[allow(clippy::absurd_extreme_comparisons)]
2755        if data.len() < Self::MIN_SIZE {
2756            return Err(ReadError::OutOfBounds);
2757        }
2758        Ok(Self { data })
2759    }
2760}
2761
2762/// [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
2763#[derive(Clone)]
2764pub struct MarkBasePosFormat1<'a> {
2765    data: FontData<'a>,
2766}
2767
2768#[allow(clippy::needless_lifetimes)]
2769impl<'a> MarkBasePosFormat1<'a> {
2770    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
2771        + Offset16::RAW_BYTE_LEN
2772        + Offset16::RAW_BYTE_LEN
2773        + u16::RAW_BYTE_LEN
2774        + Offset16::RAW_BYTE_LEN
2775        + Offset16::RAW_BYTE_LEN);
2776    basic_table_impls!(impl_the_methods);
2777
2778    /// Format identifier: format = 1
2779    pub fn pos_format(&self) -> u16 {
2780        let range = self.pos_format_byte_range();
2781        self.data.read_at(range.start).ok().unwrap()
2782    }
2783
2784    /// Offset to markCoverage table, from beginning of MarkBasePos
2785    /// subtable.
2786    pub fn mark_coverage_offset(&self) -> Offset16 {
2787        let range = self.mark_coverage_offset_byte_range();
2788        self.data.read_at(range.start).ok().unwrap()
2789    }
2790
2791    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
2792    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2793        let data = self.data;
2794        self.mark_coverage_offset().resolve(data)
2795    }
2796
2797    /// Offset to baseCoverage table, from beginning of MarkBasePos
2798    /// subtable.
2799    pub fn base_coverage_offset(&self) -> Offset16 {
2800        let range = self.base_coverage_offset_byte_range();
2801        self.data.read_at(range.start).ok().unwrap()
2802    }
2803
2804    /// Attempt to resolve [`base_coverage_offset`][Self::base_coverage_offset].
2805    pub fn base_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
2806        let data = self.data;
2807        self.base_coverage_offset().resolve(data)
2808    }
2809
2810    /// Number of classes defined for marks
2811    pub fn mark_class_count(&self) -> u16 {
2812        let range = self.mark_class_count_byte_range();
2813        self.data.read_at(range.start).ok().unwrap()
2814    }
2815
2816    /// Offset to MarkArray table, from beginning of MarkBasePos
2817    /// subtable.
2818    pub fn mark_array_offset(&self) -> Offset16 {
2819        let range = self.mark_array_offset_byte_range();
2820        self.data.read_at(range.start).ok().unwrap()
2821    }
2822
2823    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
2824    pub fn mark_array(&self) -> Result<MarkArray<'a>, ReadError> {
2825        let data = self.data;
2826        self.mark_array_offset().resolve(data)
2827    }
2828
2829    /// Offset to BaseArray table, from beginning of MarkBasePos
2830    /// subtable.
2831    pub fn base_array_offset(&self) -> Offset16 {
2832        let range = self.base_array_offset_byte_range();
2833        self.data.read_at(range.start).ok().unwrap()
2834    }
2835
2836    /// Attempt to resolve [`base_array_offset`][Self::base_array_offset].
2837    pub fn base_array(&self) -> Result<BaseArray<'a>, ReadError> {
2838        let data = self.data;
2839        let args = self.mark_class_count();
2840        self.base_array_offset().resolve_with_args(data, &args)
2841    }
2842
2843    pub fn pos_format_byte_range(&self) -> Range<usize> {
2844        let start = 0;
2845        start..start + u16::RAW_BYTE_LEN
2846    }
2847
2848    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
2849        let start = self.pos_format_byte_range().end;
2850        start..start + Offset16::RAW_BYTE_LEN
2851    }
2852
2853    pub fn base_coverage_offset_byte_range(&self) -> Range<usize> {
2854        let start = self.mark_coverage_offset_byte_range().end;
2855        start..start + Offset16::RAW_BYTE_LEN
2856    }
2857
2858    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
2859        let start = self.base_coverage_offset_byte_range().end;
2860        start..start + u16::RAW_BYTE_LEN
2861    }
2862
2863    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
2864        let start = self.mark_class_count_byte_range().end;
2865        start..start + Offset16::RAW_BYTE_LEN
2866    }
2867
2868    pub fn base_array_offset_byte_range(&self) -> Range<usize> {
2869        let start = self.mark_array_offset_byte_range().end;
2870        start..start + Offset16::RAW_BYTE_LEN
2871    }
2872}
2873
2874const _: () = assert!(FontData::default_data_long_enough(
2875    MarkBasePosFormat1::MIN_SIZE
2876));
2877
2878impl Default for MarkBasePosFormat1<'_> {
2879    fn default() -> Self {
2880        Self {
2881            data: FontData::default_format_1_u16_table_data(),
2882        }
2883    }
2884}
2885
2886#[cfg(feature = "experimental_traverse")]
2887impl<'a> SomeTable<'a> for MarkBasePosFormat1<'a> {
2888    fn type_name(&self) -> &str {
2889        "MarkBasePosFormat1"
2890    }
2891    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
2892        match idx {
2893            0usize => Some(Field::new("pos_format", self.pos_format())),
2894            1usize => Some(Field::new(
2895                "mark_coverage_offset",
2896                FieldType::offset(self.mark_coverage_offset(), self.mark_coverage()),
2897            )),
2898            2usize => Some(Field::new(
2899                "base_coverage_offset",
2900                FieldType::offset(self.base_coverage_offset(), self.base_coverage()),
2901            )),
2902            3usize => Some(Field::new("mark_class_count", self.mark_class_count())),
2903            4usize => Some(Field::new(
2904                "mark_array_offset",
2905                FieldType::offset(self.mark_array_offset(), self.mark_array()),
2906            )),
2907            5usize => Some(Field::new(
2908                "base_array_offset",
2909                FieldType::offset(self.base_array_offset(), self.base_array()),
2910            )),
2911            _ => None,
2912        }
2913    }
2914}
2915
2916#[cfg(feature = "experimental_traverse")]
2917#[allow(clippy::needless_lifetimes)]
2918impl<'a> std::fmt::Debug for MarkBasePosFormat1<'a> {
2919    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2920        (self as &dyn SomeTable<'a>).fmt(f)
2921    }
2922}
2923
2924impl<'a> MinByteRange<'a> for BaseArray<'a> {
2925    fn min_byte_range(&self) -> Range<usize> {
2926        0..self.base_records_byte_range().end
2927    }
2928    fn min_table_bytes(&self) -> &'a [u8] {
2929        let range = self.min_byte_range();
2930        self.data.as_bytes().get(range).unwrap_or_default()
2931    }
2932}
2933
2934impl ReadArgs for BaseArray<'_> {
2935    type Args = u16;
2936}
2937
2938impl<'a> FontReadWithArgs<'a> for BaseArray<'a> {
2939    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
2940        let mark_class_count = *args;
2941
2942        #[allow(clippy::absurd_extreme_comparisons)]
2943        if data.len() < Self::MIN_SIZE {
2944            return Err(ReadError::OutOfBounds);
2945        }
2946        Ok(Self {
2947            data,
2948            mark_class_count,
2949        })
2950    }
2951}
2952
2953impl<'a> BaseArray<'a> {
2954    /// A constructor that requires additional arguments.
2955    ///
2956    /// This type requires some external state in order to be
2957    /// parsed.
2958    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
2959        let args = mark_class_count;
2960        Self::read_with_args(data, &args)
2961    }
2962}
2963
2964/// Part of [MarkBasePosFormat1]
2965#[derive(Clone)]
2966pub struct BaseArray<'a> {
2967    data: FontData<'a>,
2968    mark_class_count: u16,
2969}
2970
2971#[allow(clippy::needless_lifetimes)]
2972impl<'a> BaseArray<'a> {
2973    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
2974    basic_table_impls!(impl_the_methods);
2975
2976    /// Number of BaseRecords
2977    pub fn base_count(&self) -> u16 {
2978        let range = self.base_count_byte_range();
2979        self.data.read_at(range.start).ok().unwrap()
2980    }
2981
2982    /// Array of BaseRecords, in order of baseCoverage Index.
2983    pub fn base_records(&self) -> ComputedArray<'a, BaseRecord<'a>> {
2984        let range = self.base_records_byte_range();
2985        self.data
2986            .read_with_args(range, &self.mark_class_count())
2987            .unwrap_or_default()
2988    }
2989
2990    pub(crate) fn mark_class_count(&self) -> u16 {
2991        self.mark_class_count
2992    }
2993
2994    pub fn base_count_byte_range(&self) -> Range<usize> {
2995        let start = 0;
2996        start..start + u16::RAW_BYTE_LEN
2997    }
2998
2999    pub fn base_records_byte_range(&self) -> Range<usize> {
3000        let base_count = self.base_count();
3001        let start = self.base_count_byte_range().end;
3002        start
3003            ..start
3004                + (transforms::to_usize(base_count)).saturating_mul(
3005                    <BaseRecord as ComputeSize>::compute_size(&self.mark_class_count())
3006                        .unwrap_or(0),
3007                )
3008    }
3009}
3010
3011const _: () = assert!(FontData::default_data_long_enough(BaseArray::MIN_SIZE));
3012
3013impl Default for BaseArray<'_> {
3014    fn default() -> Self {
3015        Self {
3016            data: FontData::default_table_data(),
3017            mark_class_count: Default::default(),
3018        }
3019    }
3020}
3021
3022#[cfg(feature = "experimental_traverse")]
3023impl<'a> SomeTable<'a> for BaseArray<'a> {
3024    fn type_name(&self) -> &str {
3025        "BaseArray"
3026    }
3027    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3028        match idx {
3029            0usize => Some(Field::new("base_count", self.base_count())),
3030            1usize => Some(Field::new(
3031                "base_records",
3032                traversal::FieldType::computed_array(
3033                    "BaseRecord",
3034                    self.base_records(),
3035                    self.offset_data(),
3036                ),
3037            )),
3038            _ => None,
3039        }
3040    }
3041}
3042
3043#[cfg(feature = "experimental_traverse")]
3044#[allow(clippy::needless_lifetimes)]
3045impl<'a> std::fmt::Debug for BaseArray<'a> {
3046    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3047        (self as &dyn SomeTable<'a>).fmt(f)
3048    }
3049}
3050
3051/// Part of [BaseArray]
3052#[derive(Clone, Debug)]
3053pub struct BaseRecord<'a> {
3054    /// Array of offsets (one per mark class) to Anchor tables. Offsets
3055    /// are from beginning of BaseArray table, ordered by class
3056    /// (offsets may be NULL).
3057    pub base_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3058}
3059
3060impl<'a> BaseRecord<'a> {
3061    /// Array of offsets (one per mark class) to Anchor tables. Offsets
3062    /// are from beginning of BaseArray table, ordered by class
3063    /// (offsets may be NULL).
3064    pub fn base_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3065        self.base_anchor_offsets
3066    }
3067
3068    /// Array of offsets (one per mark class) to Anchor tables. Offsets
3069    /// are from beginning of BaseArray table, ordered by class
3070    /// (offsets may be NULL).
3071    ///
3072    /// The `data` argument should be retrieved from the parent table
3073    /// By calling its `offset_data` method.
3074    pub fn base_anchors(
3075        &self,
3076        data: FontData<'a>,
3077    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3078        let offsets = self.base_anchor_offsets();
3079        ArrayOfNullableOffsets::new(offsets, data, ())
3080    }
3081}
3082
3083impl ReadArgs for BaseRecord<'_> {
3084    type Args = u16;
3085}
3086
3087impl ComputeSize for BaseRecord<'_> {
3088    #[allow(clippy::needless_question_mark)]
3089    fn compute_size(args: &u16) -> Result<usize, ReadError> {
3090        let mark_class_count = *args;
3091        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
3092    }
3093}
3094
3095impl<'a> FontReadWithArgs<'a> for BaseRecord<'a> {
3096    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
3097        let mut cursor = data.cursor();
3098        let mark_class_count = *args;
3099        Ok(Self {
3100            base_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
3101        })
3102    }
3103}
3104
3105#[allow(clippy::needless_lifetimes)]
3106impl<'a> BaseRecord<'a> {
3107    /// A constructor that requires additional arguments.
3108    ///
3109    /// This type requires some external state in order to be
3110    /// parsed.
3111    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3112        let args = mark_class_count;
3113        Self::read_with_args(data, &args)
3114    }
3115}
3116
3117#[cfg(feature = "experimental_traverse")]
3118impl<'a> SomeRecord<'a> for BaseRecord<'a> {
3119    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
3120        RecordResolver {
3121            name: "BaseRecord",
3122            get_field: Box::new(move |idx, _data| match idx {
3123                0usize => Some(Field::new(
3124                    "base_anchor_offsets",
3125                    FieldType::from(self.base_anchors(_data)),
3126                )),
3127                _ => None,
3128            }),
3129            data,
3130        }
3131    }
3132}
3133
3134impl Format<u16> for MarkLigPosFormat1<'_> {
3135    const FORMAT: u16 = 1;
3136}
3137
3138impl<'a> MinByteRange<'a> for MarkLigPosFormat1<'a> {
3139    fn min_byte_range(&self) -> Range<usize> {
3140        0..self.ligature_array_offset_byte_range().end
3141    }
3142    fn min_table_bytes(&self) -> &'a [u8] {
3143        let range = self.min_byte_range();
3144        self.data.as_bytes().get(range).unwrap_or_default()
3145    }
3146}
3147
3148impl<'a> FontRead<'a> for MarkLigPosFormat1<'a> {
3149    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3150        #[allow(clippy::absurd_extreme_comparisons)]
3151        if data.len() < Self::MIN_SIZE {
3152            return Err(ReadError::OutOfBounds);
3153        }
3154        Ok(Self { data })
3155    }
3156}
3157
3158/// [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
3159#[derive(Clone)]
3160pub struct MarkLigPosFormat1<'a> {
3161    data: FontData<'a>,
3162}
3163
3164#[allow(clippy::needless_lifetimes)]
3165impl<'a> MarkLigPosFormat1<'a> {
3166    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3167        + Offset16::RAW_BYTE_LEN
3168        + Offset16::RAW_BYTE_LEN
3169        + u16::RAW_BYTE_LEN
3170        + Offset16::RAW_BYTE_LEN
3171        + Offset16::RAW_BYTE_LEN);
3172    basic_table_impls!(impl_the_methods);
3173
3174    /// Format identifier: format = 1
3175    pub fn pos_format(&self) -> u16 {
3176        let range = self.pos_format_byte_range();
3177        self.data.read_at(range.start).ok().unwrap()
3178    }
3179
3180    /// Offset to markCoverage table, from beginning of MarkLigPos
3181    /// subtable.
3182    pub fn mark_coverage_offset(&self) -> Offset16 {
3183        let range = self.mark_coverage_offset_byte_range();
3184        self.data.read_at(range.start).ok().unwrap()
3185    }
3186
3187    /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset].
3188    pub fn mark_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3189        let data = self.data;
3190        self.mark_coverage_offset().resolve(data)
3191    }
3192
3193    /// Offset to ligatureCoverage table, from beginning of MarkLigPos
3194    /// subtable.
3195    pub fn ligature_coverage_offset(&self) -> Offset16 {
3196        let range = self.ligature_coverage_offset_byte_range();
3197        self.data.read_at(range.start).ok().unwrap()
3198    }
3199
3200    /// Attempt to resolve [`ligature_coverage_offset`][Self::ligature_coverage_offset].
3201    pub fn ligature_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3202        let data = self.data;
3203        self.ligature_coverage_offset().resolve(data)
3204    }
3205
3206    /// Number of defined mark classes
3207    pub fn mark_class_count(&self) -> u16 {
3208        let range = self.mark_class_count_byte_range();
3209        self.data.read_at(range.start).ok().unwrap()
3210    }
3211
3212    /// Offset to MarkArray table, from beginning of MarkLigPos
3213    /// subtable.
3214    pub fn mark_array_offset(&self) -> Offset16 {
3215        let range = self.mark_array_offset_byte_range();
3216        self.data.read_at(range.start).ok().unwrap()
3217    }
3218
3219    /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset].
3220    pub fn mark_array(&self) -> Result<MarkArray<'a>, ReadError> {
3221        let data = self.data;
3222        self.mark_array_offset().resolve(data)
3223    }
3224
3225    /// Offset to LigatureArray table, from beginning of MarkLigPos
3226    /// subtable.
3227    pub fn ligature_array_offset(&self) -> Offset16 {
3228        let range = self.ligature_array_offset_byte_range();
3229        self.data.read_at(range.start).ok().unwrap()
3230    }
3231
3232    /// Attempt to resolve [`ligature_array_offset`][Self::ligature_array_offset].
3233    pub fn ligature_array(&self) -> Result<LigatureArray<'a>, ReadError> {
3234        let data = self.data;
3235        let args = self.mark_class_count();
3236        self.ligature_array_offset().resolve_with_args(data, &args)
3237    }
3238
3239    pub fn pos_format_byte_range(&self) -> Range<usize> {
3240        let start = 0;
3241        start..start + u16::RAW_BYTE_LEN
3242    }
3243
3244    pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> {
3245        let start = self.pos_format_byte_range().end;
3246        start..start + Offset16::RAW_BYTE_LEN
3247    }
3248
3249    pub fn ligature_coverage_offset_byte_range(&self) -> Range<usize> {
3250        let start = self.mark_coverage_offset_byte_range().end;
3251        start..start + Offset16::RAW_BYTE_LEN
3252    }
3253
3254    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
3255        let start = self.ligature_coverage_offset_byte_range().end;
3256        start..start + u16::RAW_BYTE_LEN
3257    }
3258
3259    pub fn mark_array_offset_byte_range(&self) -> Range<usize> {
3260        let start = self.mark_class_count_byte_range().end;
3261        start..start + Offset16::RAW_BYTE_LEN
3262    }
3263
3264    pub fn ligature_array_offset_byte_range(&self) -> Range<usize> {
3265        let start = self.mark_array_offset_byte_range().end;
3266        start..start + Offset16::RAW_BYTE_LEN
3267    }
3268}
3269
3270const _: () = assert!(FontData::default_data_long_enough(
3271    MarkLigPosFormat1::MIN_SIZE
3272));
3273
3274impl Default for MarkLigPosFormat1<'_> {
3275    fn default() -> Self {
3276        Self {
3277            data: FontData::default_format_1_u16_table_data(),
3278        }
3279    }
3280}
3281
3282#[cfg(feature = "experimental_traverse")]
3283impl<'a> SomeTable<'a> for MarkLigPosFormat1<'a> {
3284    fn type_name(&self) -> &str {
3285        "MarkLigPosFormat1"
3286    }
3287    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3288        match idx {
3289            0usize => Some(Field::new("pos_format", self.pos_format())),
3290            1usize => Some(Field::new(
3291                "mark_coverage_offset",
3292                FieldType::offset(self.mark_coverage_offset(), self.mark_coverage()),
3293            )),
3294            2usize => Some(Field::new(
3295                "ligature_coverage_offset",
3296                FieldType::offset(self.ligature_coverage_offset(), self.ligature_coverage()),
3297            )),
3298            3usize => Some(Field::new("mark_class_count", self.mark_class_count())),
3299            4usize => Some(Field::new(
3300                "mark_array_offset",
3301                FieldType::offset(self.mark_array_offset(), self.mark_array()),
3302            )),
3303            5usize => Some(Field::new(
3304                "ligature_array_offset",
3305                FieldType::offset(self.ligature_array_offset(), self.ligature_array()),
3306            )),
3307            _ => None,
3308        }
3309    }
3310}
3311
3312#[cfg(feature = "experimental_traverse")]
3313#[allow(clippy::needless_lifetimes)]
3314impl<'a> std::fmt::Debug for MarkLigPosFormat1<'a> {
3315    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3316        (self as &dyn SomeTable<'a>).fmt(f)
3317    }
3318}
3319
3320impl<'a> MinByteRange<'a> for LigatureArray<'a> {
3321    fn min_byte_range(&self) -> Range<usize> {
3322        0..self.ligature_attach_offsets_byte_range().end
3323    }
3324    fn min_table_bytes(&self) -> &'a [u8] {
3325        let range = self.min_byte_range();
3326        self.data.as_bytes().get(range).unwrap_or_default()
3327    }
3328}
3329
3330impl ReadArgs for LigatureArray<'_> {
3331    type Args = u16;
3332}
3333
3334impl<'a> FontReadWithArgs<'a> for LigatureArray<'a> {
3335    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
3336        let mark_class_count = *args;
3337
3338        #[allow(clippy::absurd_extreme_comparisons)]
3339        if data.len() < Self::MIN_SIZE {
3340            return Err(ReadError::OutOfBounds);
3341        }
3342        Ok(Self {
3343            data,
3344            mark_class_count,
3345        })
3346    }
3347}
3348
3349impl<'a> LigatureArray<'a> {
3350    /// A constructor that requires additional arguments.
3351    ///
3352    /// This type requires some external state in order to be
3353    /// parsed.
3354    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3355        let args = mark_class_count;
3356        Self::read_with_args(data, &args)
3357    }
3358}
3359
3360/// Part of [MarkLigPosFormat1]
3361#[derive(Clone)]
3362pub struct LigatureArray<'a> {
3363    data: FontData<'a>,
3364    mark_class_count: u16,
3365}
3366
3367#[allow(clippy::needless_lifetimes)]
3368impl<'a> LigatureArray<'a> {
3369    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3370    basic_table_impls!(impl_the_methods);
3371
3372    /// Number of LigatureAttach table offsets
3373    pub fn ligature_count(&self) -> u16 {
3374        let range = self.ligature_count_byte_range();
3375        self.data.read_at(range.start).ok().unwrap()
3376    }
3377
3378    /// Array of offsets to LigatureAttach tables. Offsets are from
3379    /// beginning of LigatureArray table, ordered by ligatureCoverage
3380    /// index.
3381    pub fn ligature_attach_offsets(&self) -> &'a [BigEndian<Offset16>] {
3382        let range = self.ligature_attach_offsets_byte_range();
3383        self.data.read_array(range).ok().unwrap_or_default()
3384    }
3385
3386    /// A dynamically resolving wrapper for [`ligature_attach_offsets`][Self::ligature_attach_offsets].
3387    pub fn ligature_attaches(&self) -> ArrayOfOffsets<'a, LigatureAttach<'a>, Offset16> {
3388        let data = self.data;
3389        let offsets = self.ligature_attach_offsets();
3390        let args = self.mark_class_count();
3391        ArrayOfOffsets::new(offsets, data, args)
3392    }
3393
3394    pub(crate) fn mark_class_count(&self) -> u16 {
3395        self.mark_class_count
3396    }
3397
3398    pub fn ligature_count_byte_range(&self) -> Range<usize> {
3399        let start = 0;
3400        start..start + u16::RAW_BYTE_LEN
3401    }
3402
3403    pub fn ligature_attach_offsets_byte_range(&self) -> Range<usize> {
3404        let ligature_count = self.ligature_count();
3405        let start = self.ligature_count_byte_range().end;
3406        start..start + (transforms::to_usize(ligature_count)).saturating_mul(Offset16::RAW_BYTE_LEN)
3407    }
3408}
3409
3410const _: () = assert!(FontData::default_data_long_enough(LigatureArray::MIN_SIZE));
3411
3412impl Default for LigatureArray<'_> {
3413    fn default() -> Self {
3414        Self {
3415            data: FontData::default_table_data(),
3416            mark_class_count: Default::default(),
3417        }
3418    }
3419}
3420
3421#[cfg(feature = "experimental_traverse")]
3422impl<'a> SomeTable<'a> for LigatureArray<'a> {
3423    fn type_name(&self) -> &str {
3424        "LigatureArray"
3425    }
3426    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3427        match idx {
3428            0usize => Some(Field::new("ligature_count", self.ligature_count())),
3429            1usize => Some(Field::new(
3430                "ligature_attach_offsets",
3431                FieldType::from(self.ligature_attaches()),
3432            )),
3433            _ => None,
3434        }
3435    }
3436}
3437
3438#[cfg(feature = "experimental_traverse")]
3439#[allow(clippy::needless_lifetimes)]
3440impl<'a> std::fmt::Debug for LigatureArray<'a> {
3441    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3442        (self as &dyn SomeTable<'a>).fmt(f)
3443    }
3444}
3445
3446impl<'a> MinByteRange<'a> for LigatureAttach<'a> {
3447    fn min_byte_range(&self) -> Range<usize> {
3448        0..self.component_records_byte_range().end
3449    }
3450    fn min_table_bytes(&self) -> &'a [u8] {
3451        let range = self.min_byte_range();
3452        self.data.as_bytes().get(range).unwrap_or_default()
3453    }
3454}
3455
3456impl ReadArgs for LigatureAttach<'_> {
3457    type Args = u16;
3458}
3459
3460impl<'a> FontReadWithArgs<'a> for LigatureAttach<'a> {
3461    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
3462        let mark_class_count = *args;
3463
3464        #[allow(clippy::absurd_extreme_comparisons)]
3465        if data.len() < Self::MIN_SIZE {
3466            return Err(ReadError::OutOfBounds);
3467        }
3468        Ok(Self {
3469            data,
3470            mark_class_count,
3471        })
3472    }
3473}
3474
3475impl<'a> LigatureAttach<'a> {
3476    /// A constructor that requires additional arguments.
3477    ///
3478    /// This type requires some external state in order to be
3479    /// parsed.
3480    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3481        let args = mark_class_count;
3482        Self::read_with_args(data, &args)
3483    }
3484}
3485
3486/// Part of [MarkLigPosFormat1]
3487#[derive(Clone)]
3488pub struct LigatureAttach<'a> {
3489    data: FontData<'a>,
3490    mark_class_count: u16,
3491}
3492
3493#[allow(clippy::needless_lifetimes)]
3494impl<'a> LigatureAttach<'a> {
3495    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3496    basic_table_impls!(impl_the_methods);
3497
3498    /// Number of ComponentRecords in this ligature
3499    pub fn component_count(&self) -> u16 {
3500        let range = self.component_count_byte_range();
3501        self.data.read_at(range.start).ok().unwrap()
3502    }
3503
3504    /// Array of Component records, ordered in writing direction.
3505    pub fn component_records(&self) -> ComputedArray<'a, ComponentRecord<'a>> {
3506        let range = self.component_records_byte_range();
3507        self.data
3508            .read_with_args(range, &self.mark_class_count())
3509            .unwrap_or_default()
3510    }
3511
3512    pub(crate) fn mark_class_count(&self) -> u16 {
3513        self.mark_class_count
3514    }
3515
3516    pub fn component_count_byte_range(&self) -> Range<usize> {
3517        let start = 0;
3518        start..start + u16::RAW_BYTE_LEN
3519    }
3520
3521    pub fn component_records_byte_range(&self) -> Range<usize> {
3522        let component_count = self.component_count();
3523        let start = self.component_count_byte_range().end;
3524        start
3525            ..start
3526                + (transforms::to_usize(component_count)).saturating_mul(
3527                    <ComponentRecord as ComputeSize>::compute_size(&self.mark_class_count())
3528                        .unwrap_or(0),
3529                )
3530    }
3531}
3532
3533const _: () = assert!(FontData::default_data_long_enough(LigatureAttach::MIN_SIZE));
3534
3535impl Default for LigatureAttach<'_> {
3536    fn default() -> Self {
3537        Self {
3538            data: FontData::default_table_data(),
3539            mark_class_count: Default::default(),
3540        }
3541    }
3542}
3543
3544#[cfg(feature = "experimental_traverse")]
3545impl<'a> SomeTable<'a> for LigatureAttach<'a> {
3546    fn type_name(&self) -> &str {
3547        "LigatureAttach"
3548    }
3549    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3550        match idx {
3551            0usize => Some(Field::new("component_count", self.component_count())),
3552            1usize => Some(Field::new(
3553                "component_records",
3554                traversal::FieldType::computed_array(
3555                    "ComponentRecord",
3556                    self.component_records(),
3557                    self.offset_data(),
3558                ),
3559            )),
3560            _ => None,
3561        }
3562    }
3563}
3564
3565#[cfg(feature = "experimental_traverse")]
3566#[allow(clippy::needless_lifetimes)]
3567impl<'a> std::fmt::Debug for LigatureAttach<'a> {
3568    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3569        (self as &dyn SomeTable<'a>).fmt(f)
3570    }
3571}
3572
3573/// Part of [MarkLigPosFormat1]
3574#[derive(Clone, Debug)]
3575pub struct ComponentRecord<'a> {
3576    /// Array of offsets (one per class) to Anchor tables. Offsets are
3577    /// from beginning of LigatureAttach table, ordered by class
3578    /// (offsets may be NULL).
3579    pub ligature_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3580}
3581
3582impl<'a> ComponentRecord<'a> {
3583    /// Array of offsets (one per class) to Anchor tables. Offsets are
3584    /// from beginning of LigatureAttach table, ordered by class
3585    /// (offsets may be NULL).
3586    pub fn ligature_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3587        self.ligature_anchor_offsets
3588    }
3589
3590    /// Array of offsets (one per class) to Anchor tables. Offsets are
3591    /// from beginning of LigatureAttach table, ordered by class
3592    /// (offsets may be NULL).
3593    ///
3594    /// The `data` argument should be retrieved from the parent table
3595    /// By calling its `offset_data` method.
3596    pub fn ligature_anchors(
3597        &self,
3598        data: FontData<'a>,
3599    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3600        let offsets = self.ligature_anchor_offsets();
3601        ArrayOfNullableOffsets::new(offsets, data, ())
3602    }
3603}
3604
3605impl ReadArgs for ComponentRecord<'_> {
3606    type Args = u16;
3607}
3608
3609impl ComputeSize for ComponentRecord<'_> {
3610    #[allow(clippy::needless_question_mark)]
3611    fn compute_size(args: &u16) -> Result<usize, ReadError> {
3612        let mark_class_count = *args;
3613        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
3614    }
3615}
3616
3617impl<'a> FontReadWithArgs<'a> for ComponentRecord<'a> {
3618    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
3619        let mut cursor = data.cursor();
3620        let mark_class_count = *args;
3621        Ok(Self {
3622            ligature_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
3623        })
3624    }
3625}
3626
3627#[allow(clippy::needless_lifetimes)]
3628impl<'a> ComponentRecord<'a> {
3629    /// A constructor that requires additional arguments.
3630    ///
3631    /// This type requires some external state in order to be
3632    /// parsed.
3633    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3634        let args = mark_class_count;
3635        Self::read_with_args(data, &args)
3636    }
3637}
3638
3639#[cfg(feature = "experimental_traverse")]
3640impl<'a> SomeRecord<'a> for ComponentRecord<'a> {
3641    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
3642        RecordResolver {
3643            name: "ComponentRecord",
3644            get_field: Box::new(move |idx, _data| match idx {
3645                0usize => Some(Field::new(
3646                    "ligature_anchor_offsets",
3647                    FieldType::from(self.ligature_anchors(_data)),
3648                )),
3649                _ => None,
3650            }),
3651            data,
3652        }
3653    }
3654}
3655
3656impl Format<u16> for MarkMarkPosFormat1<'_> {
3657    const FORMAT: u16 = 1;
3658}
3659
3660impl<'a> MinByteRange<'a> for MarkMarkPosFormat1<'a> {
3661    fn min_byte_range(&self) -> Range<usize> {
3662        0..self.mark2_array_offset_byte_range().end
3663    }
3664    fn min_table_bytes(&self) -> &'a [u8] {
3665        let range = self.min_byte_range();
3666        self.data.as_bytes().get(range).unwrap_or_default()
3667    }
3668}
3669
3670impl<'a> FontRead<'a> for MarkMarkPosFormat1<'a> {
3671    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
3672        #[allow(clippy::absurd_extreme_comparisons)]
3673        if data.len() < Self::MIN_SIZE {
3674            return Err(ReadError::OutOfBounds);
3675        }
3676        Ok(Self { data })
3677    }
3678}
3679
3680/// [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
3681#[derive(Clone)]
3682pub struct MarkMarkPosFormat1<'a> {
3683    data: FontData<'a>,
3684}
3685
3686#[allow(clippy::needless_lifetimes)]
3687impl<'a> MarkMarkPosFormat1<'a> {
3688    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
3689        + Offset16::RAW_BYTE_LEN
3690        + Offset16::RAW_BYTE_LEN
3691        + u16::RAW_BYTE_LEN
3692        + Offset16::RAW_BYTE_LEN
3693        + Offset16::RAW_BYTE_LEN);
3694    basic_table_impls!(impl_the_methods);
3695
3696    /// Format identifier: format = 1
3697    pub fn pos_format(&self) -> u16 {
3698        let range = self.pos_format_byte_range();
3699        self.data.read_at(range.start).ok().unwrap()
3700    }
3701
3702    /// Offset to Combining Mark Coverage table, from beginning of
3703    /// MarkMarkPos subtable.
3704    pub fn mark1_coverage_offset(&self) -> Offset16 {
3705        let range = self.mark1_coverage_offset_byte_range();
3706        self.data.read_at(range.start).ok().unwrap()
3707    }
3708
3709    /// Attempt to resolve [`mark1_coverage_offset`][Self::mark1_coverage_offset].
3710    pub fn mark1_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3711        let data = self.data;
3712        self.mark1_coverage_offset().resolve(data)
3713    }
3714
3715    /// Offset to Base Mark Coverage table, from beginning of
3716    /// MarkMarkPos subtable.
3717    pub fn mark2_coverage_offset(&self) -> Offset16 {
3718        let range = self.mark2_coverage_offset_byte_range();
3719        self.data.read_at(range.start).ok().unwrap()
3720    }
3721
3722    /// Attempt to resolve [`mark2_coverage_offset`][Self::mark2_coverage_offset].
3723    pub fn mark2_coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
3724        let data = self.data;
3725        self.mark2_coverage_offset().resolve(data)
3726    }
3727
3728    /// Number of Combining Mark classes defined
3729    pub fn mark_class_count(&self) -> u16 {
3730        let range = self.mark_class_count_byte_range();
3731        self.data.read_at(range.start).ok().unwrap()
3732    }
3733
3734    /// Offset to MarkArray table for mark1, from beginning of
3735    /// MarkMarkPos subtable.
3736    pub fn mark1_array_offset(&self) -> Offset16 {
3737        let range = self.mark1_array_offset_byte_range();
3738        self.data.read_at(range.start).ok().unwrap()
3739    }
3740
3741    /// Attempt to resolve [`mark1_array_offset`][Self::mark1_array_offset].
3742    pub fn mark1_array(&self) -> Result<MarkArray<'a>, ReadError> {
3743        let data = self.data;
3744        self.mark1_array_offset().resolve(data)
3745    }
3746
3747    /// Offset to Mark2Array table for mark2, from beginning of
3748    /// MarkMarkPos subtable.
3749    pub fn mark2_array_offset(&self) -> Offset16 {
3750        let range = self.mark2_array_offset_byte_range();
3751        self.data.read_at(range.start).ok().unwrap()
3752    }
3753
3754    /// Attempt to resolve [`mark2_array_offset`][Self::mark2_array_offset].
3755    pub fn mark2_array(&self) -> Result<Mark2Array<'a>, ReadError> {
3756        let data = self.data;
3757        let args = self.mark_class_count();
3758        self.mark2_array_offset().resolve_with_args(data, &args)
3759    }
3760
3761    pub fn pos_format_byte_range(&self) -> Range<usize> {
3762        let start = 0;
3763        start..start + u16::RAW_BYTE_LEN
3764    }
3765
3766    pub fn mark1_coverage_offset_byte_range(&self) -> Range<usize> {
3767        let start = self.pos_format_byte_range().end;
3768        start..start + Offset16::RAW_BYTE_LEN
3769    }
3770
3771    pub fn mark2_coverage_offset_byte_range(&self) -> Range<usize> {
3772        let start = self.mark1_coverage_offset_byte_range().end;
3773        start..start + Offset16::RAW_BYTE_LEN
3774    }
3775
3776    pub fn mark_class_count_byte_range(&self) -> Range<usize> {
3777        let start = self.mark2_coverage_offset_byte_range().end;
3778        start..start + u16::RAW_BYTE_LEN
3779    }
3780
3781    pub fn mark1_array_offset_byte_range(&self) -> Range<usize> {
3782        let start = self.mark_class_count_byte_range().end;
3783        start..start + Offset16::RAW_BYTE_LEN
3784    }
3785
3786    pub fn mark2_array_offset_byte_range(&self) -> Range<usize> {
3787        let start = self.mark1_array_offset_byte_range().end;
3788        start..start + Offset16::RAW_BYTE_LEN
3789    }
3790}
3791
3792const _: () = assert!(FontData::default_data_long_enough(
3793    MarkMarkPosFormat1::MIN_SIZE
3794));
3795
3796impl Default for MarkMarkPosFormat1<'_> {
3797    fn default() -> Self {
3798        Self {
3799            data: FontData::default_format_1_u16_table_data(),
3800        }
3801    }
3802}
3803
3804#[cfg(feature = "experimental_traverse")]
3805impl<'a> SomeTable<'a> for MarkMarkPosFormat1<'a> {
3806    fn type_name(&self) -> &str {
3807        "MarkMarkPosFormat1"
3808    }
3809    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3810        match idx {
3811            0usize => Some(Field::new("pos_format", self.pos_format())),
3812            1usize => Some(Field::new(
3813                "mark1_coverage_offset",
3814                FieldType::offset(self.mark1_coverage_offset(), self.mark1_coverage()),
3815            )),
3816            2usize => Some(Field::new(
3817                "mark2_coverage_offset",
3818                FieldType::offset(self.mark2_coverage_offset(), self.mark2_coverage()),
3819            )),
3820            3usize => Some(Field::new("mark_class_count", self.mark_class_count())),
3821            4usize => Some(Field::new(
3822                "mark1_array_offset",
3823                FieldType::offset(self.mark1_array_offset(), self.mark1_array()),
3824            )),
3825            5usize => Some(Field::new(
3826                "mark2_array_offset",
3827                FieldType::offset(self.mark2_array_offset(), self.mark2_array()),
3828            )),
3829            _ => None,
3830        }
3831    }
3832}
3833
3834#[cfg(feature = "experimental_traverse")]
3835#[allow(clippy::needless_lifetimes)]
3836impl<'a> std::fmt::Debug for MarkMarkPosFormat1<'a> {
3837    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3838        (self as &dyn SomeTable<'a>).fmt(f)
3839    }
3840}
3841
3842impl<'a> MinByteRange<'a> for Mark2Array<'a> {
3843    fn min_byte_range(&self) -> Range<usize> {
3844        0..self.mark2_records_byte_range().end
3845    }
3846    fn min_table_bytes(&self) -> &'a [u8] {
3847        let range = self.min_byte_range();
3848        self.data.as_bytes().get(range).unwrap_or_default()
3849    }
3850}
3851
3852impl ReadArgs for Mark2Array<'_> {
3853    type Args = u16;
3854}
3855
3856impl<'a> FontReadWithArgs<'a> for Mark2Array<'a> {
3857    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
3858        let mark_class_count = *args;
3859
3860        #[allow(clippy::absurd_extreme_comparisons)]
3861        if data.len() < Self::MIN_SIZE {
3862            return Err(ReadError::OutOfBounds);
3863        }
3864        Ok(Self {
3865            data,
3866            mark_class_count,
3867        })
3868    }
3869}
3870
3871impl<'a> Mark2Array<'a> {
3872    /// A constructor that requires additional arguments.
3873    ///
3874    /// This type requires some external state in order to be
3875    /// parsed.
3876    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
3877        let args = mark_class_count;
3878        Self::read_with_args(data, &args)
3879    }
3880}
3881
3882/// Part of [MarkMarkPosFormat1]Class2Record
3883#[derive(Clone)]
3884pub struct Mark2Array<'a> {
3885    data: FontData<'a>,
3886    mark_class_count: u16,
3887}
3888
3889#[allow(clippy::needless_lifetimes)]
3890impl<'a> Mark2Array<'a> {
3891    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
3892    basic_table_impls!(impl_the_methods);
3893
3894    /// Number of Mark2 records
3895    pub fn mark2_count(&self) -> u16 {
3896        let range = self.mark2_count_byte_range();
3897        self.data.read_at(range.start).ok().unwrap()
3898    }
3899
3900    /// Array of Mark2Records, in Coverage order.
3901    pub fn mark2_records(&self) -> ComputedArray<'a, Mark2Record<'a>> {
3902        let range = self.mark2_records_byte_range();
3903        self.data
3904            .read_with_args(range, &self.mark_class_count())
3905            .unwrap_or_default()
3906    }
3907
3908    pub(crate) fn mark_class_count(&self) -> u16 {
3909        self.mark_class_count
3910    }
3911
3912    pub fn mark2_count_byte_range(&self) -> Range<usize> {
3913        let start = 0;
3914        start..start + u16::RAW_BYTE_LEN
3915    }
3916
3917    pub fn mark2_records_byte_range(&self) -> Range<usize> {
3918        let mark2_count = self.mark2_count();
3919        let start = self.mark2_count_byte_range().end;
3920        start
3921            ..start
3922                + (transforms::to_usize(mark2_count)).saturating_mul(
3923                    <Mark2Record as ComputeSize>::compute_size(&self.mark_class_count())
3924                        .unwrap_or(0),
3925                )
3926    }
3927}
3928
3929const _: () = assert!(FontData::default_data_long_enough(Mark2Array::MIN_SIZE));
3930
3931impl Default for Mark2Array<'_> {
3932    fn default() -> Self {
3933        Self {
3934            data: FontData::default_table_data(),
3935            mark_class_count: Default::default(),
3936        }
3937    }
3938}
3939
3940#[cfg(feature = "experimental_traverse")]
3941impl<'a> SomeTable<'a> for Mark2Array<'a> {
3942    fn type_name(&self) -> &str {
3943        "Mark2Array"
3944    }
3945    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
3946        match idx {
3947            0usize => Some(Field::new("mark2_count", self.mark2_count())),
3948            1usize => Some(Field::new(
3949                "mark2_records",
3950                traversal::FieldType::computed_array(
3951                    "Mark2Record",
3952                    self.mark2_records(),
3953                    self.offset_data(),
3954                ),
3955            )),
3956            _ => None,
3957        }
3958    }
3959}
3960
3961#[cfg(feature = "experimental_traverse")]
3962#[allow(clippy::needless_lifetimes)]
3963impl<'a> std::fmt::Debug for Mark2Array<'a> {
3964    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3965        (self as &dyn SomeTable<'a>).fmt(f)
3966    }
3967}
3968
3969/// Part of [MarkMarkPosFormat1]
3970#[derive(Clone, Debug)]
3971pub struct Mark2Record<'a> {
3972    /// Array of offsets (one per class) to Anchor tables. Offsets are
3973    /// from beginning of Mark2Array table, in class order (offsets may
3974    /// be NULL).
3975    pub mark2_anchor_offsets: &'a [BigEndian<Nullable<Offset16>>],
3976}
3977
3978impl<'a> Mark2Record<'a> {
3979    /// Array of offsets (one per class) to Anchor tables. Offsets are
3980    /// from beginning of Mark2Array table, in class order (offsets may
3981    /// be NULL).
3982    pub fn mark2_anchor_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
3983        self.mark2_anchor_offsets
3984    }
3985
3986    /// Array of offsets (one per class) to Anchor tables. Offsets are
3987    /// from beginning of Mark2Array table, in class order (offsets may
3988    /// be NULL).
3989    ///
3990    /// The `data` argument should be retrieved from the parent table
3991    /// By calling its `offset_data` method.
3992    pub fn mark2_anchors(
3993        &self,
3994        data: FontData<'a>,
3995    ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> {
3996        let offsets = self.mark2_anchor_offsets();
3997        ArrayOfNullableOffsets::new(offsets, data, ())
3998    }
3999}
4000
4001impl ReadArgs for Mark2Record<'_> {
4002    type Args = u16;
4003}
4004
4005impl ComputeSize for Mark2Record<'_> {
4006    #[allow(clippy::needless_question_mark)]
4007    fn compute_size(args: &u16) -> Result<usize, ReadError> {
4008        let mark_class_count = *args;
4009        Ok((transforms::to_usize(mark_class_count)).saturating_mul(Offset16::RAW_BYTE_LEN))
4010    }
4011}
4012
4013impl<'a> FontReadWithArgs<'a> for Mark2Record<'a> {
4014    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
4015        let mut cursor = data.cursor();
4016        let mark_class_count = *args;
4017        Ok(Self {
4018            mark2_anchor_offsets: cursor.read_array(transforms::to_usize(mark_class_count))?,
4019        })
4020    }
4021}
4022
4023#[allow(clippy::needless_lifetimes)]
4024impl<'a> Mark2Record<'a> {
4025    /// A constructor that requires additional arguments.
4026    ///
4027    /// This type requires some external state in order to be
4028    /// parsed.
4029    pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result<Self, ReadError> {
4030        let args = mark_class_count;
4031        Self::read_with_args(data, &args)
4032    }
4033}
4034
4035#[cfg(feature = "experimental_traverse")]
4036impl<'a> SomeRecord<'a> for Mark2Record<'a> {
4037    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
4038        RecordResolver {
4039            name: "Mark2Record",
4040            get_field: Box::new(move |idx, _data| match idx {
4041                0usize => Some(Field::new(
4042                    "mark2_anchor_offsets",
4043                    FieldType::from(self.mark2_anchors(_data)),
4044                )),
4045                _ => None,
4046            }),
4047            data,
4048        }
4049    }
4050}
4051
4052impl Format<u16> for ExtensionPosFormat1<'_> {
4053    const FORMAT: u16 = 1;
4054}
4055
4056impl Discriminant for ExtensionPosFormat1<'_, ()> {
4057    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
4058        data.read_at(u16::RAW_BYTE_LEN)
4059    }
4060}
4061
4062impl<'a, T> MinByteRange<'a> for ExtensionPosFormat1<'a, T> {
4063    fn min_byte_range(&self) -> Range<usize> {
4064        0..self.extension_offset_byte_range().end
4065    }
4066    fn min_table_bytes(&self) -> &'a [u8] {
4067        let range = self.min_byte_range();
4068        self.data.as_bytes().get(range).unwrap_or_default()
4069    }
4070}
4071
4072impl<'a, T> FontRead<'a> for ExtensionPosFormat1<'a, T> {
4073    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
4074        #[allow(clippy::absurd_extreme_comparisons)]
4075        if data.len() < Self::MIN_SIZE {
4076            return Err(ReadError::OutOfBounds);
4077        }
4078        Ok(Self {
4079            data,
4080            offset_type: std::marker::PhantomData,
4081        })
4082    }
4083}
4084
4085impl<'a, T> ExtensionPosFormat1<'a, T> {
4086    #[allow(dead_code)]
4087    /// Replace the specific generic type on this implementation with `()`
4088    pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> {
4089        ExtensionPosFormat1 {
4090            data: self.data,
4091            offset_type: std::marker::PhantomData,
4092        }
4093    }
4094}
4095
4096/// [Extension Positioning Subtable Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#extension-positioning-subtable-format-1)
4097#[derive(Clone)]
4098pub struct ExtensionPosFormat1<'a, T = ()> {
4099    data: FontData<'a>,
4100    offset_type: std::marker::PhantomData<*const T>,
4101}
4102
4103#[allow(clippy::needless_lifetimes)]
4104impl<'a, T> ExtensionPosFormat1<'a, T> {
4105    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
4106    basic_table_impls!(impl_the_methods);
4107
4108    /// Format identifier: format = 1
4109    pub fn pos_format(&self) -> u16 {
4110        let range = self.pos_format_byte_range();
4111        self.data.read_at(range.start).ok().unwrap()
4112    }
4113
4114    /// Lookup type of subtable referenced by extensionOffset (i.e. the
4115    /// extension subtable).
4116    pub fn extension_lookup_type(&self) -> u16 {
4117        let range = self.extension_lookup_type_byte_range();
4118        self.data.read_at(range.start).ok().unwrap()
4119    }
4120
4121    /// Offset to the extension subtable, of lookup type
4122    /// extensionLookupType, relative to the start of the
4123    /// ExtensionPosFormat1 subtable.
4124    pub fn extension_offset(&self) -> Offset32 {
4125        let range = self.extension_offset_byte_range();
4126        self.data.read_at(range.start).ok().unwrap()
4127    }
4128
4129    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
4130    pub fn extension(&self) -> Result<T, ReadError>
4131    where
4132        T: FontRead<'a>,
4133    {
4134        let data = self.data;
4135        self.extension_offset().resolve(data)
4136    }
4137
4138    pub fn pos_format_byte_range(&self) -> Range<usize> {
4139        let start = 0;
4140        start..start + u16::RAW_BYTE_LEN
4141    }
4142
4143    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
4144        let start = self.pos_format_byte_range().end;
4145        start..start + u16::RAW_BYTE_LEN
4146    }
4147
4148    pub fn extension_offset_byte_range(&self) -> Range<usize> {
4149        let start = self.extension_lookup_type_byte_range().end;
4150        start..start + Offset32::RAW_BYTE_LEN
4151    }
4152}
4153
4154const _: () = assert!(FontData::default_data_long_enough(
4155    ExtensionPosFormat1::<()>::MIN_SIZE
4156));
4157
4158impl<T> Default for ExtensionPosFormat1<'_, T> {
4159    fn default() -> Self {
4160        Self {
4161            data: FontData::default_format_1_u16_table_data(),
4162            offset_type: std::marker::PhantomData,
4163        }
4164    }
4165}
4166
4167#[cfg(feature = "experimental_traverse")]
4168impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionPosFormat1<'a, T> {
4169    fn type_name(&self) -> &str {
4170        "ExtensionPosFormat1"
4171    }
4172    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4173        match idx {
4174            0usize => Some(Field::new("pos_format", self.pos_format())),
4175            1usize => Some(Field::new(
4176                "extension_lookup_type",
4177                self.extension_lookup_type(),
4178            )),
4179            2usize => Some(Field::new(
4180                "extension_offset",
4181                FieldType::offset(self.extension_offset(), self.extension()),
4182            )),
4183            _ => None,
4184        }
4185    }
4186}
4187
4188#[cfg(feature = "experimental_traverse")]
4189#[allow(clippy::needless_lifetimes)]
4190impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for ExtensionPosFormat1<'a, T> {
4191    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4192        (self as &dyn SomeTable<'a>).fmt(f)
4193    }
4194}
4195
4196/// A [GPOS Extension Positioning](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#lookuptype-9-extension-positioning) subtable
4197pub enum ExtensionSubtable<'a> {
4198    Single(ExtensionPosFormat1<'a, SinglePos<'a>>),
4199    Pair(ExtensionPosFormat1<'a, PairPos<'a>>),
4200    Cursive(ExtensionPosFormat1<'a, CursivePosFormat1<'a>>),
4201    MarkToBase(ExtensionPosFormat1<'a, MarkBasePosFormat1<'a>>),
4202    MarkToLig(ExtensionPosFormat1<'a, MarkLigPosFormat1<'a>>),
4203    MarkToMark(ExtensionPosFormat1<'a, MarkMarkPosFormat1<'a>>),
4204    Contextual(ExtensionPosFormat1<'a, PositionSequenceContext<'a>>),
4205    ChainContextual(ExtensionPosFormat1<'a, PositionChainContext<'a>>),
4206}
4207
4208impl Default for ExtensionSubtable<'_> {
4209    fn default() -> Self {
4210        Self::Single(Default::default())
4211    }
4212}
4213
4214impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
4215    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
4216        let discriminant = ExtensionPosFormat1::read_discriminant(bytes)?;
4217        match discriminant {
4218            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
4219            2 => Ok(ExtensionSubtable::Pair(FontRead::read(bytes)?)),
4220            3 => Ok(ExtensionSubtable::Cursive(FontRead::read(bytes)?)),
4221            4 => Ok(ExtensionSubtable::MarkToBase(FontRead::read(bytes)?)),
4222            5 => Ok(ExtensionSubtable::MarkToLig(FontRead::read(bytes)?)),
4223            6 => Ok(ExtensionSubtable::MarkToMark(FontRead::read(bytes)?)),
4224            7 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
4225            8 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
4226            other => Err(ReadError::InvalidFormat(other.into())),
4227        }
4228    }
4229}
4230
4231impl<'a> ExtensionSubtable<'a> {
4232    #[allow(dead_code)]
4233    /// Return the inner table, removing the specific generics.
4234    ///
4235    /// This lets us return a single concrete type we can call methods on.
4236    pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> {
4237        match self {
4238            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
4239            ExtensionSubtable::Pair(inner) => inner.of_unit_type(),
4240            ExtensionSubtable::Cursive(inner) => inner.of_unit_type(),
4241            ExtensionSubtable::MarkToBase(inner) => inner.of_unit_type(),
4242            ExtensionSubtable::MarkToLig(inner) => inner.of_unit_type(),
4243            ExtensionSubtable::MarkToMark(inner) => inner.of_unit_type(),
4244            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
4245            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
4246        }
4247    }
4248}
4249
4250#[cfg(feature = "experimental_traverse")]
4251impl<'a> ExtensionSubtable<'a> {
4252    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
4253        match self {
4254            ExtensionSubtable::Single(table) => table,
4255            ExtensionSubtable::Pair(table) => table,
4256            ExtensionSubtable::Cursive(table) => table,
4257            ExtensionSubtable::MarkToBase(table) => table,
4258            ExtensionSubtable::MarkToLig(table) => table,
4259            ExtensionSubtable::MarkToMark(table) => table,
4260            ExtensionSubtable::Contextual(table) => table,
4261            ExtensionSubtable::ChainContextual(table) => table,
4262        }
4263    }
4264}
4265
4266#[cfg(feature = "experimental_traverse")]
4267impl<'a> SomeTable<'a> for ExtensionSubtable<'a> {
4268    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
4269        self.dyn_inner().get_field(idx)
4270    }
4271    fn type_name(&self) -> &str {
4272        self.dyn_inner().type_name()
4273    }
4274}
4275
4276#[cfg(feature = "experimental_traverse")]
4277impl std::fmt::Debug for ExtensionSubtable<'_> {
4278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4279        self.dyn_inner().fmt(f)
4280    }
4281}