1#[path = "./value_record.rs"]
6mod value_record;
7
8#[cfg(feature = "std")]
9mod closure;
10
11use crate::array::ComputedArray;
12
13pub use super::layout::{
15 ClassDef, CoverageTable, Device, DeviceOrVariationIndex, FeatureList, FeatureVariations,
16 Lookup, ScriptList,
17};
18use super::layout::{ExtensionLookup, LookupFlag, Subtables};
19pub use value_record::{Value, ValueContext, ValueRecord};
20
21#[cfg(test)]
22#[path = "../tests/test_gpos.rs"]
23mod spec_tests;
24
25include!("../../generated/generated_gpos.rs");
26
27pub type PositionLookupList<'a> = super::layout::LookupList<'a, PositionLookup<'a>>;
29
30pub type PositionSequenceContext<'a> = super::layout::SequenceContext<'a>;
32
33pub type PositionChainContext<'a> = super::layout::ChainedSequenceContext<'a>;
35
36impl<'a> AnchorTable<'a> {
37 pub fn x_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
40 match self {
41 AnchorTable::Format3(inner) => inner.x_device(),
42 _ => None,
43 }
44 }
45
46 pub fn y_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
49 match self {
50 AnchorTable::Format3(inner) => inner.y_device(),
51 _ => None,
52 }
53 }
54}
55
56impl<'a, T: FontRead<'a>> ExtensionLookup<'a, T> for ExtensionPosFormat1<'a, T> {
57 fn extension(&self) -> Result<T, ReadError> {
58 self.extension()
59 }
60}
61
62type PosSubtables<'a, T> = Subtables<'a, T, ExtensionPosFormat1<'a, T>>;
63
64pub enum PositionSubtables<'a> {
70 Single(PosSubtables<'a, SinglePos<'a>>),
71 Pair(PosSubtables<'a, PairPos<'a>>),
72 Cursive(PosSubtables<'a, CursivePosFormat1<'a>>),
73 MarkToBase(PosSubtables<'a, MarkBasePosFormat1<'a>>),
74 MarkToLig(PosSubtables<'a, MarkLigPosFormat1<'a>>),
75 MarkToMark(PosSubtables<'a, MarkMarkPosFormat1<'a>>),
76 Contextual(PosSubtables<'a, PositionSequenceContext<'a>>),
77 ChainContextual(PosSubtables<'a, PositionChainContext<'a>>),
78 EmptyExtension,
80}
81
82impl<'a> PositionLookup<'a> {
83 pub fn lookup_flag(&self) -> LookupFlag {
84 self.of_unit_type().lookup_flag()
85 }
86
87 pub fn lookup_type(&self) -> u16 {
89 self.of_unit_type().lookup_type()
90 }
91
92 pub fn mark_filtering_set(&self) -> Option<u16> {
93 self.of_unit_type().mark_filtering_set()
94 }
95
96 pub fn subtables(&self) -> Result<PositionSubtables<'a>, ReadError> {
101 let raw_lookup = self.of_unit_type();
102 let offsets = raw_lookup.subtable_offsets();
103 let data = raw_lookup.offset_data();
104 match raw_lookup.lookup_type() {
105 1 => Ok(PositionSubtables::Single(Subtables::new(offsets, data))),
106 2 => Ok(PositionSubtables::Pair(Subtables::new(offsets, data))),
107 3 => Ok(PositionSubtables::Cursive(Subtables::new(offsets, data))),
108 4 => Ok(PositionSubtables::MarkToBase(Subtables::new(offsets, data))),
109 5 => Ok(PositionSubtables::MarkToLig(Subtables::new(offsets, data))),
110 6 => Ok(PositionSubtables::MarkToMark(Subtables::new(offsets, data))),
111 7 => Ok(PositionSubtables::Contextual(Subtables::new(offsets, data))),
112 8 => Ok(PositionSubtables::ChainContextual(Subtables::new(
113 offsets, data,
114 ))),
115 9 => {
116 let Some(lookup_type) = offsets.iter().find_map(|off| {
120 off.get()
121 .resolve::<ExtensionPosFormat1<()>>(data)
122 .ok()
123 .map(|ext| ext.extension_lookup_type())
124 }) else {
125 return Ok(PositionSubtables::EmptyExtension);
126 };
127
128 match lookup_type {
129 1 => Ok(PositionSubtables::Single(Subtables::new_ext(offsets, data))),
130 2 => Ok(PositionSubtables::Pair(Subtables::new_ext(offsets, data))),
131 3 => Ok(PositionSubtables::Cursive(Subtables::new_ext(
132 offsets, data,
133 ))),
134 4 => Ok(PositionSubtables::MarkToBase(Subtables::new_ext(
135 offsets, data,
136 ))),
137 5 => Ok(PositionSubtables::MarkToLig(Subtables::new_ext(
138 offsets, data,
139 ))),
140 6 => Ok(PositionSubtables::MarkToMark(Subtables::new_ext(
141 offsets, data,
142 ))),
143 7 => Ok(PositionSubtables::Contextual(Subtables::new_ext(
144 offsets, data,
145 ))),
146 8 => Ok(PositionSubtables::ChainContextual(Subtables::new_ext(
147 offsets, data,
148 ))),
149 other => Err(ReadError::InvalidFormat(other as _)),
150 }
151 }
152 other => Err(ReadError::InvalidFormat(other as _)),
153 }
154 }
155}
156
157impl PairPosFormat2<'_> {
158 #[inline]
165 pub fn values(
166 &self,
167 class1: u16,
168 class2: u16,
169 context: &ValueContext,
170 ) -> Result<[Value; 2], ReadError> {
171 let format1 = self.value_format1();
172 let format1_len = format1.record_byte_len();
173 let format2 = self.value_format2();
174 let record_size = format1_len + format2.record_byte_len();
175 let data = self.offset_data();
176 let record_offset = (class1 as usize * record_size * self.class2_count() as usize)
178 + (class2 as usize * record_size)
179 + self.class1_records_byte_range().start;
180 Ok([
181 Value::read(data, record_offset, format1, context)?,
182 Value::read(data, record_offset + format1_len, format2, context)?,
183 ])
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn pair_pos2_values_match_value_records() {
193 let data = FontData::new(font_test_data::gpos::PAIRPOSFORMAT2);
194 let table = PairPosFormat2::read(data).unwrap();
195 let class1_count = table.class1_count();
196 let class2_count = table.class2_count();
197 let records = table.class1_records();
198 let context = ValueContext::default();
199 for class1 in 0..class1_count {
200 let class1_record = records.get(class1 as usize).unwrap();
201 let class2_records = class1_record.class2_records();
202 for class2 in 0..class2_count {
203 let record = class2_records.get(class2 as usize).unwrap();
204 let value_records = [record.value_record1, record.value_record2]
205 .map(|rec| rec.value(data, &context).unwrap());
206 let values = table.values(class1, class2, &context).unwrap();
207 assert_eq!(value_records, values);
208 }
209 }
210 }
211
212 #[test]
213 fn default_for_generics() {
214 let ExtensionSubtable::Single(inner) = ExtensionSubtable::default() else {
215 panic!("this is quite bad");
216 };
217
218 assert_eq!(inner.extension_lookup_type(), 0);
221
222 let SinglePos::Format1(_hmm) = inner.extension().unwrap_or_default() else {
223 panic!("unexpected");
224 };
225 }
226}