Coverage Report

Created: 2025-10-13 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ttf-parser/src/tables/gpos.rs
Line
Count
Source
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
7
use core::convert::TryFrom;
8
9
use crate::opentype_layout::ChainedContextLookup;
10
use crate::opentype_layout::{Class, ClassDefinition, ContextLookup, Coverage, LookupSubtable};
11
use crate::parser::{
12
    FromData, FromSlice, LazyArray16, LazyArray32, NumFrom, Offset, Offset16, Stream,
13
};
14
use 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)]
20
pub struct HintingDevice<'a> {
21
    start_size: u16,
22
    end_size: u16,
23
    delta_format: u16,
24
    delta_values: LazyArray16<'a, u16>,
25
}
26
27
impl HintingDevice<'_> {
28
    /// Returns X-axis delta.
29
0
    pub fn x_delta(&self, units_per_em: u16, pixels_per_em: Option<(u16, u16)>) -> Option<i32> {
30
0
        let ppem = pixels_per_em.map(|(x, _)| x)?;
31
0
        self.get_delta(ppem, units_per_em)
32
0
    }
33
34
    /// Returns Y-axis delta.
35
0
    pub fn y_delta(&self, units_per_em: u16, pixels_per_em: Option<(u16, u16)>) -> Option<i32> {
36
0
        let ppem = pixels_per_em.map(|(_, y)| y)?;
37
0
        self.get_delta(ppem, units_per_em)
38
0
    }
39
40
0
    fn get_delta(&self, ppem: u16, scale: u16) -> Option<i32> {
41
0
        let f = self.delta_format;
42
0
        debug_assert!(matches!(f, 1..=3));
43
44
0
        if ppem == 0 || ppem < self.start_size || ppem > self.end_size {
45
0
            return None;
46
0
        }
47
48
0
        let s = ppem - self.start_size;
49
0
        let byte = self.delta_values.get(s >> (4 - f))?;
50
0
        let bits = byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f));
51
0
        let mask = 0xFFFF >> (16 - (1 << f));
52
53
0
        let mut delta = i64::from(bits & mask);
54
0
        if delta >= i64::from((mask + 1) >> 1) {
55
0
            delta -= i64::from(mask + 1);
56
0
        }
57
58
0
        i32::try_from(delta * i64::from(scale) / i64::from(ppem)).ok()
59
0
    }
60
}
61
62
impl core::fmt::Debug for HintingDevice<'_> {
63
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
64
0
        write!(f, "HintingDevice {{ ... }}")
65
0
    }
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)]
73
pub 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)]
82
pub enum Device<'a> {
83
    Hinting(HintingDevice<'a>),
84
    Variation(VariationDevice),
85
}
86
87
impl<'a> Device<'a> {
88
3
    pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
89
3
        let mut s = Stream::new(data);
90
3
        let first = s.read::<u16>()?;
91
3
        let second = s.read::<u16>()?;
92
3
        let format = s.read::<u16>()?;
93
3
        match format {
94
3
            1..=3 => {
95
0
                let start_size = first;
96
0
                let end_size = second;
97
0
                let count = (1 + (end_size - start_size)) >> (4 - format);
98
0
                let delta_values = s.read_array16(count)?;
99
0
                Some(Self::Hinting(HintingDevice {
100
0
                    start_size,
101
0
                    end_size,
102
0
                    delta_format: format,
103
0
                    delta_values,
104
0
                }))
105
            }
106
0
            0x8000 => Some(Self::Variation(VariationDevice {
107
0
                outer_index: first,
108
0
                inner_index: second,
109
0
            })),
110
3
            _ => None,
111
        }
112
3
    }
