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