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