113
}
114
115
#[derive(Clone, Copy, Default, Debug)]
116
struct ValueFormatFlags(u8);
117
118
#[rustfmt::skip]
119
impl ValueFormatFlags {
120
0
    #[inline] fn x_placement(self) -> bool { self.0 & 0x01 != 0 }
121
0
    #[inline] fn y_placement(self) -> bool { self.0 & 0x02 != 0 }
122
0
    #[inline] fn x_advance(self) -> bool { self.0 & 0x04 != 0 }
123
0
    #[inline] fn y_advance(self) -> bool { self.0 & 0x08 != 0 }
124
0
    #[inline] fn x_placement_device(self) -> bool { self.0 & 0x10 != 0 }
125
0
    #[inline] fn y_placement_device(self) -> bool { self.0 & 0x20 != 0 }
126
0
    #[inline] fn x_advance_device(self) -> bool { self.0 & 0x40 != 0 }
127
0
    #[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
0
    fn size(self) -> usize {
132
        // The high 8 bits are not used, so make sure we ignore them using 0xFF.
133
0
        u16::SIZE * usize::num_from(self.0.count_ones())
134
0
    }
135
}
136
137
impl FromData for ValueFormatFlags {
138
    const SIZE: usize = 2;
139
140
    #[inline]
141
0
    fn parse(data: &[u8]) -> Option<Self> {
142
        // There is no data in high 8 bits, so skip it.
143
0
        Some(Self(data[1]))
144
0
    }
145
}
146
147
/// A [Value Record](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#value-record).
148
#[derive(Clone, Copy, Default, Debug)]
149
pub 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
169
impl<'a> ValueRecord<'a> {
170
    // Returns `None` only on parsing error.
171
0
    fn parse(
172
0
        table_data: &'a [u8],
173
0
        s: &mut Stream,
174
0
        flags: ValueFormatFlags,
175
0
    ) -> Option<ValueRecord<'a>> {
176
0
        let mut record = ValueRecord::default();
177
178
0
        if flags.x_placement() {
179
0
            record.x_placement = s.read::<i16>()?;
180
0
        }
181
182
0
        if flags.y_placement() {
183
0
            record.y_placement = s.read::<i16>()?;
184
0
        }
185
186
0
        if flags.x_advance() {
187
0
            record.x_advance = s.read::<i16>()?;
188
0
        }
189
190
0
        if flags.y_advance() {
191
0
            record.y_advance = s.read::<i16>()?;
192
0
        }
193
194
0
        if flags.x_placement_device() {
195
0
            if let Some(offset) = s.read::<Option<Offset16>>()? {
196
0
                record.x_placement_device =
197
0
                    table_data.get(offset.to_usize()..).and_then(Device::parse)
198
0
            }
199
0
        }
200
201
0
        if flags.y_placement_device() {
202
0
            if let Some(offset) = s.read::<Option<Offset16>>()? {
203
0
                record.y_placement_device =
204
0
                    table_data.get(offset.to_usize()..).and_then(Device::parse)
205
0
            }
206
0
        }
207
208
0
        if flags.x_advance_device() {
209
0
            if let Some(offset) = s.read::<Option<Offset16>>()? {
210
0
                record.x_advance_device =
211
0
                    table_data.get(offset.to_usize()..).and_then(Device::parse)
212
0
            }
213
0
        }
214
215
0
        if flags.y_advance_device() {
216
0
            if let Some(offset) = s.read::<Option<Offset16>>()? {
217
0
                record.y_advance_device =
218
0
                    table_data.get(offset.to_usize()..).and_then(Device::parse)
219
0
            }
220
0
        }
221
222
0
        Some(record)
223
0
    }
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)]
229
pub 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
243
impl<'a> ValueRecordsArray<'a> {
244
0
    fn parse(
245
0
        table_data: &'a [u8],
246
0
        count: u16,
247
0
        flags: ValueFormatFlags,
248
0
        s: &mut Stream<'a>,
249
0
    ) -> Option<Self> {
250
        Some(Self {
251
0
            table_data,
252
0
            flags,
253
0
            len: count,
254
0
            value_len: flags.size(),
255
0
            data: s.read_bytes(usize::from(count) * flags.size())?,
256
        })
257
0
    }
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
0
    pub fn is_empty(&self) -> bool {
267
0
        self.len == 0
268
0
    }
269
270
    /// Returns a [`ValueRecord`] at index.
271
0
    pub fn get(&self, index: u16) -> Option<ValueRecord<'a>> {
272
0
        let start = usize::from(index) * self.value_len;
273
0
        let end = start + self.value_len;
274
0
        let data = self.data.get(start..end)?;
275
0
        let mut s = Stream::new(data);
276
0
        ValueRecord::parse(self.table_data, &mut s, self.flags)
277
0
    }
