ttf_parser/tables/
gpos.rs

1//! A [Glyph Positioning Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos)
2//! implementation.
3
4// A heavily modified port of https://github.com/harfbuzz/rustybuzz implementation
5// originally written by https://github.com/laurmaedje
6
7use core::convert::TryFrom;
8
9use crate::opentype_layout::ChainedContextLookup;
10use crate::opentype_layout::{Class, ClassDefinition, ContextLookup, Coverage, LookupSubtable};
11use crate::parser::{
12    FromData, FromSlice, LazyArray16, LazyArray32, NumFrom, Offset, Offset16, Stream,
13};
14use crate::GlyphId;
15
16/// A [Device Table](
17/// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#devVarIdxTbls)
18/// hinting values.
19#[derive(Clone, Copy)]
20pub struct HintingDevice<'a> {
21    start_size: u16,
22    end_size: u16,
23    delta_format: u16,
24    delta_values: LazyArray16<'a, u16>,
25}
26
27impl HintingDevice<'_> {
28    /// Returns X-axis delta.
29    pub fn x_delta(&self, units_per_em: u16, pixels_per_em: Option<(u16, u16)>) -> Option<i32> {
30        let ppem = pixels_per_em.map(|(x, _)| x)?;
31        self.get_delta(ppem, units_per_em)
32    }
33
34    /// Returns Y-axis delta.
35    pub fn y_delta(&self, units_per_em: u16, pixels_per_em: Option<(u16, u16)>) -> Option<i32> {
36        let ppem = pixels_per_em.map(|(_, y)| y)?;
37        self.get_delta(ppem, units_per_em)
38    }
39
40    fn get_delta(&self, ppem: u16, scale: u16) -> Option<i32> {
41        let f = self.delta_format;
42        debug_assert!(matches!(f, 1..=3));
43
44        if ppem == 0 || ppem < self.start_size || ppem > self.end_size {
45            return None;
46        }
47
48        let s = ppem - self.start_size;
49        let byte = self.delta_values.get(s >> (4 - f))?;
50        let bits = byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f));
51        let mask = 0xFFFF >> (16 - (1 << f));
52
53        let mut delta = i64::from(bits & mask);
54        if delta >= i64::from((mask + 1) >> 1) {
55            delta -= i64::from(mask + 1);
56        }
57
58        i32::try_from(delta * i64::from(scale) / i64::from(ppem)).ok()
59    }
60}
61
62impl core::fmt::Debug for HintingDevice<'_> {
63    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
64        write!(f, "HintingDevice {{ ... }}")
65    }
66}
67
68/// A [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#devVarIdxTbls)
69/// indexes into [Item Variation Store](
70/// https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#IVS).
71#[allow(missing_docs)]
72#[derive(Clone, Copy, Debug)]
73pub struct VariationDevice {
74    pub outer_index: u16,
75    pub inner_index: u16,
76}
77
78/// A [Device Table](
79/// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#devVarIdxTbls).
80#[allow(missing_docs)]
81#[derive(Clone, Copy, Debug)]
82pub enum Device<'a> {
83    Hinting(HintingDevice<'a>),
84    Variation(VariationDevice),
85}
86
87impl<'a> Device<'a> {
88    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
89        let mut s = Stream::new(data);
90        let first = s.read::<u16>()?;
91        let second = s.read::<u16>()?;
92        let format = s.read::<u16>()?;
93        match format {
94            1..=3 => {
95                let start_size = first;
96                let end_size = second;
97                let count = (1 + (end_size - start_size)) >> (4 - format);
98                let delta_values = s.read_array16(count)?;
99                Some(Self::Hinting(HintingDevice {
100                    start_size,
101                    end_size,
102                    delta_format: format,
103                    delta_values,
104                }))
105            }
106            0x8000 => Some(Self::Variation(VariationDevice {
107                outer_index: first,
108                inner_index: second,
109            })),
110            _ => None,
111        }
112    }
113}
114
115#[derive(Clone, Copy, Default, Debug)]
116struct ValueFormatFlags(u8);
117
118#[rustfmt::skip]
119impl ValueFormatFlags {
120    #[inline] fn x_placement(self) -> bool { self.0 & 0x01 != 0 }
121    #[inline] fn y_placement(self) -> bool { self.0 & 0x02 != 0 }
122    #[inline] fn x_advance(self) -> bool { self.0 & 0x04 != 0 }
123    #[inline] fn y_advance(self) -> bool { self.0 & 0x08 != 0 }
124    #[inline] fn x_placement_device(self) -> bool { self.0 & 0x10 != 0 }
125    #[inline] fn y_placement_device(self) -> bool { self.0 & 0x20 != 0 }
126    #[inline] fn x_advance_device(self) -> bool { self.0 & 0x40 != 0 }
127    #[inline] fn y_advance_device(self) -> bool { self.0 & 0x80 != 0 }
128
129    // The ValueRecord struct constrain either i16 values or Offset16 offsets
130    // and the total size depend on how many flags are enabled.
131    fn size(self) -> usize {
132        // The high 8 bits are not used, so make sure we ignore them using 0xFF.
133        u16::SIZE * usize::num_from(self.0.count_ones())
134    }
135}
136
137impl FromData for ValueFormatFlags {
138    const SIZE: usize = 2;
139
140    #[inline]
141    fn parse(data: &[u8]) -> Option<Self> {
142        // There is no data in high 8 bits, so skip it.
143        Some(Self(data[1]))
144    }
145}
146
147/// A [Value Record](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#value-record).
148#[derive(Clone, Copy, Default, Debug)]
149pub struct ValueRecord<'a> {
150    /// Horizontal adjustment for placement, in design units.
151    pub x_placement: i16,
152    /// Vertical adjustment for placement, in design units.
153    pub y_placement: i16,
154    /// Horizontal adjustment for advance, in design units — only used for horizontal layout.
155    pub x_advance: i16,
156    /// Vertical adjustment for advance, in design units — only used for vertical layout.
157    pub y_advance: i16,
158
159    /// A [`Device`] table with horizontal adjustment for placement.
160    pub x_placement_device: Option<Device<'a>>,
161    /// A [`Device`] table with vertical adjustment for placement.
162    pub y_placement_device: Option<Device<'a>>,
163    /// A [`Device`] table with horizontal adjustment for advance.
164    pub x_advance_device: Option<Device<'a>>,
165    /// A [`Device`] table with vertical adjustment for advance.
166    pub y_advance_device: Option<Device<'a>>,
167}
168
169impl<'a> ValueRecord<'a> {
170    // Returns `None` only on parsing error.
171    fn parse(
172        table_data: &'a [u8],
173        s: &mut Stream,
174        flags: ValueFormatFlags,
175    ) -> Option<ValueRecord<'a>> {
176        let mut record = ValueRecord::default();
177
178        if flags.x_placement() {
179            record.x_placement = s.read::<i16>()?;
180        }
181
182        if flags.y_placement() {
183            record.y_placement = s.read::<i16>()?;
184        }
185
186        if flags.x_advance() {
187            record.x_advance = s.read::<i16>()?;
188        }
189
190        if flags.y_advance() {
191            record.y_advance = s.read::<i16>()?;
192        }
193
194        if flags.x_placement_device() {
195            if let Some(offset) = s.read::<Option<Offset16>>()? {
196                record.x_placement_device =
197                    table_data.get(offset.to_usize()..).and_then(Device::parse)
198            }
199        }
200
201        if flags.y_placement_device() {
202            if let Some(offset) = s.read::<Option<Offset16>>()? {
203                record.y_placement_device =
204                    table_data.get(offset.to_usize()..).and_then(Device::parse)
205            }
206        }
207
208        if flags.x_advance_device() {
209            if let Some(offset) = s.read::<Option<Offset16>>()? {
210                record.x_advance_device =
211                    table_data.get(offset.to_usize()..).and_then(Device::parse)
212            }
213        }
214
215        if flags.y_advance_device() {
216            if let Some(offset) = s.read::<Option<Offset16>>()? {
217                record.y_advance_device =
218                    table_data.get(offset.to_usize()..).and_then(Device::parse)
219            }
220        }
221
222        Some(record)
223    }
224}
225
226/// An array of
227/// [Value Records](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#value-record).
228#[derive(Clone, Copy)]
229pub struct ValueRecordsArray<'a> {
230    // We have to store the original table data because ValueRecords can have
231    // a offset to Device tables and offset is from the beginning of the table.
232    table_data: &'a [u8],
233    // A slice that contains all ValueRecords.
234    data: &'a [u8],
235    // Number of records.
236    len: u16,
237    // Size of the single record.
238    value_len: usize,
239    // Flags, used during ValueRecord parsing.
240    flags: ValueFormatFlags,
241}
242
243impl<'a> ValueRecordsArray<'a> {
244    fn parse(
245        table_data: &'a [u8],
246        count: u16,
247        flags: ValueFormatFlags,
248        s: &mut Stream<'a>,
249    ) -> Option<Self> {
250        Some(Self {
251            table_data,
252            flags,
253            len: count,
254            value_len: flags.size(),
255            data: s.read_bytes(usize::from(count) * flags.size())?,
256        })
257    }
258
259    /// Returns array's length.
260    #[inline]
261    pub fn len(&self) -> u16 {
262        self.len
263    }
264
265    /// Checks if the array is empty.
266    pub fn is_empty(&self) -> bool {
267        self.len == 0
268    }
269
270    /// Returns a [`ValueRecord`] at index.
271    pub fn get(&self, index: u16) -> Option<ValueRecord<'a>> {
272        let start = usize::from(index) * self.value_len;
273        let end = start + self.value_len;
274        let data = self.data.get(start..end)?;
275        let mut s = Stream::new(data);
276        ValueRecord::parse(self.table_data, &mut s, self.flags)
277    }
278}
279
280impl core::fmt::Debug for ValueRecordsArray<'_> {
281    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
282        write!(f, "ValueRecordsArray {{ ... }}")
283    }
284}
285
286/// A [Single Adjustment Positioning Subtable](
287/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#SP).
288#[allow(missing_docs)]
289#[derive(Clone, Copy, Debug)]
290pub enum SingleAdjustment<'a> {
291    Format1 {
292        coverage: Coverage<'a>,
293        value: ValueRecord<'a>,
294    },
295    Format2 {
296        coverage: Coverage<'a>,
297        values: ValueRecordsArray<'a>,
298    },
299}
300
301impl<'a> SingleAdjustment<'a> {
302    fn parse(data: &'a [u8]) -> Option<Self> {
303        let mut s = Stream::new(data);
304        match s.read::<u16>()? {
305            1 => {
306                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
307                let flags = s.read::<ValueFormatFlags>()?;
308                let value = ValueRecord::parse(data, &mut s, flags)?;
309                Some(Self::Format1 { coverage, value })
310            }
311            2 => {
312                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
313                let flags = s.read::<ValueFormatFlags>()?;
314                let count = s.read::<u16>()?;
315                let values = ValueRecordsArray::parse(data, count, flags, &mut s)?;
316                Some(Self::Format2 { coverage, values })
317            }
318            _ => None,
319        }
320    }
321
322    /// Returns the subtable coverage.
323    #[inline]
324    pub fn coverage(&self) -> Coverage<'a> {
325        match self {
326            Self::Format1 { coverage, .. } => *coverage,
327            Self::Format2 { coverage, .. } => *coverage,
328        }
329    }
330}
331
332/// A [`ValueRecord`] pairs set used by [`PairAdjustment`].
333#[derive(Clone, Copy)]
334pub struct PairSet<'a> {
335    data: &'a [u8],
336    flags: (ValueFormatFlags, ValueFormatFlags),
337    record_len: u8,
338}
339
340impl<'a> PairSet<'a> {
341    fn parse(data: &'a [u8], flags: (ValueFormatFlags, ValueFormatFlags)) -> Option<Self> {
342        let mut s = Stream::new(data);
343        let count = s.read::<u16>()?;
344        // Max len is 34, so u8 is just enough.
345        let record_len = (GlyphId::SIZE + flags.0.size() + flags.1.size()) as u8;
346        let data = s.read_bytes(usize::from(count) * usize::from(record_len))?;
347        Some(Self {
348            data,
349            flags,
350            record_len,
351        })
352    }
353
354    #[inline]
355    fn binary_search(&self, second: GlyphId) -> Option<&'a [u8]> {
356        // Based on Rust std implementation.
357
358        let mut size = self.data.len() / usize::from(self.record_len);
359        if size == 0 {
360            return None;
361        }
362
363        let get_record = |index| {
364            let start = index * usize::from(self.record_len);
365            let end = start + usize::from(self.record_len);
366            self.data.get(start..end)
367        };
368
369        let get_glyph = |data: &[u8]| GlyphId(u16::from_be_bytes([data[0], data[1]]));
370
371        let mut base = 0;
372        while size > 1 {
373            let half = size / 2;
374            let mid = base + half;
375            // mid is always in [0, size), that means mid is >= 0 and < size.
376            // mid >= 0: by definition
377            // mid < size: mid = size / 2 + size / 4 + size / 8 ...
378            let cmp = get_glyph(get_record(mid)?).cmp(&second);
379            base = if cmp == core::cmp::Ordering::Greater {
380                base
381            } else {
382                mid
383            };
384            size -= half;
385        }
386
387        // base is always in [0, size) because base <= mid.
388        let value = get_record(base)?;
389        if get_glyph(value).cmp(&second) == core::cmp::Ordering::Equal {
390            Some(value)
391        } else {
392            None
393        }
394    }
395
396    /// Returns a [`ValueRecord`] pair using the second glyph.
397    pub fn get(&self, second: GlyphId) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> {
398        let record_data = self.binary_search(second)?;
399        let mut s = Stream::new(record_data);
400        s.skip::<GlyphId>();
401        Some((
402            ValueRecord::parse(self.data, &mut s, self.flags.0)?,
403            ValueRecord::parse(self.data, &mut s, self.flags.1)?,
404        ))
405    }
406}
407
408impl core::fmt::Debug for PairSet<'_> {
409    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
410        write!(f, "PairSet {{ ... }}")
411    }
412}
413
414// Essentially a `LazyOffsetArray16` but stores additional data required to parse [`PairSet`].
415
416/// A list of [`PairSet`]s.
417#[derive(Clone, Copy)]
418pub struct PairSets<'a> {
419    data: &'a [u8],
420    // Zero offsets must be ignored, therefore we're using `Option<Offset16>`.
421    offsets: LazyArray16<'a, Option<Offset16>>,
422    flags: (ValueFormatFlags, ValueFormatFlags),
423}
424
425impl<'a> PairSets<'a> {
426    fn new(
427        data: &'a [u8],
428        offsets: LazyArray16<'a, Option<Offset16>>,
429        flags: (ValueFormatFlags, ValueFormatFlags),
430    ) -> Self {
431        Self {
432            data,
433            offsets,
434            flags,
435        }
436    }
437
438    /// Returns a value at `index`.
439    #[inline]
440    pub fn get(&self, index: u16) -> Option<PairSet<'a>> {
441        let offset = self.offsets.get(index)??.to_usize();
442        self.data
443            .get(offset..)
444            .and_then(|data| PairSet::parse(data, self.flags))
445    }
446
447    /// Returns array's length.
448    #[inline]
449    pub fn len(&self) -> u16 {
450        self.offsets.len()
451    }
452
453    /// Checks if the array is empty.
454    pub fn is_empty(&self) -> bool {
455        self.offsets.is_empty()
456    }
457}
458
459impl core::fmt::Debug for PairSets<'_> {
460    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
461        write!(f, "PairSets {{ ... }}")
462    }
463}
464
465/// A [`ValueRecord`] pairs matrix used by [`PairAdjustment`].
466#[derive(Clone, Copy)]
467pub struct ClassMatrix<'a> {
468    // We have to store table's original slice,
469    // because offsets in ValueRecords are from the begging of the table.
470    table_data: &'a [u8],
471    matrix: &'a [u8],
472    counts: (u16, u16),
473    flags: (ValueFormatFlags, ValueFormatFlags),
474    record_len: u8,
475}
476
477impl<'a> ClassMatrix<'a> {
478    fn parse(
479        table_data: &'a [u8],
480        counts: (u16, u16),
481        flags: (ValueFormatFlags, ValueFormatFlags),
482        s: &mut Stream<'a>,
483    ) -> Option<Self> {
484        let count = usize::num_from(u32::from(counts.0) * u32::from(counts.1));
485        // Max len is 32, so u8 is just enough.
486        let record_len = (flags.0.size() + flags.1.size()) as u8;
487        let matrix = s.read_bytes(count * usize::from(record_len))?;
488        Some(Self {
489            table_data,
490            matrix,
491            counts,
492            flags,
493            record_len,
494        })
495    }
496
497    /// Returns a [`ValueRecord`] pair using specified classes.
498    pub fn get(&self, classes: (u16, u16)) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> {
499        if classes.0 >= self.counts.0 || classes.1 >= self.counts.1 {
500            return None;
501        }
502
503        let idx = usize::from(classes.0) * usize::from(self.counts.1) + usize::from(classes.1);
504        let record = self.matrix.get(idx * usize::from(self.record_len)..)?;
505
506        let mut s = Stream::new(record);
507        Some((
508            ValueRecord::parse(self.table_data, &mut s, self.flags.0)?,
509            ValueRecord::parse(self.table_data, &mut s, self.flags.1)?,
510        ))
511    }
512}
513
514impl core::fmt::Debug for ClassMatrix<'_> {
515    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
516        write!(f, "ClassMatrix {{ ... }}")
517    }
518}
519
520/// A [Pair Adjustment Positioning Subtable](
521/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#PP).
522#[allow(missing_docs)]
523#[derive(Clone, Copy, Debug)]
524pub enum PairAdjustment<'a> {
525    Format1 {
526        coverage: Coverage<'a>,
527        sets: PairSets<'a>,
528    },
529    Format2 {
530        coverage: Coverage<'a>,
531        classes: (ClassDefinition<'a>, ClassDefinition<'a>),
532        matrix: ClassMatrix<'a>,
533    },
534}
535
536impl<'a> PairAdjustment<'a> {
537    fn parse(data: &'a [u8]) -> Option<Self> {
538        let mut s = Stream::new(data);
539        match s.read::<u16>()? {
540            1 => {
541                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
542                let flags = (s.read::<ValueFormatFlags>()?, s.read::<ValueFormatFlags>()?);
543                let count = s.read::<u16>()?;
544                let offsets = s.read_array16(count)?;
545                Some(Self::Format1 {
546                    coverage,
547                    sets: PairSets::new(data, offsets, flags),
548                })
549            }
550            2 => {
551                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
552                let flags = (s.read::<ValueFormatFlags>()?, s.read::<ValueFormatFlags>()?);
553                let classes = (
554                    ClassDefinition::parse(s.read_at_offset16(data)?)?,
555                    ClassDefinition::parse(s.read_at_offset16(data)?)?,
556                );
557                let counts = (s.read::<u16>()?, s.read::<u16>()?);
558                Some(Self::Format2 {
559                    coverage,
560                    classes,
561                    matrix: ClassMatrix::parse(data, counts, flags, &mut s)?,
562                })
563            }
564            _ => None,
565        }
566    }
567
568    /// Returns the subtable coverage.
569    #[inline]
570    pub fn coverage(&self) -> Coverage<'a> {
571        match self {
572            Self::Format1 { coverage, .. } => *coverage,
573            Self::Format2 { coverage, .. } => *coverage,
574        }
575    }
576}
577
578#[derive(Clone, Copy)]
579struct EntryExitRecord {
580    entry_anchor_offset: Option<Offset16>,
581    exit_anchor_offset: Option<Offset16>,
582}
583
584impl FromData for EntryExitRecord {
585    const SIZE: usize = 4;
586
587    #[inline]
588    fn parse(data: &[u8]) -> Option<Self> {
589        let mut s = Stream::new(data);
590        Some(Self {
591            entry_anchor_offset: s.read::<Option<Offset16>>()?,
592            exit_anchor_offset: s.read::<Option<Offset16>>()?,
593        })
594    }
595}
596
597/// A list of entry and exit [`Anchor`] pairs.
598#[derive(Clone, Copy)]
599pub struct CursiveAnchorSet<'a> {
600    data: &'a [u8],
601    records: LazyArray16<'a, EntryExitRecord>,
602}
603
604impl<'a> CursiveAnchorSet<'a> {
605    /// Returns an entry [`Anchor`] at index.
606    pub fn entry(&self, index: u16) -> Option<Anchor<'a>> {
607        let offset = self.records.get(index)?.entry_anchor_offset?.to_usize();
608        self.data.get(offset..).and_then(Anchor::parse)
609    }
610
611    /// Returns an exit [`Anchor`] at index.
612    pub fn exit(&self, index: u16) -> Option<Anchor<'a>> {
613        let offset = self.records.get(index)?.exit_anchor_offset?.to_usize();
614        self.data.get(offset..).and_then(Anchor::parse)
615    }
616
617    /// Returns the number of items.
618    pub fn len(&self) -> u16 {
619        self.records.len()
620    }
621
622    /// Checks if the set is empty.
623    pub fn is_empty(&self) -> bool {
624        self.records.is_empty()
625    }
626}
627
628impl core::fmt::Debug for CursiveAnchorSet<'_> {
629    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
630        write!(f, "CursiveAnchorSet {{ ... }}")
631    }
632}
633
634/// A [Cursive Attachment Positioning Subtable](
635/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#CAP).
636#[allow(missing_docs)]
637#[derive(Clone, Copy, Debug)]
638pub struct CursiveAdjustment<'a> {
639    pub coverage: Coverage<'a>,
640    pub sets: CursiveAnchorSet<'a>,
641}
642
643impl<'a> CursiveAdjustment<'a> {
644    fn parse(data: &'a [u8]) -> Option<Self> {
645        let mut s = Stream::new(data);
646        match s.read::<u16>()? {
647            1 => {
648                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
649                let count = s.read::<u16>()?;
650                let records = s.read_array16(count)?;
651                Some(Self {
652                    coverage,
653                    sets: CursiveAnchorSet { data, records },
654                })
655            }
656            _ => None,
657        }
658    }
659}
660
661/// A [Mark-to-Base Attachment Positioning Subtable](
662/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MBP).
663#[derive(Clone, Copy, Debug)]
664pub struct MarkToBaseAdjustment<'a> {
665    /// A mark coverage.
666    pub mark_coverage: Coverage<'a>,
667    /// A base coverage.
668    pub base_coverage: Coverage<'a>,
669    /// A list of mark anchors.
670    pub marks: MarkArray<'a>,
671    /// An anchors matrix.
672    pub anchors: AnchorMatrix<'a>,
673}
674
675impl<'a> MarkToBaseAdjustment<'a> {
676    fn parse(data: &'a [u8]) -> Option<Self> {
677        let mut s = Stream::new(data);
678        match s.read::<u16>()? {
679            1 => {
680                let mark_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
681                let base_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
682                let class_count = s.read::<u16>()?;
683                let marks = MarkArray::parse(s.read_at_offset16(data)?)?;
684                let anchors = AnchorMatrix::parse(s.read_at_offset16(data)?, class_count)?;
685                Some(Self {
686                    mark_coverage,
687                    base_coverage,
688                    marks,
689                    anchors,
690                })
691            }
692            _ => None,
693        }
694    }
695}
696
697/// A [Mark-to-Ligature Attachment Positioning Subtable](
698/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MLP).
699#[allow(missing_docs)]
700#[derive(Clone, Copy, Debug)]
701pub struct MarkToLigatureAdjustment<'a> {
702    pub mark_coverage: Coverage<'a>,
703    pub ligature_coverage: Coverage<'a>,
704    pub marks: MarkArray<'a>,
705    pub ligature_array: LigatureArray<'a>,
706}
707
708impl<'a> MarkToLigatureAdjustment<'a> {
709    fn parse(data: &'a [u8]) -> Option<Self> {
710        let mut s = Stream::new(data);
711        match s.read::<u16>()? {
712            1 => {
713                let mark_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
714                let ligature_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
715                let class_count = s.read::<u16>()?;
716                let marks = MarkArray::parse(s.read_at_offset16(data)?)?;
717                let ligature_array = LigatureArray::parse(s.read_at_offset16(data)?, class_count)?;
718                Some(Self {
719                    mark_coverage,
720                    ligature_coverage,
721                    marks,
722                    ligature_array,
723                })
724            }
725            _ => None,
726        }
727    }
728}
729
730/// An array or ligature anchor matrices.
731#[derive(Clone, Copy)]
732pub struct LigatureArray<'a> {
733    data: &'a [u8],
734    class_count: u16,
735    offsets: LazyArray16<'a, Offset16>,
736}
737
738impl<'a> LigatureArray<'a> {
739    fn parse(data: &'a [u8], class_count: u16) -> Option<Self> {
740        let mut s = Stream::new(data);
741        let count = s.read::<u16>()?;
742        let offsets = s.read_array16(count)?;
743        Some(Self {
744            data,
745            class_count,
746            offsets,
747        })
748    }
749
750    /// Returns an [`AnchorMatrix`] at index.
751    pub fn get(&self, index: u16) -> Option<AnchorMatrix<'a>> {
752        let offset = self.offsets.get(index)?.to_usize();
753        let data = self.data.get(offset..)?;
754        AnchorMatrix::parse(data, self.class_count)
755    }
756
757    /// Returns the array length.
758    pub fn len(&self) -> u16 {
759        self.offsets.len()
760    }
761
762    /// Checks if the array is empty.
763    pub fn is_empty(&self) -> bool {
764        self.offsets.is_empty()
765    }
766}
767
768impl core::fmt::Debug for LigatureArray<'_> {
769    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
770        write!(f, "LigatureArray {{ ... }}")
771    }
772}
773
774#[derive(Clone, Copy)]
775struct MarkRecord {
776    class: Class,
777    mark_anchor: Offset16,
778}
779
780impl FromData for MarkRecord {
781    const SIZE: usize = 4;
782
783    #[inline]
784    fn parse(data: &[u8]) -> Option<Self> {
785        let mut s = Stream::new(data);
786        Some(Self {
787            class: s.read::<Class>()?,
788            mark_anchor: s.read::<Offset16>()?,
789        })
790    }
791}
792
793/// A [Mark Array](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table).
794#[derive(Clone, Copy)]
795pub struct MarkArray<'a> {
796    data: &'a [u8],
797    array: LazyArray16<'a, MarkRecord>,
798}
799
800impl<'a> MarkArray<'a> {
801    fn parse(data: &'a [u8]) -> Option<Self> {
802        let mut s = Stream::new(data);
803        let count = s.read::<u16>()?;
804        let array = s.read_array16(count)?;
805        Some(Self { data, array })
806    }
807
808    /// Returns contained data at index.
809    pub fn get(&self, index: u16) -> Option<(Class, Anchor<'a>)> {
810        let record = self.array.get(index)?;
811        let anchor = self
812            .data
813            .get(record.mark_anchor.to_usize()..)
814            .and_then(Anchor::parse)?;
815        Some((record.class, anchor))
816    }
817
818    /// Returns the array length.
819    pub fn len(&self) -> u16 {
820        self.array.len()
821    }
822
823    /// Checks if the array is empty.
824    pub fn is_empty(&self) -> bool {
825        self.array.is_empty()
826    }
827}
828
829impl core::fmt::Debug for MarkArray<'_> {
830    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
831        write!(f, "MarkArray {{ ... }}")
832    }
833}
834
835/// An [Anchor Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables).
836///
837/// The *Anchor Table Format 2: Design Units Plus Contour Point* is not supported.
838#[derive(Clone, Copy, Debug)]
839pub struct Anchor<'a> {
840    /// Horizontal value, in design units.
841    pub x: i16,
842    /// Vertical value, in design units.
843    pub y: i16,
844    /// A [`Device`] table with horizontal value.
845    pub x_device: Option<Device<'a>>,
846    /// A [`Device`] table with vertical value.
847    pub y_device: Option<Device<'a>>,
848}
849
850impl<'a> Anchor<'a> {
851    fn parse(data: &'a [u8]) -> Option<Self> {
852        let mut s = Stream::new(data);
853        let format = s.read::<u16>()?;
854        if !matches!(format, 1..=3) {
855            return None;
856        }
857
858        let mut table = Anchor {
859            x: s.read::<i16>()?,
860            y: s.read::<i16>()?,
861            x_device: None,
862            y_device: None,
863        };
864
865        // Note: Format 2 is not handled since there is currently no way to
866        // get a glyph contour point by index.
867
868        if format == 3 {
869            table.x_device = s
870                .read::<Option<Offset16>>()?
871                .and_then(|offset| data.get(offset.to_usize()..))
872                .and_then(Device::parse);
873
874            table.y_device = s
875                .read::<Option<Offset16>>()?
876                .and_then(|offset| data.get(offset.to_usize()..))
877                .and_then(Device::parse);
878        }
879
880        Some(table)
881    }
882}
883
884/// An [`Anchor`] parsing helper.
885#[derive(Clone, Copy)]
886pub struct AnchorMatrix<'a> {
887    data: &'a [u8],
888    /// Number of rows in the matrix.
889    pub rows: u16,
890    /// Number of columns in the matrix.
891    pub cols: u16,
892    matrix: LazyArray32<'a, Option<Offset16>>,
893}
894
895impl<'a> AnchorMatrix<'a> {
896    fn parse(data: &'a [u8], cols: u16) -> Option<Self> {
897        let mut s = Stream::new(data);
898        let rows = s.read::<u16>()?;
899        let count = u32::from(rows) * u32::from(cols);
900        let matrix = s.read_array32(count)?;
901        Some(Self {
902            data,
903            rows,
904            cols,
905            matrix,
906        })
907    }
908
909    /// Returns an [`Anchor`] at position.
910    pub fn get(&self, row: u16, col: u16) -> Option<Anchor> {
911        let idx = u32::from(row) * u32::from(self.cols) + u32::from(col);
912        let offset = self.matrix.get(idx)??.to_usize();
913        Anchor::parse(self.data.get(offset..)?)
914    }
915}
916
917impl core::fmt::Debug for AnchorMatrix<'_> {
918    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
919        write!(f, "AnchorMatrix {{ ... }}")
920    }
921}
922
923/// A [Mark-to-Mark Attachment Positioning Subtable](
924/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MMP).
925#[allow(missing_docs)]
926#[derive(Clone, Copy, Debug)]
927pub struct MarkToMarkAdjustment<'a> {
928    pub mark1_coverage: Coverage<'a>,
929    pub mark2_coverage: Coverage<'a>,
930    pub marks: MarkArray<'a>,
931    pub mark2_matrix: AnchorMatrix<'a>,
932}
933
934impl<'a> MarkToMarkAdjustment<'a> {
935    fn parse(data: &'a [u8]) -> Option<Self> {
936        let mut s = Stream::new(data);
937        match s.read::<u16>()? {
938            1 => {
939                let mark1_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
940                let mark2_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
941                let class_count = s.read::<u16>()?;
942                let marks = MarkArray::parse(s.read_at_offset16(data)?)?;
943                let mark2_matrix = AnchorMatrix::parse(s.read_at_offset16(data)?, class_count)?;
944                Some(Self {
945                    mark1_coverage,
946                    mark2_coverage,
947                    marks,
948                    mark2_matrix,
949                })
950            }
951            _ => None,
952        }
953    }
954}
955
956/// A glyph positioning
957/// [lookup subtable](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#table-organization)
958/// enumeration.
959#[allow(missing_docs)]
960#[derive(Clone, Copy, Debug)]
961pub enum PositioningSubtable<'a> {
962    Single(SingleAdjustment<'a>),
963    Pair(PairAdjustment<'a>),
964    Cursive(CursiveAdjustment<'a>),
965    MarkToBase(MarkToBaseAdjustment<'a>),
966    MarkToLigature(MarkToLigatureAdjustment<'a>),
967    MarkToMark(MarkToMarkAdjustment<'a>),
968    Context(ContextLookup<'a>),
969    ChainContext(ChainedContextLookup<'a>),
970}
971
972impl<'a> LookupSubtable<'a> for PositioningSubtable<'a> {
973    fn parse(data: &'a [u8], kind: u16) -> Option<Self> {
974        match kind {
975            1 => SingleAdjustment::parse(data).map(Self::Single),
976            2 => PairAdjustment::parse(data).map(Self::Pair),
977            3 => CursiveAdjustment::parse(data).map(Self::Cursive),
978            4 => MarkToBaseAdjustment::parse(data).map(Self::MarkToBase),
979            5 => MarkToLigatureAdjustment::parse(data).map(Self::MarkToLigature),
980            6 => MarkToMarkAdjustment::parse(data).map(Self::MarkToMark),
981            7 => ContextLookup::parse(data).map(Self::Context),
982            8 => ChainedContextLookup::parse(data).map(Self::ChainContext),
983            9 => crate::ggg::parse_extension_lookup(data, Self::parse),
984            _ => None,
985        }
986    }
987}
988
989impl<'a> PositioningSubtable<'a> {
990    /// Returns the subtable coverage.
991    #[inline]
992    pub fn coverage(&self) -> Coverage<'a> {
993        match self {
994            Self::Single(t) => t.coverage(),
995            Self::Pair(t) => t.coverage(),
996            Self::Cursive(t) => t.coverage,
997            Self::MarkToBase(t) => t.mark_coverage,
998            Self::MarkToLigature(t) => t.mark_coverage,
999            Self::MarkToMark(t) => t.mark1_coverage,
1000            Self::Context(t) => t.coverage(),
1001            Self::ChainContext(t) => t.coverage(),
1002        }
1003    }
1004}