Skip to main content

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