278
}
279
280
impl core::fmt::Debug for ValueRecordsArray<'_> {
281
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
282
0
        write!(f, "ValueRecordsArray {{ ... }}")
283
0
    }
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)]
290
pub 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
301
impl<'a> SingleAdjustment<'a> {
302
0
    fn parse(data: &'a [u8]) -> Option<Self> {
303
0
        let mut s = Stream::new(data);
304
0
        match s.read::<u16>()? {
305
            1 => {
306
0
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
307
0
                let flags = s.read::<ValueFormatFlags>()?;
308
0
                let value = ValueRecord::parse(data, &mut s, flags)?;
309
0
                Some(Self::Format1 { coverage, value })
310
            }
311
            2 => {
312
0
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
313
0
                let flags = s.read::<ValueFormatFlags>()?;
314
0
                let count = s.read::<u16>()?;
315
0
                let values = ValueRecordsArray::parse(data, count, flags, &mut s)?;
316
0
                Some(Self::Format2 { coverage, values })
317
            }
318
0
            _ => None,
319
        }
320
0
    }
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)]
334
pub struct PairSet<'a> {
335
    data: &'a [u8],
336
    flags: (ValueFormatFlags, ValueFormatFlags),
337
    record_len: u8,
338
}
339
340
impl<'a> PairSet<'a> {
341
0
    fn parse(data: &'a [u8], flags: (ValueFormatFlags, ValueFormatFlags)) -> Option<Self> {
342
0
        let mut s = Stream::new(data);
343
0
        let count = s.read::<u16>()?;
344
        // Max len is 34, so u8 is just enough.
345
0
        let record_len = (GlyphId::SIZE + flags.0.size() + flags.1.size()) as u8;
346
0
        let data = s.read_bytes(usize::from(count) * usize::from(record_len))?;
347
0
        Some(Self {
348
0
            data,
349
0
            flags,
350
0
            record_len,
351
0
        })
352
0
    }
353
354
    #[inline]
355
0
    fn binary_search(&self, second: GlyphId) -> Option<&'a [u8]> {
356
        // Based on Rust std implementation.
357
358
0
        let mut size = self.data.len() / usize::from(self.record_len);
359
0
        if size == 0 {
360
0
            return None;
361
0
        }
362
363
0
        let get_record = |index| {
364
0
            let start = index * usize::from(self.record_len);
365
0
            let end = start + usize::from(self.record_len);
366
0
            self.data.get(start..end)
367
0
        };
368
369
0
        let get_glyph = |data: &[u8]| GlyphId(u16::from_be_bytes([data[0], data[1]]));
370
371
0
        let mut base = 0;
372
0
        while size > 1 {
373
0
            let half = size / 2;
374
0
            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
0
            let cmp = get_glyph(get_record(mid)?).cmp(&second);
379
0
            base = if cmp == core::cmp::Ordering::Greater {
380
0
                base
381
            } else {
382
0
                mid
383
            };
384
0
            size -= half;
385
        }
386
387
        // base is always in [0, size) because base <= mid.
388
0
        let value = get_record(base)?;
389
0
        if get_glyph(value).cmp(&second) == core::cmp::Ordering::Equal {
390
0
            Some(value)
391
        } else {
392
0
            None
393
        }
394
0
    }
395
396
    /// Returns a [`ValueRecord`] pair using the second glyph.
397
0
    pub fn get(&self, second: GlyphId) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> {
398
0
        let record_data = self.binary_search(second)?;
399
0
        let mut s = Stream::new(record_data);
400
0
        s.skip::<GlyphId>();
401
        Some((
402
0
            ValueRecord::parse(self.data, &mut s, self.flags.0)?,
403
0
            ValueRecord::parse(self.data, &mut s, self.flags.1)?,
404
        ))
405
0
    }
406
}
407
408
impl core::fmt::Debug for PairSet<'_> {
409
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
410
0
        write!(f, "PairSet {{ ... }}")
