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::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<'_> = 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: usize = self.records.get(index)?.entry_anchor_offset?.to_usize();
608 self.data.get(index: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: usize = self.records.get(index)?.exit_anchor_offset?.to_usize();
614 self.data.get(index: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<'_> = Stream::new(data);
646 match s.read::<u16>()? {
647 1 => {
648 let coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
649 let count: u16 = s.read::<u16>()?;
650 let records: LazyArray16<'_, EntryExitRecord> = 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<'_> = Stream::new(data);
678 match s.read::<u16>()? {
679 1 => {
680 let mark_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
681 let base_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
682 let class_count: u16 = s.read::<u16>()?;
683 let marks: MarkArray<'_> = MarkArray::parse(data:s.read_at_offset16(data)?)?;
684 let anchors: AnchorMatrix<'_> = AnchorMatrix::parse(data:s.read_at_offset16(data)?, cols: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<'_> = Stream::new(data);
711 match s.read::<u16>()? {
712 1 => {
713 let mark_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
714 let ligature_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
715 let class_count: u16 = s.read::<u16>()?;
716 let marks: MarkArray<'_> = MarkArray::parse(data:s.read_at_offset16(data)?)?;
717 let ligature_array: LigatureArray<'_> = LigatureArray::parse(data: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<'_> = 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, Offset16>,
893}
894
895impl<'a> AnchorMatrix<'a> {
896 fn parse(data: &'a [u8], cols: u16) -> Option<Self> {
897 let mut s: Stream<'_> = Stream::new(data);
898 let rows: u16 = s.read::<u16>()?;
899 let count: u32 = u32::from(rows) * u32::from(cols);
900 let matrix: LazyArray32<'_, Offset16> = 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 = u32::from(row) * u32::from(self.cols) + u32::from(col);
912 let offset: usize = self.matrix.get(index:idx)?.to_usize();
913 Anchor::parse(self.data.get(index: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<'_> = Stream::new(data);
937 match s.read::<u16>()? {
938 1 => {
939 let mark1_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
940 let mark2_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?;
941 let class_count: u16 = s.read::<u16>()?;
942 let marks: MarkArray<'_> = MarkArray::parse(data:s.read_at_offset16(data)?)?;
943 let mark2_matrix: AnchorMatrix<'_> = AnchorMatrix::parse(data:s.read_at_offset16(data)?, cols: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: &SingleAdjustment<'_>) => t.coverage(),
995 Self::Pair(t: &PairAdjustment<'_>) => t.coverage(),
996 Self::Cursive(t: &CursiveAdjustment<'_>) => t.coverage,
997 Self::MarkToBase(t: &MarkToBaseAdjustment<'_>) => t.mark_coverage,
998 Self::MarkToLigature(t: &MarkToLigatureAdjustment<'_>) => t.mark_coverage,
999 Self::MarkToMark(t: &MarkToMarkAdjustment<'_>) => t.mark1_coverage,
1000 Self::Context(t: &ContextLookup<'_>) => t.coverage(),
1001 Self::ChainContext(t: &ChainedContextLookup<'_>) => t.coverage(),
1002 }
1003 }
1004}
1005