1 //! the [GPOS] table
2 //!
3 //! [GPOS]: https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
4 
5 #[path = "./value_record.rs"]
6 mod value_record;
7 
8 use crate::array::ComputedArray;
9 
10 /// reexport stuff from layout that we use
11 pub use super::layout::{
12     ClassDef, CoverageTable, Device, DeviceOrVariationIndex, FeatureList, FeatureVariations,
13     Lookup, ScriptList,
14 };
15 use super::layout::{ExtensionLookup, LookupFlag, Subtables};
16 pub use value_record::ValueRecord;
17 
18 #[cfg(test)]
19 #[path = "../tests/test_gpos.rs"]
20 mod spec_tests;
21 
22 include!("../../generated/generated_gpos.rs");
23 
24 /// A typed GPOS [LookupList](super::layout::LookupList) table
25 pub type PositionLookupList<'a> = super::layout::LookupList<'a, PositionLookup<'a>>;
26 
27 /// A GPOS [SequenceContext](super::layout::SequenceContext)
28 pub type PositionSequenceContext<'a> = super::layout::SequenceContext<'a>;
29 
30 /// A GPOS [ChainedSequenceContext](super::layout::ChainedSequenceContext)
31 pub type PositionChainContext<'a> = super::layout::ChainedSequenceContext<'a>;
32 
33 impl<'a> AnchorTable<'a> {
34     /// Attempt to resolve the `Device` or `VariationIndex` table for the
35     /// x_coordinate, if present
x_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>>36     pub fn x_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
37         match self {
38             AnchorTable::Format3(inner) => inner.x_device(),
39             _ => None,
40         }
41     }
42 
43     /// Attempt to resolve the `Device` or `VariationIndex` table for the
44     /// y_coordinate, if present
y_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>>45     pub fn y_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
46         match self {
47             AnchorTable::Format3(inner) => inner.y_device(),
48             _ => None,
49         }
50     }
51 }
52 
53 impl<'a, T: FontRead<'a>> ExtensionLookup<'a, T> for ExtensionPosFormat1<'a, T> {
extension(&self) -> Result<T, ReadError>54     fn extension(&self) -> Result<T, ReadError> {
55         self.extension()
56     }
57 }
58 
59 type PosSubtables<'a, T> = Subtables<'a, T, ExtensionPosFormat1<'a, T>>;
60 
61 /// The subtables from a GPOS lookup.
62 ///
63 /// This type is a convenience that removes the need to dig into the
64 /// [`PositionLookup`] enum in order to access subtables, and it also abstracts
65 /// away the distinction between extension and non-extension lookups.
66 pub enum PositionSubtables<'a> {
67     Single(PosSubtables<'a, SinglePos<'a>>),
68     Pair(PosSubtables<'a, PairPos<'a>>),
69     Cursive(PosSubtables<'a, CursivePosFormat1<'a>>),
70     MarkToBase(PosSubtables<'a, MarkBasePosFormat1<'a>>),
71     MarkToLig(PosSubtables<'a, MarkLigPosFormat1<'a>>),
72     MarkToMark(PosSubtables<'a, MarkMarkPosFormat1<'a>>),
73     Contextual(PosSubtables<'a, PositionSequenceContext<'a>>),
74     ChainContextual(PosSubtables<'a, PositionChainContext<'a>>),
75 }
76 
77 impl<'a> PositionLookup<'a> {
lookup_flag(&self) -> LookupFlag78     pub fn lookup_flag(&self) -> LookupFlag {
79         self.of_unit_type().lookup_flag()
80     }
81 
82     /// Different enumerations for GSUB and GPOS
lookup_type(&self) -> u1683     pub fn lookup_type(&self) -> u16 {
84         self.of_unit_type().lookup_type()
85     }
86 
mark_filtering_set(&self) -> u1687     pub fn mark_filtering_set(&self) -> u16 {
88         self.of_unit_type().mark_filtering_set()
89     }
90 
91     /// Return the subtables for this lookup.
92     ///
93     /// This method handles both extension and non-extension lookups, and saves
94     /// the caller needing to dig into the `PositionLookup` enum itself.
subtables(&self) -> Result<PositionSubtables<'a>, ReadError>95     pub fn subtables(&self) -> Result<PositionSubtables<'a>, ReadError> {
96         let raw_lookup = self.of_unit_type();
97         let offsets = raw_lookup.subtable_offsets();
98         let data = raw_lookup.offset_data();
99         match raw_lookup.lookup_type() {
100             1 => Ok(PositionSubtables::Single(Subtables::new(offsets, data))),
101             2 => Ok(PositionSubtables::Pair(Subtables::new(offsets, data))),
102             3 => Ok(PositionSubtables::Cursive(Subtables::new(offsets, data))),
103             4 => Ok(PositionSubtables::MarkToBase(Subtables::new(offsets, data))),
104             5 => Ok(PositionSubtables::MarkToLig(Subtables::new(offsets, data))),
105             6 => Ok(PositionSubtables::MarkToMark(Subtables::new(offsets, data))),
106             7 => Ok(PositionSubtables::Contextual(Subtables::new(offsets, data))),
107             8 => Ok(PositionSubtables::ChainContextual(Subtables::new(
108                 offsets, data,
109             ))),
110             9 => {
111                 let first = offsets.first().ok_or(ReadError::OutOfBounds)?.get();
112                 let ext: ExtensionPosFormat1<()> = first.resolve(data)?;
113                 match ext.extension_lookup_type() {
114                     1 => Ok(PositionSubtables::Single(Subtables::new_ext(offsets, data))),
115                     2 => Ok(PositionSubtables::Pair(Subtables::new_ext(offsets, data))),
116                     3 => Ok(PositionSubtables::Cursive(Subtables::new_ext(
117                         offsets, data,
118                     ))),
119                     4 => Ok(PositionSubtables::MarkToBase(Subtables::new_ext(
120                         offsets, data,
121                     ))),
122                     5 => Ok(PositionSubtables::MarkToLig(Subtables::new_ext(
123                         offsets, data,
124                     ))),
125                     6 => Ok(PositionSubtables::MarkToMark(Subtables::new_ext(
126                         offsets, data,
127                     ))),
128                     7 => Ok(PositionSubtables::Contextual(Subtables::new_ext(
129                         offsets, data,
130                     ))),
131                     8 => Ok(PositionSubtables::ChainContextual(Subtables::new_ext(
132                         offsets, data,
133                     ))),
134                     other => Err(ReadError::InvalidFormat(other as _)),
135                 }
136             }
137             other => Err(ReadError::InvalidFormat(other as _)),
138         }
139     }
140 }
141