411
0
    }
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)]
418
pub 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
425
impl<'a> PairSets<'a> {
426
0
    fn new(
427
0
        data: &'a [u8],
428
0
        offsets: LazyArray16<'a, Option<Offset16>>,
429
0
        flags: (ValueFormatFlags, ValueFormatFlags),
430
0
    ) -> Self {
431
0
        Self {
432
0
            data,
433
0
            offsets,
434
0
            flags,
435
0
        }
436
0
    }
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
0
    pub fn is_empty(&self) -> bool {
455
0
        self.offsets.is_empty()
456
0
    }
457
}
458
459
impl core::fmt::Debug for PairSets<'_> {
460
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
461
0
        write!(f, "PairSets {{ ... }}")
462
0
    }
463
}
464
465
/// A [`ValueRecord`] pairs matrix used by [`PairAdjustment`].
466
#[derive(Clone, Copy)]
467
pub 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
477
impl<'a> ClassMatrix<'a> {
478
0
    fn parse(
479
0
        table_data: &'a [u8],
480
0
        counts: (u16, u16),
481
0
        flags: (ValueFormatFlags, ValueFormatFlags),
482
0
        s: &mut Stream<'a>,
483
0
    ) -> Option<Self> {
484
0
        let count = usize::num_from(u32::from(counts.0) * u32::from(counts.1));
485
        // Max len is 32, so u8 is just enough.
486
0
        let record_len = (flags.0.size() + flags.1.size()) as u8;
487
0
        let matrix = s.read_bytes(count * usize::from(record_len))?;
488
0
        Some(Self {
489
0
            table_data,
490
0
            matrix,
491
0
            counts,
492
0
            flags,
493
0
            record_len,
494
0
        })
495
0
    }
496
497
    /// Returns a [`ValueRecord`] pair using specified classes.
498
0
    pub fn get(&self, classes: (u16, u16)) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> {
499
0
        if classes.0 >= self.counts.0 || classes.1 >= self.counts.1 {
500
0
            return None;
501
0
        }
502
503
0
        let idx = usize::from(classes.0) * usize::from(self.counts.1) + usize::from(classes.1);
504
0
        let record = self.matrix.get(idx * usize::from(self.record_len)..)?;
505
506
0
        let mut s = Stream::new(record);
507
        Some((
508
0
            ValueRecord::parse(self.table_data, &mut s, self.flags.0)?,
509
0
            ValueRecord::parse(self.table_data, &mut s, self.flags.1)?,
510
        ))
511
0
    }
512
}
513
514
impl core::fmt::Debug for ClassMatrix<'_> {
515
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
516
0
        write!(f, "ClassMatrix {{ ... }}")
517
0
    }
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)]
524
pub 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
536
impl<'a> PairAdjustment<'a> {
537
0
    fn parse(data: &'a [u8]) -> Option<Self> {
538
0
        let mut s = Stream::new(data);
539
0
        match s.read::<u16>()? {
540
            1 => {
541
0
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
542
0
                let flags = (s.read::<ValueFormatFlags>()?, s.read::<ValueFormatFlags>()?);
543
0
                let count = s.read::<u16>()?;
544
0
                let offsets = s.read_array16(count)?;
545
0
                Some(Self::Format1 {
546
0
                    coverage,
547
0
                    sets: PairSets::new(data, offsets, flags),
548
0
                })
549
            }
550
            2 => {
551
0
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
552
0
                let flags = (s.read::<ValueFormatFlags>()?, s.read::<ValueFormatFlags>()?);
553
0
                let classes = (
554
0
                    ClassDefinition::parse(s.read_at_offset16(data)?)?,
555
0
                    ClassDefinition::parse(s.read_at_offset16(data)?)?,
556
                );
557
0
                let counts = (s.read::<u16>()?, s.read::<u16>()?);
558
                Some(Self::Format2 {
559
0
                    coverage,
560
0
                    classes,
561
0
                    matrix: ClassMatrix::parse(data, counts, flags, &mut s)?,
562
                })
563
            }
564
0
            _ => None,
565
        }
566
0
    }
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)]
579
struct EntryExitRecord {
580
    entry_anchor_offset: Option<Offset16>,
581
    exit_anchor_offset: Option<Offset16>,
582
}
583
584
impl FromData for EntryExitRecord {
585
    const SIZE: usize = 4;
586
587
    #[inline]
588
0
    fn parse(data: &[u8]) -> Option<Self> {
589
0
        let mut s = Stream::new(data);
590
        Some(Self {
591
0
            entry_anchor_offset: s.read::<Option<Offset16>>()?,
592
0
            exit_anchor_offset: s.read::<Option<Offset16>>()?,
593
        })
594
0
    }
595
}
596
597
/// A list of entry and exit [`Anchor`] pairs.
598
#[derive(Clone, Copy)]
599
pub struct CursiveAnchorSet<'a> {
600
    data: &'a [u8],
