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, Default, 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: OffsetMarker<ScriptList>,
17    /// Offset to FeatureList table, from beginning of GPOS table
18    pub feature_list: OffsetMarker<FeatureList>,
19    /// Offset to LookupList table, from beginning of GPOS table
20    pub lookup_list: OffsetMarker<PositionLookupList>,
21    pub feature_variations: NullableOffsetMarker<FeatureVariations, WIDTH_32>,
22}
23
24impl Gpos {
25    /// Construct a new `Gpos`
26    pub fn new(
27        script_list: ScriptList,
28        feature_list: FeatureList,
29        lookup_list: PositionLookupList,
30    ) -> Self {
31        Self {
32            script_list: script_list.into(),
33            feature_list: feature_list.into(),
34            lookup_list: lookup_list.into(),
35            ..Default::default()
36        }
37    }
38}
39
40impl FontWrite for Gpos {
41    #[allow(clippy::unnecessary_cast)]
42    fn write_into(&self, writer: &mut TableWriter) {
43        let version = self.compute_version() as MajorMinor;
44        version.write_into(writer);
45        self.script_list.write_into(writer);
46        self.feature_list.write_into(writer);
47        self.lookup_list.write_into(writer);
48        version
49            .compatible((1u16, 1u16))
50            .then(|| self.feature_variations.write_into(writer));
51    }
52    fn table_type(&self) -> TableType {
53        TableType::TopLevel(Gpos::TAG)
54    }
55}
56
57impl Validate for Gpos {
58    fn validate_impl(&self, ctx: &mut ValidationCtx) {
59        ctx.in_table("Gpos", |ctx| {
60            ctx.in_field("script_list", |ctx| {
61                self.script_list.validate_impl(ctx);
62            });
63            ctx.in_field("feature_list", |ctx| {
64                self.feature_list.validate_impl(ctx);
65            });
66            ctx.in_field("lookup_list", |ctx| {
67                self.lookup_list.validate_impl(ctx);
68            });
69            ctx.in_field("feature_variations", |ctx| {
70                self.feature_variations.validate_impl(ctx);
71            });
72        })
73    }
74}
75
76impl TopLevelTable for Gpos {
77    const TAG: Tag = Tag::new(b"GPOS");
78}
79
80impl<'a> FromObjRef<read_fonts::tables::gpos::Gpos<'a>> for Gpos {
81    fn from_obj_ref(obj: &read_fonts::tables::gpos::Gpos<'a>, _: FontData) -> Self {
82        Gpos {
83            script_list: obj.script_list().to_owned_table(),
84            feature_list: obj.feature_list().to_owned_table(),
85            lookup_list: obj.lookup_list().to_owned_table(),
86            feature_variations: obj.feature_variations().to_owned_table(),
87        }
88    }
89}
90
91#[allow(clippy::needless_lifetimes)]
92impl<'a> FromTableRef<read_fonts::tables::gpos::Gpos<'a>> for Gpos {}
93
94impl<'a> FontRead<'a> for Gpos {
95    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
96        <read_fonts::tables::gpos::Gpos as FontRead>::read(data).map(|x| x.to_owned_table())
97    }
98}
99
100/// A [GPOS Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#gsubLookupTypeEnum) subtable.
101#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
103pub enum PositionLookup {
104    Single(Lookup<SinglePos>),
105    Pair(Lookup<PairPos>),
106    Cursive(Lookup<CursivePosFormat1>),
107    MarkToBase(Lookup<MarkBasePosFormat1>),
108    MarkToLig(Lookup<MarkLigPosFormat1>),
109    MarkToMark(Lookup<MarkMarkPosFormat1>),
110    Contextual(Lookup<PositionSequenceContext>),
111    ChainContextual(Lookup<PositionChainContext>),
112    Extension(Lookup<ExtensionSubtable>),
113}
114
115impl Default for PositionLookup {
116    fn default() -> Self {
117        Self::Single(Default::default())
118    }
119}
120
121impl FontWrite for PositionLookup {
122    fn write_into(&self, writer: &mut TableWriter) {
123        match self {
124            Self::Single(table) => table.write_into(writer),
125            Self::Pair(table) => table.write_into(writer),
126            Self::Cursive(table) => table.write_into(writer),
127            Self::MarkToBase(table) => table.write_into(writer),
128            Self::MarkToLig(table) => table.write_into(writer),
129            Self::MarkToMark(table) => table.write_into(writer),
130            Self::Contextual(table) => table.write_into(writer),
131            Self::ChainContextual(table) => table.write_into(writer),
132            Self::Extension(table) => table.write_into(writer),
133        }
134    }
135    fn table_type(&self) -> TableType {
136        match self {
137            Self::Single(table) => table.table_type(),
138            Self::Pair(table) => table.table_type(),
139            Self::Cursive(table) => table.table_type(),
140            Self::MarkToBase(table) => table.table_type(),
141            Self::MarkToLig(table) => table.table_type(),
142            Self::MarkToMark(table) => table.table_type(),
143            Self::Contextual(table) => table.table_type(),
144            Self::ChainContextual(table) => table.table_type(),
145            Self::Extension(table) => table.table_type(),
146        }
147    }
148}
149
150impl Validate for PositionLookup {
151    fn validate_impl(&self, ctx: &mut ValidationCtx) {
152        match self {
153            Self::Single(table) => table.validate_impl(ctx),
154            Self::Pair(table) => table.validate_impl(ctx),
155            Self::Cursive(table) => table.validate_impl(ctx),
156            Self::MarkToBase(table) => table.validate_impl(ctx),
157            Self::MarkToLig(table) => table.validate_impl(ctx),
158            Self::MarkToMark(table) => table.validate_impl(ctx),
159            Self::Contextual(table) => table.validate_impl(ctx),
160            Self::ChainContextual(table) => table.validate_impl(ctx),
161            Self::Extension(table) => table.validate_impl(ctx),
162        }
163    }
164}
165
166impl FromObjRef<read_fonts::tables::gpos::PositionLookup<'_>> for PositionLookup {
167    fn from_obj_ref(from: &read_fonts::tables::gpos::PositionLookup<'_>, data: FontData) -> Self {
168        match from {
169            read_fonts::tables::gpos::PositionLookup::Single(table) => {
170                Self::Single(table.to_owned_obj(data))
171            }
172            read_fonts::tables::gpos::PositionLookup::Pair(table) => {
173                Self::Pair(table.to_owned_obj(data))
174            }
175            read_fonts::tables::gpos::PositionLookup::Cursive(table) => {
176                Self::Cursive(table.to_owned_obj(data))
177            }
178            read_fonts::tables::gpos::PositionLookup::MarkToBase(table) => {
179                Self::MarkToBase(table.to_owned_obj(data))
180            }
181            read_fonts::tables::gpos::PositionLookup::MarkToLig(table) => {
182                Self::MarkToLig(table.to_owned_obj(data))
183            }
184            read_fonts::tables::gpos::PositionLookup::MarkToMark(table) => {
185                Self::MarkToMark(table.to_owned_obj(data))
186            }
187            read_fonts::tables::gpos::PositionLookup::Contextual(table) => {
188                Self::Contextual(table.to_owned_obj(data))
189            }
190            read_fonts::tables::gpos::PositionLookup::ChainContextual(table) => {
191                Self::ChainContextual(table.to_owned_obj(data))
192            }
193            read_fonts::tables::gpos::PositionLookup::Extension(table) => {
194                Self::Extension(table.to_owned_obj(data))
195            }
196        }
197    }
198}
199
200impl FromTableRef<read_fonts::tables::gpos::PositionLookup<'_>> for PositionLookup {}
201
202impl From<Lookup<SinglePos>> for PositionLookup {
203    fn from(src: Lookup<SinglePos>) -> PositionLookup {
204        PositionLookup::Single(src)
205    }
206}
207
208impl From<Lookup<PairPos>> for PositionLookup {
209    fn from(src: Lookup<PairPos>) -> PositionLookup {
210        PositionLookup::Pair(src)
211    }
212}
213
214impl From<Lookup<CursivePosFormat1>> for PositionLookup {
215    fn from(src: Lookup<CursivePosFormat1>) -> PositionLookup {
216        PositionLookup::Cursive(src)
217    }
218}
219
220impl From<Lookup<MarkBasePosFormat1>> for PositionLookup {
221    fn from(src: Lookup<MarkBasePosFormat1>) -> PositionLookup {
222        PositionLookup::MarkToBase(src)
223    }
224}
225
226impl From<Lookup<MarkLigPosFormat1>> for PositionLookup {
227    fn from(src: Lookup<MarkLigPosFormat1>) -> PositionLookup {
228        PositionLookup::MarkToLig(src)
229    }
230}
231
232impl From<Lookup<MarkMarkPosFormat1>> for PositionLookup {
233    fn from(src: Lookup<MarkMarkPosFormat1>) -> PositionLookup {
234        PositionLookup::MarkToMark(src)
235    }
236}
237
238impl From<Lookup<PositionSequenceContext>> for PositionLookup {
239    fn from(src: Lookup<PositionSequenceContext>) -> PositionLookup {
240        PositionLookup::Contextual(src)
241    }
242}
243
244impl From<Lookup<PositionChainContext>> for PositionLookup {
245    fn from(src: Lookup<PositionChainContext>) -> PositionLookup {
246        PositionLookup::ChainContextual(src)
247    }
248}
249
250impl From<Lookup<ExtensionSubtable>> for PositionLookup {
251    fn from(src: Lookup<ExtensionSubtable>) -> PositionLookup {
252        PositionLookup::Extension(src)
253    }
254}
255
256impl FontWrite for ValueFormat {
257    fn write_into(&self, writer: &mut TableWriter) {
258        writer.write_slice(&self.bits().to_be_bytes())
259    }
260}
261
262/// [Anchor Tables](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables)
263/// position one glyph with respect to another.
264#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
265#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
266pub enum AnchorTable {
267    Format1(AnchorFormat1),
268    Format2(AnchorFormat2),
269    Format3(AnchorFormat3),
270}
271
272impl AnchorTable {
273    /// Construct a new `AnchorFormat1` subtable
274    pub fn format_1(x_coordinate: i16, y_coordinate: i16) -> Self {
275        Self::Format1(AnchorFormat1::new(x_coordinate, y_coordinate))
276    }
277
278    /// Construct a new `AnchorFormat2` subtable
279    pub fn format_2(x_coordinate: i16, y_coordinate: i16, anchor_point: u16) -> Self {
280        Self::Format2(AnchorFormat2::new(x_coordinate, y_coordinate, anchor_point))
281    }
282
283    /// Construct a new `AnchorFormat3` subtable
284    pub fn format_3(
285        x_coordinate: i16,
286        y_coordinate: i16,
287        x_device: Option<DeviceOrVariationIndex>,
288        y_device: Option<DeviceOrVariationIndex>,
289    ) -> Self {
290        Self::Format3(AnchorFormat3::new(
291            x_coordinate,
292            y_coordinate,
293            x_device,
294            y_device,
295        ))
296    }
297}
298
299impl Default for AnchorTable {
300    fn default() -> Self {
301        Self::Format1(Default::default())
302    }
303}
304
305impl FontWrite for AnchorTable {
306    fn write_into(&self, writer: &mut TableWriter) {
307        match self {
308            Self::Format1(item) => item.write_into(writer),
309            Self::Format2(item) => item.write_into(writer),
310            Self::Format3(item) => item.write_into(writer),
311        }
312    }
313    fn table_type(&self) -> TableType {
314        match self {
315            Self::Format1(item) => item.table_type(),
316            Self::Format2(item) => item.table_type(),
317            Self::Format3(item) => item.table_type(),
318        }
319    }
320}
321
322impl Validate for AnchorTable {
323    fn validate_impl(&self, ctx: &mut ValidationCtx) {
324        match self {
325            Self::Format1(item) => item.validate_impl(ctx),
326            Self::Format2(item) => item.validate_impl(ctx),
327            Self::Format3(item) => item.validate_impl(ctx),
328        }
329    }
330}
331
332impl FromObjRef<read_fonts::tables::gpos::AnchorTable<'_>> for AnchorTable {
333    fn from_obj_ref(obj: &read_fonts::tables::gpos::AnchorTable, _: FontData) -> Self {
334        use read_fonts::tables::gpos::AnchorTable as ObjRefType;
335        match obj {
336            ObjRefType::Format1(item) => AnchorTable::Format1(item.to_owned_table()),
337            ObjRefType::Format2(item) => AnchorTable::Format2(item.to_owned_table()),
338            ObjRefType::Format3(item) => AnchorTable::Format3(item.to_owned_table()),
339        }
340    }
341}
342
343impl FromTableRef<read_fonts::tables::gpos::AnchorTable<'_>> for AnchorTable {}
344
345impl<'a> FontRead<'a> for AnchorTable {
346    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
347        <read_fonts::tables::gpos::AnchorTable as FontRead>::read(data).map(|x| x.to_owned_table())
348    }
349}
350
351impl From<AnchorFormat1> for AnchorTable {
352    fn from(src: AnchorFormat1) -> AnchorTable {
353        AnchorTable::Format1(src)
354    }
355}
356
357impl From<AnchorFormat2> for AnchorTable {
358    fn from(src: AnchorFormat2) -> AnchorTable {
359        AnchorTable::Format2(src)
360    }
361}
362
363impl From<AnchorFormat3> for AnchorTable {
364    fn from(src: AnchorFormat3) -> AnchorTable {
365        AnchorTable::Format3(src)
366    }
367}
368
369/// [Anchor Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-1-design-units): Design Units
370#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
371#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
372pub struct AnchorFormat1 {
373    /// Horizontal value, in design units
374    pub x_coordinate: i16,
375    /// Vertical value, in design units
376    pub y_coordinate: i16,
377}
378
379impl AnchorFormat1 {
380    /// Construct a new `AnchorFormat1`
381    pub fn new(x_coordinate: i16, y_coordinate: i16) -> Self {
382        Self {
383            x_coordinate,
384            y_coordinate,
385        }
386    }
387}
388
389impl FontWrite for AnchorFormat1 {
390    #[allow(clippy::unnecessary_cast)]
391    fn write_into(&self, writer: &mut TableWriter) {
392        (1 as u16).write_into(writer);
393        self.x_coordinate.write_into(writer);
394        self.y_coordinate.write_into(writer);
395    }
396    fn table_type(&self) -> TableType {
397        TableType::Named("AnchorFormat1")
398    }
399}
400
401impl Validate for AnchorFormat1 {
402    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
403}
404
405impl<'a> FromObjRef<read_fonts::tables::gpos::AnchorFormat1<'a>> for AnchorFormat1 {
406    fn from_obj_ref(obj: &read_fonts::tables::gpos::AnchorFormat1<'a>, _: FontData) -> Self {
407        AnchorFormat1 {
408            x_coordinate: obj.x_coordinate(),
409            y_coordinate: obj.y_coordinate(),
410        }
411    }
412}
413
414#[allow(clippy::needless_lifetimes)]
415impl<'a> FromTableRef<read_fonts::tables::gpos::AnchorFormat1<'a>> for AnchorFormat1 {}
416
417impl<'a> FontRead<'a> for AnchorFormat1 {
418    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
419        <read_fonts::tables::gpos::AnchorFormat1 as FontRead>::read(data)
420            .map(|x| x.to_owned_table())
421    }
422}
423
424/// [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
425#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
426#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
427pub struct AnchorFormat2 {
428    /// Horizontal value, in design units
429    pub x_coordinate: i16,
430    /// Vertical value, in design units
431    pub y_coordinate: i16,
432    /// Index to glyph contour point
433    pub anchor_point: u16,
434}
435
436impl AnchorFormat2 {
437    /// Construct a new `AnchorFormat2`
438    pub fn new(x_coordinate: i16, y_coordinate: i16, anchor_point: u16) -> Self {
439        Self {
440            x_coordinate,
441            y_coordinate,
442            anchor_point,
443        }
444    }
445}
446
447impl FontWrite for AnchorFormat2 {
448    #[allow(clippy::unnecessary_cast)]
449    fn write_into(&self, writer: &mut TableWriter) {
450        (2 as u16).write_into(writer);
451        self.x_coordinate.write_into(writer);
452        self.y_coordinate.write_into(writer);
453        self.anchor_point.write_into(writer);
454    }
455    fn table_type(&self) -> TableType {
456        TableType::Named("AnchorFormat2")
457    }
458}
459
460impl Validate for AnchorFormat2 {
461    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
462}
463
464impl<'a> FromObjRef<read_fonts::tables::gpos::AnchorFormat2<'a>> for AnchorFormat2 {
465    fn from_obj_ref(obj: &read_fonts::tables::gpos::AnchorFormat2<'a>, _: FontData) -> Self {
466        AnchorFormat2 {
467            x_coordinate: obj.x_coordinate(),
468            y_coordinate: obj.y_coordinate(),
469            anchor_point: obj.anchor_point(),
470        }
471    }
472}
473
474#[allow(clippy::needless_lifetimes)]
475impl<'a> FromTableRef<read_fonts::tables::gpos::AnchorFormat2<'a>> for AnchorFormat2 {}
476
477impl<'a> FontRead<'a> for AnchorFormat2 {
478    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
479        <read_fonts::tables::gpos::AnchorFormat2 as FontRead>::read(data)
480            .map(|x| x.to_owned_table())
481    }
482}
483
484/// [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
485#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
486#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
487pub struct AnchorFormat3 {
488    /// Horizontal value, in design units
489    pub x_coordinate: i16,
490    /// Vertical value, in design units
491    pub y_coordinate: i16,
492    /// Offset to Device table (non-variable font) / VariationIndex
493    /// table (variable font) for X coordinate, from beginning of
494    /// Anchor table (may be NULL)
495    pub x_device: NullableOffsetMarker<DeviceOrVariationIndex>,
496    /// Offset to Device table (non-variable font) / VariationIndex
497    /// table (variable font) for Y coordinate, from beginning of
498    /// Anchor table (may be NULL)
499    pub y_device: NullableOffsetMarker<DeviceOrVariationIndex>,
500}
501
502impl AnchorFormat3 {
503    /// Construct a new `AnchorFormat3`
504    pub fn new(
505        x_coordinate: i16,
506        y_coordinate: i16,
507        x_device: Option<DeviceOrVariationIndex>,
508        y_device: Option<DeviceOrVariationIndex>,
509    ) -> Self {
510        Self {
511            x_coordinate,
512            y_coordinate,
513            x_device: x_device.into(),
514            y_device: y_device.into(),
515        }
516    }
517}
518
519impl FontWrite for AnchorFormat3 {
520    #[allow(clippy::unnecessary_cast)]
521    fn write_into(&self, writer: &mut TableWriter) {
522        (3 as u16).write_into(writer);
523        self.x_coordinate.write_into(writer);
524        self.y_coordinate.write_into(writer);
525        self.x_device.write_into(writer);
526        self.y_device.write_into(writer);
527    }
528    fn table_type(&self) -> TableType {
529        TableType::Named("AnchorFormat3")
530    }
531}
532
533impl Validate for AnchorFormat3 {
534    fn validate_impl(&self, ctx: &mut ValidationCtx) {
535        ctx.in_table("AnchorFormat3", |ctx| {
536            ctx.in_field("x_device", |ctx| {
537                self.x_device.validate_impl(ctx);
538            });
539            ctx.in_field("y_device", |ctx| {
540                self.y_device.validate_impl(ctx);
541            });
542        })
543    }
544}
545
546impl<'a> FromObjRef<read_fonts::tables::gpos::AnchorFormat3<'a>> for AnchorFormat3 {
547    fn from_obj_ref(obj: &read_fonts::tables::gpos::AnchorFormat3<'a>, _: FontData) -> Self {
548        AnchorFormat3 {
549            x_coordinate: obj.x_coordinate(),
550            y_coordinate: obj.y_coordinate(),
551            x_device: obj.x_device().to_owned_table(),
552            y_device: obj.y_device().to_owned_table(),
553        }
554    }
555}
556
557#[allow(clippy::needless_lifetimes)]
558impl<'a> FromTableRef<read_fonts::tables::gpos::AnchorFormat3<'a>> for AnchorFormat3 {}
559
560impl<'a> FontRead<'a> for AnchorFormat3 {
561    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
562        <read_fonts::tables::gpos::AnchorFormat3 as FontRead>::read(data)
563            .map(|x| x.to_owned_table())
564    }
565}
566
567/// [Mark Array Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table)
568#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
569#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
570pub struct MarkArray {
571    /// Array of MarkRecords, ordered by corresponding glyphs in the
572    /// associated mark Coverage table.
573    pub mark_records: Vec<MarkRecord>,
574}
575
576impl MarkArray {
577    /// Construct a new `MarkArray`
578    pub fn new(mark_records: Vec<MarkRecord>) -> Self {
579        Self { mark_records }
580    }
581}
582
583impl FontWrite for MarkArray {
584    #[allow(clippy::unnecessary_cast)]
585    fn write_into(&self, writer: &mut TableWriter) {
586        (u16::try_from(array_len(&self.mark_records)).unwrap()).write_into(writer);
587        self.mark_records.write_into(writer);
588    }
589    fn table_type(&self) -> TableType {
590        TableType::Named("MarkArray")
591    }
592}
593
594impl Validate for MarkArray {
595    fn validate_impl(&self, ctx: &mut ValidationCtx) {
596        ctx.in_table("MarkArray", |ctx| {
597            ctx.in_field("mark_records", |ctx| {
598                if self.mark_records.len() > (u16::MAX as usize) {
599                    ctx.report("array exceeds max length");
600                }
601                self.mark_records.validate_impl(ctx);
602            });
603        })
604    }
605}
606
607impl<'a> FromObjRef<read_fonts::tables::gpos::MarkArray<'a>> for MarkArray {
608    fn from_obj_ref(obj: &read_fonts::tables::gpos::MarkArray<'a>, _: FontData) -> Self {
609        let offset_data = obj.offset_data();
610        MarkArray {
611            mark_records: obj.mark_records().to_owned_obj(offset_data),
612        }
613    }
614}
615
616#[allow(clippy::needless_lifetimes)]
617impl<'a> FromTableRef<read_fonts::tables::gpos::MarkArray<'a>> for MarkArray {}
618
619impl<'a> FontRead<'a> for MarkArray {
620    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
621        <read_fonts::tables::gpos::MarkArray as FontRead>::read(data).map(|x| x.to_owned_table())
622    }
623}
624
625/// Part of [MarkArray]
626#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
627#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
628pub struct MarkRecord {
629    /// Class defined for the associated mark.
630    pub mark_class: u16,
631    /// Offset to Anchor table, from beginning of MarkArray table.
632    pub mark_anchor: OffsetMarker<AnchorTable>,
633}
634
635impl MarkRecord {
636    /// Construct a new `MarkRecord`
637    pub fn new(mark_class: u16, mark_anchor: AnchorTable) -> Self {
638        Self {
639            mark_class,
640            mark_anchor: mark_anchor.into(),
641        }
642    }
643}
644
645impl FontWrite for MarkRecord {
646    fn write_into(&self, writer: &mut TableWriter) {
647        self.mark_class.write_into(writer);
648        self.mark_anchor.write_into(writer);
649    }
650    fn table_type(&self) -> TableType {
651        TableType::Named("MarkRecord")
652    }
653}
654
655impl Validate for MarkRecord {
656    fn validate_impl(&self, ctx: &mut ValidationCtx) {
657        ctx.in_table("MarkRecord", |ctx| {
658            ctx.in_field("mark_anchor", |ctx| {
659                self.mark_anchor.validate_impl(ctx);
660            });
661        })
662    }
663}
664
665impl FromObjRef<read_fonts::tables::gpos::MarkRecord> for MarkRecord {
666    fn from_obj_ref(obj: &read_fonts::tables::gpos::MarkRecord, offset_data: FontData) -> Self {
667        MarkRecord {
668            mark_class: obj.mark_class(),
669            mark_anchor: obj.mark_anchor(offset_data).to_owned_table(),
670        }
671    }
672}
673
674/// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable
675#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
676#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
677pub enum SinglePos {
678    Format1(SinglePosFormat1),
679    Format2(SinglePosFormat2),
680}
681
682impl SinglePos {
683    /// Construct a new `SinglePosFormat1` subtable
684    pub fn format_1(coverage: CoverageTable, value_record: ValueRecord) -> Self {
685        Self::Format1(SinglePosFormat1::new(coverage, value_record))
686    }
687
688    /// Construct a new `SinglePosFormat2` subtable
689    pub fn format_2(coverage: CoverageTable, value_records: Vec<ValueRecord>) -> Self {
690        Self::Format2(SinglePosFormat2::new(coverage, value_records))
691    }
692}
693
694impl Default for SinglePos {
695    fn default() -> Self {
696        Self::Format1(Default::default())
697    }
698}
699
700impl FontWrite for SinglePos {
701    fn write_into(&self, writer: &mut TableWriter) {
702        match self {
703            Self::Format1(item) => item.write_into(writer),
704            Self::Format2(item) => item.write_into(writer),
705        }
706    }
707    fn table_type(&self) -> TableType {
708        match self {
709            Self::Format1(item) => item.table_type(),
710            Self::Format2(item) => item.table_type(),
711        }
712    }
713}
714
715impl Validate for SinglePos {
716    fn validate_impl(&self, ctx: &mut ValidationCtx) {
717        match self {
718            Self::Format1(item) => item.validate_impl(ctx),
719            Self::Format2(item) => item.validate_impl(ctx),
720        }
721    }
722}
723
724impl FromObjRef<read_fonts::tables::gpos::SinglePos<'_>> for SinglePos {
725    fn from_obj_ref(obj: &read_fonts::tables::gpos::SinglePos, _: FontData) -> Self {
726        use read_fonts::tables::gpos::SinglePos as ObjRefType;
727        match obj {
728            ObjRefType::Format1(item) => SinglePos::Format1(item.to_owned_table()),
729            ObjRefType::Format2(item) => SinglePos::Format2(item.to_owned_table()),
730        }
731    }
732}
733
734impl FromTableRef<read_fonts::tables::gpos::SinglePos<'_>> for SinglePos {}
735
736impl<'a> FontRead<'a> for SinglePos {
737    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
738        <read_fonts::tables::gpos::SinglePos as FontRead>::read(data).map(|x| x.to_owned_table())
739    }
740}
741
742impl From<SinglePosFormat1> for SinglePos {
743    fn from(src: SinglePosFormat1) -> SinglePos {
744        SinglePos::Format1(src)
745    }
746}
747
748impl From<SinglePosFormat2> for SinglePos {
749    fn from(src: SinglePosFormat2) -> SinglePos {
750        SinglePos::Format2(src)
751    }
752}
753
754/// [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
755#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
756#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
757pub struct SinglePosFormat1 {
758    /// Offset to Coverage table, from beginning of SinglePos subtable.
759    pub coverage: OffsetMarker<CoverageTable>,
760    /// Defines positioning value(s) — applied to all glyphs in the
761    /// Coverage table.
762    pub value_record: ValueRecord,
763}
764
765impl SinglePosFormat1 {
766    /// Construct a new `SinglePosFormat1`
767    pub fn new(coverage: CoverageTable, value_record: ValueRecord) -> Self {
768        Self {
769            coverage: coverage.into(),
770            value_record,
771        }
772    }
773}
774
775impl FontWrite for SinglePosFormat1 {
776    #[allow(clippy::unnecessary_cast)]
777    fn write_into(&self, writer: &mut TableWriter) {
778        (1 as u16).write_into(writer);
779        self.coverage.write_into(writer);
780        (self.compute_value_format() as ValueFormat).write_into(writer);
781        self.value_record.write_into(writer);
782    }
783    fn table_type(&self) -> TableType {
784        TableType::Named("SinglePosFormat1")
785    }
786}
787
788impl Validate for SinglePosFormat1 {
789    fn validate_impl(&self, ctx: &mut ValidationCtx) {
790        ctx.in_table("SinglePosFormat1", |ctx| {
791            ctx.in_field("coverage", |ctx| {
792                self.coverage.validate_impl(ctx);
793            });
794        })
795    }
796}
797
798impl<'a> FromObjRef<read_fonts::tables::gpos::SinglePosFormat1<'a>> for SinglePosFormat1 {
799    fn from_obj_ref(obj: &read_fonts::tables::gpos::SinglePosFormat1<'a>, _: FontData) -> Self {
800        let offset_data = obj.offset_data();
801        SinglePosFormat1 {
802            coverage: obj.coverage().to_owned_table(),
803            value_record: obj.value_record().to_owned_obj(offset_data),
804        }
805    }
806}
807
808#[allow(clippy::needless_lifetimes)]
809impl<'a> FromTableRef<read_fonts::tables::gpos::SinglePosFormat1<'a>> for SinglePosFormat1 {}
810
811impl<'a> FontRead<'a> for SinglePosFormat1 {
812    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
813        <read_fonts::tables::gpos::SinglePosFormat1 as FontRead>::read(data)
814            .map(|x| x.to_owned_table())
815    }
816}
817
818/// [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
819#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
820#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
821pub struct SinglePosFormat2 {
822    /// Offset to Coverage table, from beginning of SinglePos subtable.
823    pub coverage: OffsetMarker<CoverageTable>,
824    /// Array of ValueRecords — positioning values applied to glyphs.
825    pub value_records: Vec<ValueRecord>,
826}
827
828impl SinglePosFormat2 {
829    /// Construct a new `SinglePosFormat2`
830    pub fn new(coverage: CoverageTable, value_records: Vec<ValueRecord>) -> Self {
831        Self {
832            coverage: coverage.into(),
833            value_records,
834        }
835    }
836}
837
838impl FontWrite for SinglePosFormat2 {
839    #[allow(clippy::unnecessary_cast)]
840    fn write_into(&self, writer: &mut TableWriter) {
841        (2 as u16).write_into(writer);
842        self.coverage.write_into(writer);
843        (self.compute_value_format() as ValueFormat).write_into(writer);
844        (u16::try_from(array_len(&self.value_records)).unwrap()).write_into(writer);
845        self.value_records.write_into(writer);
846    }
847    fn table_type(&self) -> TableType {
848        TableType::Named("SinglePosFormat2")
849    }
850}
851
852impl Validate for SinglePosFormat2 {
853    fn validate_impl(&self, ctx: &mut ValidationCtx) {
854        ctx.in_table("SinglePosFormat2", |ctx| {
855            ctx.in_field("coverage", |ctx| {
856                self.coverage.validate_impl(ctx);
857            });
858            ctx.in_field("value_records", |ctx| {
859                if self.value_records.len() > (u16::MAX as usize) {
860                    ctx.report("array exceeds max length");
861                }
862                self.value_records.validate_impl(ctx);
863            });
864        })
865    }
866}
867
868impl<'a> FromObjRef<read_fonts::tables::gpos::SinglePosFormat2<'a>> for SinglePosFormat2 {
869    fn from_obj_ref(obj: &read_fonts::tables::gpos::SinglePosFormat2<'a>, _: FontData) -> Self {
870        let offset_data = obj.offset_data();
871        SinglePosFormat2 {
872            coverage: obj.coverage().to_owned_table(),
873            value_records: obj
874                .value_records()
875                .iter()
876                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
877                .collect(),
878        }
879    }
880}
881
882#[allow(clippy::needless_lifetimes)]
883impl<'a> FromTableRef<read_fonts::tables::gpos::SinglePosFormat2<'a>> for SinglePosFormat2 {}
884
885impl<'a> FontRead<'a> for SinglePosFormat2 {
886    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
887        <read_fonts::tables::gpos::SinglePosFormat2 as FontRead>::read(data)
888            .map(|x| x.to_owned_table())
889    }
890}
891
892/// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable
893#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
894#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
895pub enum PairPos {
896    Format1(PairPosFormat1),
897    Format2(PairPosFormat2),
898}
899
900impl PairPos {
901    /// Construct a new `PairPosFormat1` subtable
902    pub fn format_1(coverage: CoverageTable, pair_sets: Vec<PairSet>) -> Self {
903        Self::Format1(PairPosFormat1::new(coverage, pair_sets))
904    }
905
906    /// Construct a new `PairPosFormat2` subtable
907    pub fn format_2(
908        coverage: CoverageTable,
909        class_def1: ClassDef,
910        class_def2: ClassDef,
911        class1_records: Vec<Class1Record>,
912    ) -> Self {
913        Self::Format2(PairPosFormat2::new(
914            coverage,
915            class_def1,
916            class_def2,
917            class1_records,
918        ))
919    }
920}
921
922impl Default for PairPos {
923    fn default() -> Self {
924        Self::Format1(Default::default())
925    }
926}
927
928impl FontWrite for PairPos {
929    fn write_into(&self, writer: &mut TableWriter) {
930        match self {
931            Self::Format1(item) => item.write_into(writer),
932            Self::Format2(item) => item.write_into(writer),
933        }
934    }
935    fn table_type(&self) -> TableType {
936        match self {
937            Self::Format1(item) => item.table_type(),
938            Self::Format2(item) => item.table_type(),
939        }
940    }
941}
942
943impl Validate for PairPos {
944    fn validate_impl(&self, ctx: &mut ValidationCtx) {
945        match self {
946            Self::Format1(item) => item.validate_impl(ctx),
947            Self::Format2(item) => item.validate_impl(ctx),
948        }
949    }
950}
951
952impl FromObjRef<read_fonts::tables::gpos::PairPos<'_>> for PairPos {
953    fn from_obj_ref(obj: &read_fonts::tables::gpos::PairPos, _: FontData) -> Self {
954        use read_fonts::tables::gpos::PairPos as ObjRefType;
955        match obj {
956            ObjRefType::Format1(item) => PairPos::Format1(item.to_owned_table()),
957            ObjRefType::Format2(item) => PairPos::Format2(item.to_owned_table()),
958        }
959    }
960}
961
962impl FromTableRef<read_fonts::tables::gpos::PairPos<'_>> for PairPos {}
963
964impl<'a> FontRead<'a> for PairPos {
965    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
966        <read_fonts::tables::gpos::PairPos as FontRead>::read(data).map(|x| x.to_owned_table())
967    }
968}
969
970impl From<PairPosFormat1> for PairPos {
971    fn from(src: PairPosFormat1) -> PairPos {
972        PairPos::Format1(src)
973    }
974}
975
976impl From<PairPosFormat2> for PairPos {
977    fn from(src: PairPosFormat2) -> PairPos {
978        PairPos::Format2(src)
979    }
980}
981
982/// [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
983#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
984#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
985pub struct PairPosFormat1 {
986    /// Offset to Coverage table, from beginning of PairPos subtable.
987    pub coverage: OffsetMarker<CoverageTable>,
988    /// Array of offsets to PairSet tables. Offsets are from beginning
989    /// of PairPos subtable, ordered by Coverage Index.
990    pub pair_sets: Vec<OffsetMarker<PairSet>>,
991}
992
993impl PairPosFormat1 {
994    /// Construct a new `PairPosFormat1`
995    pub fn new(coverage: CoverageTable, pair_sets: Vec<PairSet>) -> Self {
996        Self {
997            coverage: coverage.into(),
998            pair_sets: pair_sets.into_iter().map(Into::into).collect(),
999        }
1000    }
1001}
1002
1003impl FontWrite for PairPosFormat1 {
1004    #[allow(clippy::unnecessary_cast)]
1005    fn write_into(&self, writer: &mut TableWriter) {
1006        (1 as u16).write_into(writer);
1007        self.coverage.write_into(writer);
1008        (self.compute_value_format1() as ValueFormat).write_into(writer);
1009        (self.compute_value_format2() as ValueFormat).write_into(writer);
1010        (u16::try_from(array_len(&self.pair_sets)).unwrap()).write_into(writer);
1011        self.pair_sets.write_into(writer);
1012    }
1013    fn table_type(&self) -> TableType {
1014        TableType::Named("PairPosFormat1")
1015    }
1016}
1017
1018impl Validate for PairPosFormat1 {
1019    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1020        ctx.in_table("PairPosFormat1", |ctx| {
1021            ctx.in_field("coverage", |ctx| {
1022                self.coverage.validate_impl(ctx);
1023            });
1024            ctx.in_field("pair_sets", |ctx| {
1025                if self.pair_sets.len() > (u16::MAX as usize) {
1026                    ctx.report("array exceeds max length");
1027                }
1028                self.check_format_consistency(ctx);
1029            });
1030        })
1031    }
1032}
1033
1034impl<'a> FromObjRef<read_fonts::tables::gpos::PairPosFormat1<'a>> for PairPosFormat1 {
1035    fn from_obj_ref(obj: &read_fonts::tables::gpos::PairPosFormat1<'a>, _: FontData) -> Self {
1036        PairPosFormat1 {
1037            coverage: obj.coverage().to_owned_table(),
1038            pair_sets: obj.pair_sets().to_owned_table(),
1039        }
1040    }
1041}
1042
1043#[allow(clippy::needless_lifetimes)]
1044impl<'a> FromTableRef<read_fonts::tables::gpos::PairPosFormat1<'a>> for PairPosFormat1 {}
1045
1046impl<'a> FontRead<'a> for PairPosFormat1 {
1047    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1048        <read_fonts::tables::gpos::PairPosFormat1 as FontRead>::read(data)
1049            .map(|x| x.to_owned_table())
1050    }
1051}
1052
1053/// Part of [PairPosFormat1]
1054#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1055#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1056pub struct PairSet {
1057    /// Array of PairValueRecords, ordered by glyph ID of the second
1058    /// glyph.
1059    pub pair_value_records: Vec<PairValueRecord>,
1060}
1061
1062impl PairSet {
1063    /// Construct a new `PairSet`
1064    pub fn new(pair_value_records: Vec<PairValueRecord>) -> Self {
1065        Self { pair_value_records }
1066    }
1067}
1068
1069impl FontWrite for PairSet {
1070    #[allow(clippy::unnecessary_cast)]
1071    fn write_into(&self, writer: &mut TableWriter) {
1072        (u16::try_from(array_len(&self.pair_value_records)).unwrap()).write_into(writer);
1073        self.pair_value_records.write_into(writer);
1074    }
1075    fn table_type(&self) -> TableType {
1076        TableType::Named("PairSet")
1077    }
1078}
1079
1080impl Validate for PairSet {
1081    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1082        ctx.in_table("PairSet", |ctx| {
1083            ctx.in_field("pair_value_records", |ctx| {
1084                if self.pair_value_records.len() > (u16::MAX as usize) {
1085                    ctx.report("array exceeds max length");
1086                }
1087                self.pair_value_records.validate_impl(ctx);
1088            });
1089        })
1090    }
1091}
1092
1093impl<'a> FromObjRef<read_fonts::tables::gpos::PairSet<'a>> for PairSet {
1094    fn from_obj_ref(obj: &read_fonts::tables::gpos::PairSet<'a>, _: FontData) -> Self {
1095        let offset_data = obj.offset_data();
1096        PairSet {
1097            pair_value_records: obj
1098                .pair_value_records()
1099                .iter()
1100                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
1101                .collect(),
1102        }
1103    }
1104}
1105
1106#[allow(clippy::needless_lifetimes)]
1107impl<'a> FromTableRef<read_fonts::tables::gpos::PairSet<'a>> for PairSet {}
1108
1109/// Part of [PairSet]
1110#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1111#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1112pub struct PairValueRecord {
1113    /// Glyph ID of second glyph in the pair (first glyph is listed in
1114    /// the Coverage table).
1115    pub second_glyph: GlyphId16,
1116    /// Positioning data for the first glyph in the pair.
1117    pub value_record1: ValueRecord,
1118    /// Positioning data for the second glyph in the pair.
1119    pub value_record2: ValueRecord,
1120}
1121
1122impl PairValueRecord {
1123    /// Construct a new `PairValueRecord`
1124    pub fn new(
1125        second_glyph: GlyphId16,
1126        value_record1: ValueRecord,
1127        value_record2: ValueRecord,
1128    ) -> Self {
1129        Self {
1130            second_glyph,
1131            value_record1,
1132            value_record2,
1133        }
1134    }
1135}
1136
1137impl FontWrite for PairValueRecord {
1138    fn write_into(&self, writer: &mut TableWriter) {
1139        self.second_glyph.write_into(writer);
1140        self.value_record1.write_into(writer);
1141        self.value_record2.write_into(writer);
1142    }
1143    fn table_type(&self) -> TableType {
1144        TableType::Named("PairValueRecord")
1145    }
1146}
1147
1148impl Validate for PairValueRecord {
1149    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
1150}
1151
1152impl FromObjRef<read_fonts::tables::gpos::PairValueRecord> for PairValueRecord {
1153    fn from_obj_ref(
1154        obj: &read_fonts::tables::gpos::PairValueRecord,
1155        offset_data: FontData,
1156    ) -> Self {
1157        PairValueRecord {
1158            second_glyph: obj.second_glyph(),
1159            value_record1: obj.value_record1().to_owned_obj(offset_data),
1160            value_record2: obj.value_record2().to_owned_obj(offset_data),
1161        }
1162    }
1163}
1164
1165/// [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
1166#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1167#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1168pub struct PairPosFormat2 {
1169    /// Offset to Coverage table, from beginning of PairPos subtable.
1170    pub coverage: OffsetMarker<CoverageTable>,
1171    /// Offset to ClassDef table, from beginning of PairPos subtable
1172    /// — for the first glyph of the pair.
1173    pub class_def1: OffsetMarker<ClassDef>,
1174    /// Offset to ClassDef table, from beginning of PairPos subtable
1175    /// — for the second glyph of the pair.
1176    pub class_def2: OffsetMarker<ClassDef>,
1177    /// Array of Class1 records, ordered by classes in classDef1.
1178    pub class1_records: Vec<Class1Record>,
1179}
1180
1181impl PairPosFormat2 {
1182    /// Construct a new `PairPosFormat2`
1183    pub fn new(
1184        coverage: CoverageTable,
1185        class_def1: ClassDef,
1186        class_def2: ClassDef,
1187        class1_records: Vec<Class1Record>,
1188    ) -> Self {
1189        Self {
1190            coverage: coverage.into(),
1191            class_def1: class_def1.into(),
1192            class_def2: class_def2.into(),
1193            class1_records,
1194        }
1195    }
1196}
1197
1198impl FontWrite for PairPosFormat2 {
1199    #[allow(clippy::unnecessary_cast)]
1200    fn write_into(&self, writer: &mut TableWriter) {
1201        (2 as u16).write_into(writer);
1202        self.coverage.write_into(writer);
1203        (self.compute_value_format1() as ValueFormat).write_into(writer);
1204        (self.compute_value_format2() as ValueFormat).write_into(writer);
1205        self.class_def1.write_into(writer);
1206        self.class_def2.write_into(writer);
1207        (self.compute_class1_count() as u16).write_into(writer);
1208        (self.compute_class2_count() as u16).write_into(writer);
1209        self.class1_records.write_into(writer);
1210    }
1211    fn table_type(&self) -> TableType {
1212        TableType::Named("PairPosFormat2")
1213    }
1214}
1215
1216impl Validate for PairPosFormat2 {
1217    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1218        ctx.in_table("PairPosFormat2", |ctx| {
1219            ctx.in_field("coverage", |ctx| {
1220                self.coverage.validate_impl(ctx);
1221            });
1222            ctx.in_field("class_def1", |ctx| {
1223                self.class_def1.validate_impl(ctx);
1224            });
1225            ctx.in_field("class_def2", |ctx| {
1226                self.class_def2.validate_impl(ctx);
1227            });
1228            ctx.in_field("class1_records", |ctx| {
1229                if self.class1_records.len() > (u16::MAX as usize) {
1230                    ctx.report("array exceeds max length");
1231                }
1232                self.class1_records.validate_impl(ctx);
1233            });
1234            self.check_length_and_format_conformance(ctx);
1235        })
1236    }
1237}
1238
1239impl<'a> FromObjRef<read_fonts::tables::gpos::PairPosFormat2<'a>> for PairPosFormat2 {
1240    fn from_obj_ref(obj: &read_fonts::tables::gpos::PairPosFormat2<'a>, _: FontData) -> Self {
1241        let offset_data = obj.offset_data();
1242        PairPosFormat2 {
1243            coverage: obj.coverage().to_owned_table(),
1244            class_def1: obj.class_def1().to_owned_table(),
1245            class_def2: obj.class_def2().to_owned_table(),
1246            class1_records: obj
1247                .class1_records()
1248                .iter()
1249                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
1250                .collect(),
1251        }
1252    }
1253}
1254
1255#[allow(clippy::needless_lifetimes)]
1256impl<'a> FromTableRef<read_fonts::tables::gpos::PairPosFormat2<'a>> for PairPosFormat2 {}
1257
1258impl<'a> FontRead<'a> for PairPosFormat2 {
1259    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1260        <read_fonts::tables::gpos::PairPosFormat2 as FontRead>::read(data)
1261            .map(|x| x.to_owned_table())
1262    }
1263}
1264
1265/// Part of [PairPosFormat2]
1266#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1267#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1268pub struct Class1Record {
1269    /// Array of Class2 records, ordered by classes in classDef2.
1270    pub class2_records: Vec<Class2Record>,
1271}
1272
1273impl Class1Record {
1274    /// Construct a new `Class1Record`
1275    pub fn new(class2_records: Vec<Class2Record>) -> Self {
1276        Self { class2_records }
1277    }
1278}
1279
1280impl FontWrite for Class1Record {
1281    fn write_into(&self, writer: &mut TableWriter) {
1282        self.class2_records.write_into(writer);
1283    }
1284    fn table_type(&self) -> TableType {
1285        TableType::Named("Class1Record")
1286    }
1287}
1288
1289impl Validate for Class1Record {
1290    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1291        ctx.in_table("Class1Record", |ctx| {
1292            ctx.in_field("class2_records", |ctx| {
1293                if self.class2_records.len() > (u16::MAX as usize) {
1294                    ctx.report("array exceeds max length");
1295                }
1296                self.class2_records.validate_impl(ctx);
1297            });
1298        })
1299    }
1300}
1301
1302impl FromObjRef<read_fonts::tables::gpos::Class1Record<'_>> for Class1Record {
1303    fn from_obj_ref(obj: &read_fonts::tables::gpos::Class1Record, offset_data: FontData) -> Self {
1304        Class1Record {
1305            class2_records: obj
1306                .class2_records()
1307                .iter()
1308                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
1309                .collect(),
1310        }
1311    }
1312}
1313
1314/// Part of [PairPosFormat2]
1315#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1316#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1317pub struct Class2Record {
1318    /// Positioning for first glyph — empty if valueFormat1 = 0.
1319    pub value_record1: ValueRecord,
1320    /// Positioning for second glyph — empty if valueFormat2 = 0.
1321    pub value_record2: ValueRecord,
1322}
1323
1324impl Class2Record {
1325    /// Construct a new `Class2Record`
1326    pub fn new(value_record1: ValueRecord, value_record2: ValueRecord) -> Self {
1327        Self {
1328            value_record1,
1329            value_record2,
1330        }
1331    }
1332}
1333
1334impl FontWrite for Class2Record {
1335    fn write_into(&self, writer: &mut TableWriter) {
1336        self.value_record1.write_into(writer);
1337        self.value_record2.write_into(writer);
1338    }
1339    fn table_type(&self) -> TableType {
1340        TableType::Named("Class2Record")
1341    }
1342}
1343
1344impl Validate for Class2Record {
1345    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
1346}
1347
1348impl FromObjRef<read_fonts::tables::gpos::Class2Record> for Class2Record {
1349    fn from_obj_ref(obj: &read_fonts::tables::gpos::Class2Record, offset_data: FontData) -> Self {
1350        Class2Record {
1351            value_record1: obj.value_record1().to_owned_obj(offset_data),
1352            value_record2: obj.value_record2().to_owned_obj(offset_data),
1353        }
1354    }
1355}
1356
1357/// [Cursive Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#cursive-attachment-positioning-format1-cursive-attachment): Cursvie attachment
1358#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1360pub struct CursivePosFormat1 {
1361    /// Offset to Coverage table, from beginning of CursivePos subtable.
1362    pub coverage: OffsetMarker<CoverageTable>,
1363    /// Array of EntryExit records, in Coverage index order.
1364    pub entry_exit_record: Vec<EntryExitRecord>,
1365}
1366
1367impl CursivePosFormat1 {
1368    /// Construct a new `CursivePosFormat1`
1369    pub fn new(coverage: CoverageTable, entry_exit_record: Vec<EntryExitRecord>) -> Self {
1370        Self {
1371            coverage: coverage.into(),
1372            entry_exit_record,
1373        }
1374    }
1375}
1376
1377impl FontWrite for CursivePosFormat1 {
1378    #[allow(clippy::unnecessary_cast)]
1379    fn write_into(&self, writer: &mut TableWriter) {
1380        (1 as u16).write_into(writer);
1381        self.coverage.write_into(writer);
1382        (u16::try_from(array_len(&self.entry_exit_record)).unwrap()).write_into(writer);
1383        self.entry_exit_record.write_into(writer);
1384    }
1385    fn table_type(&self) -> TableType {
1386        TableType::Named("CursivePosFormat1")
1387    }
1388}
1389
1390impl Validate for CursivePosFormat1 {
1391    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1392        ctx.in_table("CursivePosFormat1", |ctx| {
1393            ctx.in_field("coverage", |ctx| {
1394                self.coverage.validate_impl(ctx);
1395            });
1396            ctx.in_field("entry_exit_record", |ctx| {
1397                if self.entry_exit_record.len() > (u16::MAX as usize) {
1398                    ctx.report("array exceeds max length");
1399                }
1400                self.entry_exit_record.validate_impl(ctx);
1401            });
1402        })
1403    }
1404}
1405
1406impl<'a> FromObjRef<read_fonts::tables::gpos::CursivePosFormat1<'a>> for CursivePosFormat1 {
1407    fn from_obj_ref(obj: &read_fonts::tables::gpos::CursivePosFormat1<'a>, _: FontData) -> Self {
1408        let offset_data = obj.offset_data();
1409        CursivePosFormat1 {
1410            coverage: obj.coverage().to_owned_table(),
1411            entry_exit_record: obj.entry_exit_record().to_owned_obj(offset_data),
1412        }
1413    }
1414}
1415
1416#[allow(clippy::needless_lifetimes)]
1417impl<'a> FromTableRef<read_fonts::tables::gpos::CursivePosFormat1<'a>> for CursivePosFormat1 {}
1418
1419impl<'a> FontRead<'a> for CursivePosFormat1 {
1420    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1421        <read_fonts::tables::gpos::CursivePosFormat1 as FontRead>::read(data)
1422            .map(|x| x.to_owned_table())
1423    }
1424}
1425
1426/// Part of [CursivePosFormat1]
1427#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1428#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1429pub struct EntryExitRecord {
1430    /// Offset to entryAnchor table, from beginning of CursivePos
1431    /// subtable (may be NULL).
1432    pub entry_anchor: NullableOffsetMarker<AnchorTable>,
1433    /// Offset to exitAnchor table, from beginning of CursivePos
1434    /// subtable (may be NULL).
1435    pub exit_anchor: NullableOffsetMarker<AnchorTable>,
1436}
1437
1438impl EntryExitRecord {
1439    /// Construct a new `EntryExitRecord`
1440    pub fn new(entry_anchor: Option<AnchorTable>, exit_anchor: Option<AnchorTable>) -> Self {
1441        Self {
1442            entry_anchor: entry_anchor.into(),
1443            exit_anchor: exit_anchor.into(),
1444        }
1445    }
1446}
1447
1448impl FontWrite for EntryExitRecord {
1449    fn write_into(&self, writer: &mut TableWriter) {
1450        self.entry_anchor.write_into(writer);
1451        self.exit_anchor.write_into(writer);
1452    }
1453    fn table_type(&self) -> TableType {
1454        TableType::Named("EntryExitRecord")
1455    }
1456}
1457
1458impl Validate for EntryExitRecord {
1459    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1460        ctx.in_table("EntryExitRecord", |ctx| {
1461            ctx.in_field("entry_anchor", |ctx| {
1462                self.entry_anchor.validate_impl(ctx);
1463            });
1464            ctx.in_field("exit_anchor", |ctx| {
1465                self.exit_anchor.validate_impl(ctx);
1466            });
1467        })
1468    }
1469}
1470
1471impl FromObjRef<read_fonts::tables::gpos::EntryExitRecord> for EntryExitRecord {
1472    fn from_obj_ref(
1473        obj: &read_fonts::tables::gpos::EntryExitRecord,
1474        offset_data: FontData,
1475    ) -> Self {
1476        EntryExitRecord {
1477            entry_anchor: obj.entry_anchor(offset_data).to_owned_table(),
1478            exit_anchor: obj.exit_anchor(offset_data).to_owned_table(),
1479        }
1480    }
1481}
1482
1483/// [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
1484#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1485#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1486pub struct MarkBasePosFormat1 {
1487    /// Offset to markCoverage table, from beginning of MarkBasePos
1488    /// subtable.
1489    pub mark_coverage: OffsetMarker<CoverageTable>,
1490    /// Offset to baseCoverage table, from beginning of MarkBasePos
1491    /// subtable.
1492    pub base_coverage: OffsetMarker<CoverageTable>,
1493    /// Offset to MarkArray table, from beginning of MarkBasePos
1494    /// subtable.
1495    pub mark_array: OffsetMarker<MarkArray>,
1496    /// Offset to BaseArray table, from beginning of MarkBasePos
1497    /// subtable.
1498    pub base_array: OffsetMarker<BaseArray>,
1499}
1500
1501impl MarkBasePosFormat1 {
1502    /// Construct a new `MarkBasePosFormat1`
1503    pub fn new(
1504        mark_coverage: CoverageTable,
1505        base_coverage: CoverageTable,
1506        mark_array: MarkArray,
1507        base_array: BaseArray,
1508    ) -> Self {
1509        Self {
1510            mark_coverage: mark_coverage.into(),
1511            base_coverage: base_coverage.into(),
1512            mark_array: mark_array.into(),
1513            base_array: base_array.into(),
1514        }
1515    }
1516}
1517
1518impl FontWrite for MarkBasePosFormat1 {
1519    #[allow(clippy::unnecessary_cast)]
1520    fn write_into(&self, writer: &mut TableWriter) {
1521        (1 as u16).write_into(writer);
1522        self.mark_coverage.write_into(writer);
1523        self.base_coverage.write_into(writer);
1524        (self.compute_mark_class_count() as u16).write_into(writer);
1525        self.mark_array.write_into(writer);
1526        self.base_array.write_into(writer);
1527    }
1528    fn table_type(&self) -> TableType {
1529        TableType::Named("MarkBasePosFormat1")
1530    }
1531}
1532
1533impl Validate for MarkBasePosFormat1 {
1534    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1535        ctx.in_table("MarkBasePosFormat1", |ctx| {
1536            ctx.in_field("mark_coverage", |ctx| {
1537                self.mark_coverage.validate_impl(ctx);
1538            });
1539            ctx.in_field("base_coverage", |ctx| {
1540                self.base_coverage.validate_impl(ctx);
1541            });
1542            ctx.in_field("mark_array", |ctx| {
1543                self.mark_array.validate_impl(ctx);
1544            });
1545            ctx.in_field("base_array", |ctx| {
1546                self.base_array.validate_impl(ctx);
1547            });
1548        })
1549    }
1550}
1551
1552impl<'a> FromObjRef<read_fonts::tables::gpos::MarkBasePosFormat1<'a>> for MarkBasePosFormat1 {
1553    fn from_obj_ref(obj: &read_fonts::tables::gpos::MarkBasePosFormat1<'a>, _: FontData) -> Self {
1554        MarkBasePosFormat1 {
1555            mark_coverage: obj.mark_coverage().to_owned_table(),
1556            base_coverage: obj.base_coverage().to_owned_table(),
1557            mark_array: obj.mark_array().to_owned_table(),
1558            base_array: obj.base_array().to_owned_table(),
1559        }
1560    }
1561}
1562
1563#[allow(clippy::needless_lifetimes)]
1564impl<'a> FromTableRef<read_fonts::tables::gpos::MarkBasePosFormat1<'a>> for MarkBasePosFormat1 {}
1565
1566impl<'a> FontRead<'a> for MarkBasePosFormat1 {
1567    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1568        <read_fonts::tables::gpos::MarkBasePosFormat1 as FontRead>::read(data)
1569            .map(|x| x.to_owned_table())
1570    }
1571}
1572
1573/// Part of [MarkBasePosFormat1]
1574#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1575#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1576pub struct BaseArray {
1577    /// Array of BaseRecords, in order of baseCoverage Index.
1578    pub base_records: Vec<BaseRecord>,
1579}
1580
1581impl BaseArray {
1582    /// Construct a new `BaseArray`
1583    pub fn new(base_records: Vec<BaseRecord>) -> Self {
1584        Self { base_records }
1585    }
1586}
1587
1588impl FontWrite for BaseArray {
1589    #[allow(clippy::unnecessary_cast)]
1590    fn write_into(&self, writer: &mut TableWriter) {
1591        (u16::try_from(array_len(&self.base_records)).unwrap()).write_into(writer);
1592        self.base_records.write_into(writer);
1593    }
1594    fn table_type(&self) -> TableType {
1595        TableType::Named("BaseArray")
1596    }
1597}
1598
1599impl Validate for BaseArray {
1600    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1601        ctx.in_table("BaseArray", |ctx| {
1602            ctx.in_field("base_records", |ctx| {
1603                if self.base_records.len() > (u16::MAX as usize) {
1604                    ctx.report("array exceeds max length");
1605                }
1606                self.base_records.validate_impl(ctx);
1607            });
1608        })
1609    }
1610}
1611
1612impl<'a> FromObjRef<read_fonts::tables::gpos::BaseArray<'a>> for BaseArray {
1613    fn from_obj_ref(obj: &read_fonts::tables::gpos::BaseArray<'a>, _: FontData) -> Self {
1614        let offset_data = obj.offset_data();
1615        BaseArray {
1616            base_records: obj
1617                .base_records()
1618                .iter()
1619                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
1620                .collect(),
1621        }
1622    }
1623}
1624
1625#[allow(clippy::needless_lifetimes)]
1626impl<'a> FromTableRef<read_fonts::tables::gpos::BaseArray<'a>> for BaseArray {}
1627
1628/// Part of [BaseArray]
1629#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1630#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1631pub struct BaseRecord {
1632    /// Array of offsets (one per mark class) to Anchor tables. Offsets
1633    /// are from beginning of BaseArray table, ordered by class
1634    /// (offsets may be NULL).
1635    pub base_anchors: Vec<NullableOffsetMarker<AnchorTable>>,
1636}
1637
1638impl BaseRecord {
1639    /// Construct a new `BaseRecord`
1640    pub fn new(base_anchors: Vec<Option<AnchorTable>>) -> Self {
1641        Self {
1642            base_anchors: base_anchors.into_iter().map(Into::into).collect(),
1643        }
1644    }
1645}
1646
1647impl FontWrite for BaseRecord {
1648    fn write_into(&self, writer: &mut TableWriter) {
1649        self.base_anchors.write_into(writer);
1650    }
1651    fn table_type(&self) -> TableType {
1652        TableType::Named("BaseRecord")
1653    }
1654}
1655
1656impl Validate for BaseRecord {
1657    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1658        ctx.in_table("BaseRecord", |ctx| {
1659            ctx.in_field("base_anchors", |ctx| {
1660                if self.base_anchors.len() > (u16::MAX as usize) {
1661                    ctx.report("array exceeds max length");
1662                }
1663                self.base_anchors.validate_impl(ctx);
1664            });
1665        })
1666    }
1667}
1668
1669impl FromObjRef<read_fonts::tables::gpos::BaseRecord<'_>> for BaseRecord {
1670    fn from_obj_ref(obj: &read_fonts::tables::gpos::BaseRecord, offset_data: FontData) -> Self {
1671        BaseRecord {
1672            base_anchors: obj.base_anchors(offset_data).to_owned_table(),
1673        }
1674    }
1675}
1676
1677/// [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
1678#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1679#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1680pub struct MarkLigPosFormat1 {
1681    /// Offset to markCoverage table, from beginning of MarkLigPos
1682    /// subtable.
1683    pub mark_coverage: OffsetMarker<CoverageTable>,
1684    /// Offset to ligatureCoverage table, from beginning of MarkLigPos
1685    /// subtable.
1686    pub ligature_coverage: OffsetMarker<CoverageTable>,
1687    /// Offset to MarkArray table, from beginning of MarkLigPos
1688    /// subtable.
1689    pub mark_array: OffsetMarker<MarkArray>,
1690    /// Offset to LigatureArray table, from beginning of MarkLigPos
1691    /// subtable.
1692    pub ligature_array: OffsetMarker<LigatureArray>,
1693}
1694
1695impl MarkLigPosFormat1 {
1696    /// Construct a new `MarkLigPosFormat1`
1697    pub fn new(
1698        mark_coverage: CoverageTable,
1699        ligature_coverage: CoverageTable,
1700        mark_array: MarkArray,
1701        ligature_array: LigatureArray,
1702    ) -> Self {
1703        Self {
1704            mark_coverage: mark_coverage.into(),
1705            ligature_coverage: ligature_coverage.into(),
1706            mark_array: mark_array.into(),
1707            ligature_array: ligature_array.into(),
1708        }
1709    }
1710}
1711
1712impl FontWrite for MarkLigPosFormat1 {
1713    #[allow(clippy::unnecessary_cast)]
1714    fn write_into(&self, writer: &mut TableWriter) {
1715        (1 as u16).write_into(writer);
1716        self.mark_coverage.write_into(writer);
1717        self.ligature_coverage.write_into(writer);
1718        (self.compute_mark_class_count() as u16).write_into(writer);
1719        self.mark_array.write_into(writer);
1720        self.ligature_array.write_into(writer);
1721    }
1722    fn table_type(&self) -> TableType {
1723        TableType::Named("MarkLigPosFormat1")
1724    }
1725}
1726
1727impl Validate for MarkLigPosFormat1 {
1728    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1729        ctx.in_table("MarkLigPosFormat1", |ctx| {
1730            ctx.in_field("mark_coverage", |ctx| {
1731                self.mark_coverage.validate_impl(ctx);
1732            });
1733            ctx.in_field("ligature_coverage", |ctx| {
1734                self.ligature_coverage.validate_impl(ctx);
1735            });
1736            ctx.in_field("mark_array", |ctx| {
1737                self.mark_array.validate_impl(ctx);
1738            });
1739            ctx.in_field("ligature_array", |ctx| {
1740                self.ligature_array.validate_impl(ctx);
1741            });
1742        })
1743    }
1744}
1745
1746impl<'a> FromObjRef<read_fonts::tables::gpos::MarkLigPosFormat1<'a>> for MarkLigPosFormat1 {
1747    fn from_obj_ref(obj: &read_fonts::tables::gpos::MarkLigPosFormat1<'a>, _: FontData) -> Self {
1748        MarkLigPosFormat1 {
1749            mark_coverage: obj.mark_coverage().to_owned_table(),
1750            ligature_coverage: obj.ligature_coverage().to_owned_table(),
1751            mark_array: obj.mark_array().to_owned_table(),
1752            ligature_array: obj.ligature_array().to_owned_table(),
1753        }
1754    }
1755}
1756
1757#[allow(clippy::needless_lifetimes)]
1758impl<'a> FromTableRef<read_fonts::tables::gpos::MarkLigPosFormat1<'a>> for MarkLigPosFormat1 {}
1759
1760impl<'a> FontRead<'a> for MarkLigPosFormat1 {
1761    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
1762        <read_fonts::tables::gpos::MarkLigPosFormat1 as FontRead>::read(data)
1763            .map(|x| x.to_owned_table())
1764    }
1765}
1766
1767/// Part of [MarkLigPosFormat1]
1768#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1769#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1770pub struct LigatureArray {
1771    /// Array of offsets to LigatureAttach tables. Offsets are from
1772    /// beginning of LigatureArray table, ordered by ligatureCoverage
1773    /// index.
1774    pub ligature_attaches: Vec<OffsetMarker<LigatureAttach>>,
1775}
1776
1777impl LigatureArray {
1778    /// Construct a new `LigatureArray`
1779    pub fn new(ligature_attaches: Vec<LigatureAttach>) -> Self {
1780        Self {
1781            ligature_attaches: ligature_attaches.into_iter().map(Into::into).collect(),
1782        }
1783    }
1784}
1785
1786impl FontWrite for LigatureArray {
1787    #[allow(clippy::unnecessary_cast)]
1788    fn write_into(&self, writer: &mut TableWriter) {
1789        (u16::try_from(array_len(&self.ligature_attaches)).unwrap()).write_into(writer);
1790        self.ligature_attaches.write_into(writer);
1791    }
1792    fn table_type(&self) -> TableType {
1793        TableType::Named("LigatureArray")
1794    }
1795}
1796
1797impl Validate for LigatureArray {
1798    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1799        ctx.in_table("LigatureArray", |ctx| {
1800            ctx.in_field("ligature_attaches", |ctx| {
1801                if self.ligature_attaches.len() > (u16::MAX as usize) {
1802                    ctx.report("array exceeds max length");
1803                }
1804                self.ligature_attaches.validate_impl(ctx);
1805            });
1806        })
1807    }
1808}
1809
1810impl<'a> FromObjRef<read_fonts::tables::gpos::LigatureArray<'a>> for LigatureArray {
1811    fn from_obj_ref(obj: &read_fonts::tables::gpos::LigatureArray<'a>, _: FontData) -> Self {
1812        LigatureArray {
1813            ligature_attaches: obj.ligature_attaches().to_owned_table(),
1814        }
1815    }
1816}
1817
1818#[allow(clippy::needless_lifetimes)]
1819impl<'a> FromTableRef<read_fonts::tables::gpos::LigatureArray<'a>> for LigatureArray {}
1820
1821/// Part of [MarkLigPosFormat1]
1822#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1823#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1824pub struct LigatureAttach {
1825    /// Array of Component records, ordered in writing direction.
1826    pub component_records: Vec<ComponentRecord>,
1827}
1828
1829impl LigatureAttach {
1830    /// Construct a new `LigatureAttach`
1831    pub fn new(component_records: Vec<ComponentRecord>) -> Self {
1832        Self { component_records }
1833    }
1834}
1835
1836impl FontWrite for LigatureAttach {
1837    #[allow(clippy::unnecessary_cast)]
1838    fn write_into(&self, writer: &mut TableWriter) {
1839        (u16::try_from(array_len(&self.component_records)).unwrap()).write_into(writer);
1840        self.component_records.write_into(writer);
1841    }
1842    fn table_type(&self) -> TableType {
1843        TableType::Named("LigatureAttach")
1844    }
1845}
1846
1847impl Validate for LigatureAttach {
1848    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1849        ctx.in_table("LigatureAttach", |ctx| {
1850            ctx.in_field("component_records", |ctx| {
1851                if self.component_records.len() > (u16::MAX as usize) {
1852                    ctx.report("array exceeds max length");
1853                }
1854                self.component_records.validate_impl(ctx);
1855            });
1856        })
1857    }
1858}
1859
1860impl<'a> FromObjRef<read_fonts::tables::gpos::LigatureAttach<'a>> for LigatureAttach {
1861    fn from_obj_ref(obj: &read_fonts::tables::gpos::LigatureAttach<'a>, _: FontData) -> Self {
1862        let offset_data = obj.offset_data();
1863        LigatureAttach {
1864            component_records: obj
1865                .component_records()
1866                .iter()
1867                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
1868                .collect(),
1869        }
1870    }
1871}
1872
1873#[allow(clippy::needless_lifetimes)]
1874impl<'a> FromTableRef<read_fonts::tables::gpos::LigatureAttach<'a>> for LigatureAttach {}
1875
1876/// Part of [MarkLigPosFormat1]
1877#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1878#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1879pub struct ComponentRecord {
1880    /// Array of offsets (one per class) to Anchor tables. Offsets are
1881    /// from beginning of LigatureAttach table, ordered by class
1882    /// (offsets may be NULL).
1883    pub ligature_anchors: Vec<NullableOffsetMarker<AnchorTable>>,
1884}
1885
1886impl ComponentRecord {
1887    /// Construct a new `ComponentRecord`
1888    pub fn new(ligature_anchors: Vec<Option<AnchorTable>>) -> Self {
1889        Self {
1890            ligature_anchors: ligature_anchors.into_iter().map(Into::into).collect(),
1891        }
1892    }
1893}
1894
1895impl FontWrite for ComponentRecord {
1896    fn write_into(&self, writer: &mut TableWriter) {
1897        self.ligature_anchors.write_into(writer);
1898    }
1899    fn table_type(&self) -> TableType {
1900        TableType::Named("ComponentRecord")
1901    }
1902}
1903
1904impl Validate for ComponentRecord {
1905    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1906        ctx.in_table("ComponentRecord", |ctx| {
1907            ctx.in_field("ligature_anchors", |ctx| {
1908                if self.ligature_anchors.len() > (u16::MAX as usize) {
1909                    ctx.report("array exceeds max length");
1910                }
1911                self.ligature_anchors.validate_impl(ctx);
1912            });
1913        })
1914    }
1915}
1916
1917impl FromObjRef<read_fonts::tables::gpos::ComponentRecord<'_>> for ComponentRecord {
1918    fn from_obj_ref(
1919        obj: &read_fonts::tables::gpos::ComponentRecord,
1920        offset_data: FontData,
1921    ) -> Self {
1922        ComponentRecord {
1923            ligature_anchors: obj.ligature_anchors(offset_data).to_owned_table(),
1924        }
1925    }
1926}
1927
1928/// [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
1929#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
1930#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1931pub struct MarkMarkPosFormat1 {
1932    /// Offset to Combining Mark Coverage table, from beginning of
1933    /// MarkMarkPos subtable.
1934    pub mark1_coverage: OffsetMarker<CoverageTable>,
1935    /// Offset to Base Mark Coverage table, from beginning of
1936    /// MarkMarkPos subtable.
1937    pub mark2_coverage: OffsetMarker<CoverageTable>,
1938    /// Offset to MarkArray table for mark1, from beginning of
1939    /// MarkMarkPos subtable.
1940    pub mark1_array: OffsetMarker<MarkArray>,
1941    /// Offset to Mark2Array table for mark2, from beginning of
1942    /// MarkMarkPos subtable.
1943    pub mark2_array: OffsetMarker<Mark2Array>,
1944}
1945
1946impl MarkMarkPosFormat1 {
1947    /// Construct a new `MarkMarkPosFormat1`
1948    pub fn new(
1949        mark1_coverage: CoverageTable,
1950        mark2_coverage: CoverageTable,
1951        mark1_array: MarkArray,
1952        mark2_array: Mark2Array,
1953    ) -> Self {
1954        Self {
1955            mark1_coverage: mark1_coverage.into(),
1956            mark2_coverage: mark2_coverage.into(),
1957            mark1_array: mark1_array.into(),
1958            mark2_array: mark2_array.into(),
1959        }
1960    }
1961}
1962
1963impl FontWrite for MarkMarkPosFormat1 {
1964    #[allow(clippy::unnecessary_cast)]
1965    fn write_into(&self, writer: &mut TableWriter) {
1966        (1 as u16).write_into(writer);
1967        self.mark1_coverage.write_into(writer);
1968        self.mark2_coverage.write_into(writer);
1969        (self.compute_mark_class_count() as u16).write_into(writer);
1970        self.mark1_array.write_into(writer);
1971        self.mark2_array.write_into(writer);
1972    }
1973    fn table_type(&self) -> TableType {
1974        TableType::Named("MarkMarkPosFormat1")
1975    }
1976}
1977
1978impl Validate for MarkMarkPosFormat1 {
1979    fn validate_impl(&self, ctx: &mut ValidationCtx) {
1980        ctx.in_table("MarkMarkPosFormat1", |ctx| {
1981            ctx.in_field("mark1_coverage", |ctx| {
1982                self.mark1_coverage.validate_impl(ctx);
1983            });
1984            ctx.in_field("mark2_coverage", |ctx| {
1985                self.mark2_coverage.validate_impl(ctx);
1986            });
1987            ctx.in_field("mark1_array", |ctx| {
1988                self.mark1_array.validate_impl(ctx);
1989            });
1990            ctx.in_field("mark2_array", |ctx| {
1991                self.mark2_array.validate_impl(ctx);
1992            });
1993        })
1994    }
1995}
1996
1997impl<'a> FromObjRef<read_fonts::tables::gpos::MarkMarkPosFormat1<'a>> for MarkMarkPosFormat1 {
1998    fn from_obj_ref(obj: &read_fonts::tables::gpos::MarkMarkPosFormat1<'a>, _: FontData) -> Self {
1999        MarkMarkPosFormat1 {
2000            mark1_coverage: obj.mark1_coverage().to_owned_table(),
2001            mark2_coverage: obj.mark2_coverage().to_owned_table(),
2002            mark1_array: obj.mark1_array().to_owned_table(),
2003            mark2_array: obj.mark2_array().to_owned_table(),
2004        }
2005    }
2006}
2007
2008#[allow(clippy::needless_lifetimes)]
2009impl<'a> FromTableRef<read_fonts::tables::gpos::MarkMarkPosFormat1<'a>> for MarkMarkPosFormat1 {}
2010
2011impl<'a> FontRead<'a> for MarkMarkPosFormat1 {
2012    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
2013        <read_fonts::tables::gpos::MarkMarkPosFormat1 as FontRead>::read(data)
2014            .map(|x| x.to_owned_table())
2015    }
2016}
2017
2018/// Part of [MarkMarkPosFormat1]Class2Record
2019#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
2020#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2021pub struct Mark2Array {
2022    /// Array of Mark2Records, in Coverage order.
2023    pub mark2_records: Vec<Mark2Record>,
2024}
2025
2026impl Mark2Array {
2027    /// Construct a new `Mark2Array`
2028    pub fn new(mark2_records: Vec<Mark2Record>) -> Self {
2029        Self { mark2_records }
2030    }
2031}
2032
2033impl FontWrite for Mark2Array {
2034    #[allow(clippy::unnecessary_cast)]
2035    fn write_into(&self, writer: &mut TableWriter) {
2036        (u16::try_from(array_len(&self.mark2_records)).unwrap()).write_into(writer);
2037        self.mark2_records.write_into(writer);
2038    }
2039    fn table_type(&self) -> TableType {
2040        TableType::Named("Mark2Array")
2041    }
2042}
2043
2044impl Validate for Mark2Array {
2045    fn validate_impl(&self, ctx: &mut ValidationCtx) {
2046        ctx.in_table("Mark2Array", |ctx| {
2047            ctx.in_field("mark2_records", |ctx| {
2048                if self.mark2_records.len() > (u16::MAX as usize) {
2049                    ctx.report("array exceeds max length");
2050                }
2051                self.mark2_records.validate_impl(ctx);
2052            });
2053        })
2054    }
2055}
2056
2057impl<'a> FromObjRef<read_fonts::tables::gpos::Mark2Array<'a>> for Mark2Array {
2058    fn from_obj_ref(obj: &read_fonts::tables::gpos::Mark2Array<'a>, _: FontData) -> Self {
2059        let offset_data = obj.offset_data();
2060        Mark2Array {
2061            mark2_records: obj
2062                .mark2_records()
2063                .iter()
2064                .filter_map(|x| x.map(|x| FromObjRef::from_obj_ref(&x, offset_data)).ok())
2065                .collect(),
2066        }
2067    }
2068}
2069
2070#[allow(clippy::needless_lifetimes)]
2071impl<'a> FromTableRef<read_fonts::tables::gpos::Mark2Array<'a>> for Mark2Array {}
2072
2073/// Part of [MarkMarkPosFormat1]
2074#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
2075#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2076pub struct Mark2Record {
2077    /// Array of offsets (one per class) to Anchor tables. Offsets are
2078    /// from beginning of Mark2Array table, in class order (offsets may
2079    /// be NULL).
2080    pub mark2_anchors: Vec<NullableOffsetMarker<AnchorTable>>,
2081}
2082
2083impl Mark2Record {
2084    /// Construct a new `Mark2Record`
2085    pub fn new(mark2_anchors: Vec<Option<AnchorTable>>) -> Self {
2086        Self {
2087            mark2_anchors: mark2_anchors.into_iter().map(Into::into).collect(),
2088        }
2089    }
2090}
2091
2092impl FontWrite for Mark2Record {
2093    fn write_into(&self, writer: &mut TableWriter) {
2094        self.mark2_anchors.write_into(writer);
2095    }
2096    fn table_type(&self) -> TableType {
2097        TableType::Named("Mark2Record")
2098    }
2099}
2100
2101impl Validate for Mark2Record {
2102    fn validate_impl(&self, ctx: &mut ValidationCtx) {
2103        ctx.in_table("Mark2Record", |ctx| {
2104            ctx.in_field("mark2_anchors", |ctx| {
2105                if self.mark2_anchors.len() > (u16::MAX as usize) {
2106                    ctx.report("array exceeds max length");
2107                }
2108                self.mark2_anchors.validate_impl(ctx);
2109            });
2110        })
2111    }
2112}
2113
2114impl FromObjRef<read_fonts::tables::gpos::Mark2Record<'_>> for Mark2Record {
2115    fn from_obj_ref(obj: &read_fonts::tables::gpos::Mark2Record, offset_data: FontData) -> Self {
2116        Mark2Record {
2117            mark2_anchors: obj.mark2_anchors(offset_data).to_owned_table(),
2118        }
2119    }
2120}
2121
2122/// [Extension Positioning Subtable Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#extension-positioning-subtable-format-1)
2123#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
2124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2125pub struct ExtensionPosFormat1<T> {
2126    /// Lookup type of subtable referenced by extensionOffset (i.e. the
2127    /// extension subtable).
2128    pub extension_lookup_type: u16,
2129    /// Offset to the extension subtable, of lookup type
2130    /// extensionLookupType, relative to the start of the
2131    /// ExtensionPosFormat1 subtable.
2132    pub extension: OffsetMarker<T, WIDTH_32>,
2133}
2134
2135impl<T: Default> ExtensionPosFormat1<T> {
2136    /// Construct a new `ExtensionPosFormat1`
2137    pub fn new(extension_lookup_type: u16, extension: T) -> Self {
2138        Self {
2139            extension_lookup_type,
2140            extension: extension.into(),
2141        }
2142    }
2143}
2144
2145impl<T: Validate> Validate for ExtensionPosFormat1<T> {
2146    fn validate_impl(&self, ctx: &mut ValidationCtx) {
2147        ctx.in_table("ExtensionPosFormat1", |ctx| {
2148            ctx.in_field("extension", |ctx| {
2149                self.extension.validate_impl(ctx);
2150            });
2151        })
2152    }
2153}
2154
2155impl<'a, T, U> FromObjRef<read_fonts::tables::gpos::ExtensionPosFormat1<'a, U>>
2156    for ExtensionPosFormat1<T>
2157where
2158    U: FontRead<'a>,
2159    T: FromTableRef<U> + Default + 'static,
2160{
2161    fn from_obj_ref(
2162        obj: &read_fonts::tables::gpos::ExtensionPosFormat1<'a, U>,
2163        _: FontData,
2164    ) -> Self {
2165        ExtensionPosFormat1 {
2166            extension_lookup_type: obj.extension_lookup_type(),
2167            extension: obj.extension().to_owned_table(),
2168        }
2169    }
2170}
2171
2172#[allow(clippy::needless_lifetimes)]
2173impl<'a, T, U> FromTableRef<read_fonts::tables::gpos::ExtensionPosFormat1<'a, U>>
2174    for ExtensionPosFormat1<T>
2175where
2176    U: FontRead<'a>,
2177    T: FromTableRef<U> + Default + 'static,
2178{
2179}
2180
2181/// A [GPOS Extension Positioning](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#lookuptype-9-extension-positioning) subtable
2182#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2184pub enum ExtensionSubtable {
2185    Single(ExtensionPosFormat1<SinglePos>),
2186    Pair(ExtensionPosFormat1<PairPos>),
2187    Cursive(ExtensionPosFormat1<CursivePosFormat1>),
2188    MarkToBase(ExtensionPosFormat1<MarkBasePosFormat1>),
2189    MarkToLig(ExtensionPosFormat1<MarkLigPosFormat1>),
2190    MarkToMark(ExtensionPosFormat1<MarkMarkPosFormat1>),
2191    Contextual(ExtensionPosFormat1<PositionSequenceContext>),
2192    ChainContextual(ExtensionPosFormat1<PositionChainContext>),
2193}
2194
2195impl Default for ExtensionSubtable {
2196    fn default() -> Self {
2197        Self::Single(Default::default())
2198    }
2199}
2200
2201impl FontWrite for ExtensionSubtable {
2202    fn write_into(&self, writer: &mut TableWriter) {
2203        match self {
2204            Self::Single(table) => table.write_into(writer),
2205            Self::Pair(table) => table.write_into(writer),
2206            Self::Cursive(table) => table.write_into(writer),
2207            Self::MarkToBase(table) => table.write_into(writer),
2208            Self::MarkToLig(table) => table.write_into(writer),
2209            Self::MarkToMark(table) => table.write_into(writer),
2210            Self::Contextual(table) => table.write_into(writer),
2211            Self::ChainContextual(table) => table.write_into(writer),
2212        }
2213    }
2214    fn table_type(&self) -> TableType {
2215        match self {
2216            Self::Single(table) => table.table_type(),
2217            Self::Pair(table) => table.table_type(),
2218            Self::Cursive(table) => table.table_type(),
2219            Self::MarkToBase(table) => table.table_type(),
2220            Self::MarkToLig(table) => table.table_type(),
2221            Self::MarkToMark(table) => table.table_type(),
2222            Self::Contextual(table) => table.table_type(),
2223            Self::ChainContextual(table) => table.table_type(),
2224        }
2225    }
2226}
2227
2228impl Validate for ExtensionSubtable {
2229    fn validate_impl(&self, ctx: &mut ValidationCtx) {
2230        match self {
2231            Self::Single(table) => table.validate_impl(ctx),
2232            Self::Pair(table) => table.validate_impl(ctx),
2233            Self::Cursive(table) => table.validate_impl(ctx),
2234            Self::MarkToBase(table) => table.validate_impl(ctx),
2235            Self::MarkToLig(table) => table.validate_impl(ctx),
2236            Self::MarkToMark(table) => table.validate_impl(ctx),
2237            Self::Contextual(table) => table.validate_impl(ctx),
2238            Self::ChainContextual(table) => table.validate_impl(ctx),
2239        }
2240    }
2241}
2242
2243impl FromObjRef<read_fonts::tables::gpos::ExtensionSubtable<'_>> for ExtensionSubtable {
2244    fn from_obj_ref(
2245        from: &read_fonts::tables::gpos::ExtensionSubtable<'_>,
2246        data: FontData,
2247    ) -> Self {
2248        match from {
2249            read_fonts::tables::gpos::ExtensionSubtable::Single(table) => {
2250                Self::Single(table.to_owned_obj(data))
2251            }
2252            read_fonts::tables::gpos::ExtensionSubtable::Pair(table) => {
2253                Self::Pair(table.to_owned_obj(data))
2254            }
2255            read_fonts::tables::gpos::ExtensionSubtable::Cursive(table) => {
2256                Self::Cursive(table.to_owned_obj(data))
2257            }
2258            read_fonts::tables::gpos::ExtensionSubtable::MarkToBase(table) => {
2259                Self::MarkToBase(table.to_owned_obj(data))
2260            }
2261            read_fonts::tables::gpos::ExtensionSubtable::MarkToLig(table) => {
2262                Self::MarkToLig(table.to_owned_obj(data))
2263            }
2264            read_fonts::tables::gpos::ExtensionSubtable::MarkToMark(table) => {
2265                Self::MarkToMark(table.to_owned_obj(data))
2266            }
2267            read_fonts::tables::gpos::ExtensionSubtable::Contextual(table) => {
2268                Self::Contextual(table.to_owned_obj(data))
2269            }
2270            read_fonts::tables::gpos::ExtensionSubtable::ChainContextual(table) => {
2271                Self::ChainContextual(table.to_owned_obj(data))
2272            }
2273        }
2274    }
2275}
2276
2277impl FromTableRef<read_fonts::tables::gpos::ExtensionSubtable<'_>> for ExtensionSubtable {}
2278
2279impl From<ExtensionPosFormat1<SinglePos>> for ExtensionSubtable {
2280    fn from(src: ExtensionPosFormat1<SinglePos>) -> ExtensionSubtable {
2281        ExtensionSubtable::Single(src)
2282    }
2283}
2284
2285impl From<ExtensionPosFormat1<PairPos>> for ExtensionSubtable {
2286    fn from(src: ExtensionPosFormat1<PairPos>) -> ExtensionSubtable {
2287        ExtensionSubtable::Pair(src)
2288    }
2289}
2290
2291impl From<ExtensionPosFormat1<CursivePosFormat1>> for ExtensionSubtable {
2292    fn from(src: ExtensionPosFormat1<CursivePosFormat1>) -> ExtensionSubtable {
2293        ExtensionSubtable::Cursive(src)
2294    }
2295}
2296
2297impl From<ExtensionPosFormat1<MarkBasePosFormat1>> for ExtensionSubtable {
2298    fn from(src: ExtensionPosFormat1<MarkBasePosFormat1>) -> ExtensionSubtable {
2299        ExtensionSubtable::MarkToBase(src)
2300    }
2301}
2302
2303impl From<ExtensionPosFormat1<MarkLigPosFormat1>> for ExtensionSubtable {
2304    fn from(src: ExtensionPosFormat1<MarkLigPosFormat1>) -> ExtensionSubtable {
2305        ExtensionSubtable::MarkToLig(src)
2306    }
2307}
2308
2309impl From<ExtensionPosFormat1<MarkMarkPosFormat1>> for ExtensionSubtable {
2310    fn from(src: ExtensionPosFormat1<MarkMarkPosFormat1>) -> ExtensionSubtable {
2311        ExtensionSubtable::MarkToMark(src)
2312    }
2313}
2314
2315impl From<ExtensionPosFormat1<PositionSequenceContext>> for ExtensionSubtable {
2316    fn from(src: ExtensionPosFormat1<PositionSequenceContext>) -> ExtensionSubtable {
2317        ExtensionSubtable::Contextual(src)
2318    }
2319}
2320
2321impl From<ExtensionPosFormat1<PositionChainContext>> for ExtensionSubtable {
2322    fn from(src: ExtensionPosFormat1<PositionChainContext>) -> ExtensionSubtable {
2323        ExtensionSubtable::ChainContextual(src)
2324    }
2325}