601
    records: LazyArray16<'a, EntryExitRecord>,
602
}
603
604
impl<'a> CursiveAnchorSet<'a> {
605
    /// Returns an entry [`Anchor`] at index.
606
0
    pub fn entry(&self, index: u16) -> Option<Anchor<'a>> {
607
0
        let offset = self.records.get(index)?.entry_anchor_offset?.to_usize();
608
0
        self.data.get(offset..).and_then(Anchor::parse)
609
0
    }
610
611
    /// Returns an exit [`Anchor`] at index.
612
0
    pub fn exit(&self, index: u16) -> Option<Anchor<'a>> {
613
0
        let offset = self.records.get(index)?.exit_anchor_offset?.to_usize();
614
0
        self.data.get(offset..).and_then(Anchor::parse)
615
0
    }
616
617
    /// Returns the number of items.
618
0
    pub fn len(&self) -> u16 {
619
0
        self.records.len()
620
0
    }
621
622
    /// Checks if the set is empty.
623
0
    pub fn is_empty(&self) -> bool {
624
0
        self.records.is_empty()
625
0
    }
626
}
627
628
impl core::fmt::Debug for CursiveAnchorSet<'_> {
629
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
630
0
        write!(f, "CursiveAnchorSet {{ ... }}")
631
0
    }
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)]
638
pub struct CursiveAdjustment<'a> {
639
    pub coverage: Coverage<'a>,
640
    pub sets: CursiveAnchorSet<'a>,
641
}
642
643
impl<'a> CursiveAdjustment<'a> {
644
0
    fn parse(data: &'a [u8]) -> Option<Self> {
645
0
        let mut s = Stream::new(data);
646
0
        match s.read::<u16>()? {
647
            1 => {
648
0
                let coverage = Coverage::parse(s.read_at_offset16(data)?)?;
649
0
                let count = s.read::<u16>()?;
650
0
                let records = s.read_array16(count)?;
651
0
                Some(Self {
652
0
                    coverage,
653
0
                    sets: CursiveAnchorSet { data, records },
654
0
                })
655
            }
656
0
            _ => None,
657
        }
658
0
    }
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)]
664
pub 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
675
impl<'a> MarkToBaseAdjustment<'a> {
676
0
    fn parse(data: &'a [u8]) -> Option<Self> {
677
0
        let mut s = Stream::new(data);
678
0
        match s.read::<u16>()? {
679
            1 => {
680
0
                let mark_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
681
0
                let base_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
682
0
                let class_count = s.read::<u16>()?;
683
0
                let marks = MarkArray::parse(s.read_at_offset16(data)?)?;
684
0
                let anchors = AnchorMatrix::parse(s.read_at_offset16(data)?, class_count)?;
685
0
                Some(Self {
686
0
                    mark_coverage,
687
0
                    base_coverage,
688
0
                    marks,
689
0
                    anchors,
690
0
                })
691
            }
692
0
            _ => None,
693
        }
694
0
    }
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)]
701
pub 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
708
impl<'a> MarkToLigatureAdjustment<'a> {
709
0
    fn parse(data: &'a [u8]) -> Option<Self> {
710
0
        let mut s = Stream::new(data);
711
0
        match s.read::<u16>()? {
712
            1 => {
713
0
                let mark_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
714
0
                let ligature_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
715
0
                let class_count = s.read::<u16>()?;
716
0
                let marks = MarkArray::parse(s.read_at_offset16(data)?)?;
717
0
                let ligature_array = LigatureArray::parse(s.read_at_offset16(data)?, class_count)?;
718
0
                Some(Self {
719
0
                    mark_coverage,
720
0
                    ligature_coverage,
721
0
                    marks,
722
0
                    ligature_array,
723
0
                })
724
            }
725
0
            _ => None,
726
        }
727
0
    }
728
}
729
730
/// An array or ligature anchor matrices.
731
#[derive(Clone, Copy)]
732
pub struct LigatureArray<'a> {
733
    data: &'a [u8],
734
    class_count: u16,
735
    offsets: LazyArray16<'a, Offset16>,
736
}
737
738
impl<'a> LigatureArray<'a> {
739
0
    fn parse(data: &'a [u8], class_count: u16) -> Option<Self> {
740
0
        let mut s = Stream::new(data);
741
0
        let count = s.read::<u16>()?;
742
0
        let offsets = s.read_array16(count)?;
743
0
        Some(Self {
744
0
            data,
745
0
            class_count,
746
0
            offsets,
747
0
        })
748
0
    }
749
750
    /// Returns an [`AnchorMatrix`] at index.
751
0
    pub fn get(&self, index: u16) -> Option<AnchorMatrix<'a>> {
752
0
        let offset = self.offsets.get(index)?.to_usize();
753
0
        let data = self.data.get(offset..)?;
754
0
        AnchorMatrix::parse(data, self.class_count)
755
0
    }
756
757
    /// Returns the array length.
758
0
    pub fn len(&self) -> u16 {
759
0
        self.offsets.len()
760
0
    }
761
762
    /// Checks if the array is empty.
763
0
    pub fn is_empty(&self) -> bool {
764
0
        self.offsets.is_empty()
765
0
    }
766
}
767
768
impl core::fmt::Debug for LigatureArray<'_> {
769
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
770
0
        write!(f, "LigatureArray {{ ... }}")
771
0
    }
772
}
773
774
#[derive(Clone, Copy)]
775
struct MarkRecord {
776
    class: Class,
777
    mark_anchor: Offset16,
778
}
779
780
impl FromData for MarkRecord {
781
    const SIZE: usize = 4;
782
783
    #[inline]
784
0
    fn parse(data: &[u8]) -> Option<Self> {
785
0
        let mut s = Stream::new(data);
786
        Some(Self {
787
0
            class: s.read::<Class>()?,
788
0
            mark_anchor: s.read::<Offset16>()?,
789
        })
790
0
    }
791
}
792
793
/// A [Mark Array](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table).
794
#[derive(Clone, Copy)]
795
pub struct MarkArray<'a> {
796
    data: &'a [u8],
797
    array: LazyArray16<'a, MarkRecord>,
798
}
799
800
impl<'a> MarkArray<'a> {
801
0
    fn parse(data: &'a [u8]) -> Option<Self> {
802
0
        let mut s = Stream::new(data);
803
0
        let count = s.read::<u16>()?;
804
0
        let array = s.read_array16(count)?;
805
0
        Some(Self { data, array })
806
0
    }
807
808
    /// Returns contained data at index.
809
0
    pub fn get(&self, index: u16) -> Option<(Class, Anchor<'a>)> {
810
0
        let record = self.array.get(index)?;
811
0
        let anchor = self
812
0
            .data
813
0
            .get(record.mark_anchor.to_usize()..)
814
0
            .and_then(Anchor::parse)?;
815
0
        Some((record.class, anchor))
816
0
    }
817
818
    /// Returns the array length.
819
0
    pub fn len(&self) -> u16 {
820
0
        self.array.len()
821
0
    }
822
823
    /// Checks if the array is empty.
824
0
    pub fn is_empty(&self) -> bool {
825
0
        self.array.is_empty()
826
0
    }
827
}
828
829
impl core::fmt::Debug for MarkArray<'_> {
830
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
831
0
        write!(f, "MarkArray {{ ... }}")
832
0
    }
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)]
839
pub 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
850
impl<'a> Anchor<'a> {
851
0
    fn parse(data: &'a [u8]) -> Option<Self> {
852
0
        let mut s = Stream::new(data);
853
0
        let format = s.read::<u16>()?;
854
0
        if !matches!(format, 1..=3) {
855
0
            return None;
856
0
        }
857
858
0
        let mut table = Anchor {
859
0
            x: s.read::<i16>()?,
860
0
            y: s.read::<i16>()?,
861
0
            x_device: None,
862
0
            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
0
        if format == 3 {
869
0
            table.x_device = s
870
0
                .read::<Option<Offset16>>()?
871
0
                .and_then(|offset| data.get(offset.to_usize()..))
872
0
                .and_then(Device::parse);
873
874
0
            table.y_device = s
875
0
                .read::<Option<Offset16>>()?
876
0
                .and_then(|offset| data.get(offset.to_usize()..))
877
0
                .and_then(Device::parse);
878
0
        }
879
880
0
        Some(table)
881
0
    }
882
}
883
884
/// An [`Anchor`] parsing helper.
885
#[derive(Clone, Copy)]
886
pub 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
895
impl<'a> AnchorMatrix<'a> {
896
0
    fn parse(data: &'a [u8], cols: u16) -> Option<Self> {
897
0
        let mut s = Stream::new(data);
898
0
        let rows = s.read::<u16>()?;
899
0
        let count = u32::from(rows) * u32::from(cols);
900
0
        let matrix = s.read_array32(count)?;
901
0
        Some(Self {
902
0
            data,
903
0
            rows,
904
0
            cols,
905
0
            matrix,
906
0
        })
907
0
    }
908
909
    /// Returns an [`Anchor`] at position.
910
0
    pub fn get(&self, row: u16, col: u16) -> Option<Anchor<'_>> {
911
0
        let idx = u32::from(row) * u32::from(self.cols) + u32::from(col);
912
0
        let offset = self.matrix.get(idx)??.to_usize();
913
0
        Anchor::parse(self.data.get(offset..)?)
914
0
    }
915
}
916
917
impl core::fmt::Debug for AnchorMatrix<'_> {
918
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
919
0
        write!(f, "AnchorMatrix {{ ... }}")
920
0
    }
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)]
927
pub 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
934
impl<'a> MarkToMarkAdjustment<'a> {
935
0
    fn parse(data: &'a [u8]) -> Option<Self> {
936
0
        let mut s = Stream::new(data);
937
0
        match s.read::<u16>()? {
938
            1 => {
939
0
                let mark1_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
940
0
                let mark2_coverage = Coverage::parse(s.read_at_offset16(data)?)?;
941
0
                let class_count = s.read::<u16>()?;
942
0
                let marks = MarkArray::parse(s.read_at_offset16(data)?)?;
943
0
                let mark2_matrix = AnchorMatrix::parse(s.read_at_offset16(data)?, class_count)?;
944
0
                Some(Self {
945
0
                    mark1_coverage,
946
0
                    mark2_coverage,
947
0
                    marks,
948
0
                    mark2_matrix,
949
0
                })
950
            }
951
0
            _ => None,
952
        }
953
0
    }
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)]
961
pub 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
972
impl<'a> LookupSubtable<'a> for PositioningSubtable<'a> {
973
0
    fn parse(data: &'a [u8], kind: u16) -> Option<Self> {
974
0
        match kind {
975
0
            1 => SingleAdjustment::parse(data).map(Self::Single),
976
0
            2 => PairAdjustment::parse(data).map(Self::Pair),
977
0
            3 => CursiveAdjustment::parse(data).map(Self::Cursive),
978
0
            4 => MarkToBaseAdjustment::parse(data).map(Self::MarkToBase),
979
0
            5 => MarkToLigatureAdjustment::parse(data).map(Self::MarkToLigature),
980
0
            6 => MarkToMarkAdjustment::parse(data).map(Self::MarkToMark),
981
0
            7 => ContextLookup::parse(data).map(Self::Context),
982
0
            8 => ChainedContextLookup::parse(data).map(Self::ChainContext),
983
0
            9 => crate::ggg::parse_extension_lookup(data, Self::parse),
984
0
            _ => None,
985
        }
986
0
    }
987
}
988
989
impl<'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
}