1 use crate::tables::layout::DeltaFormat;
2 
3 use super::*;
4 use font_test_data::gpos as test_data;
5 
6 #[test]
singleposformat1()7 fn singleposformat1() {
8     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-2-singleposformat1-subtable
9 
10     let table = SinglePosFormat1::read(test_data::SINGLEPOSFORMAT1.into()).unwrap();
11     assert_eq!(table.value_format(), ValueFormat::Y_PLACEMENT);
12     assert_eq!(table.value_record().y_placement.unwrap().get(), -80);
13     let coverage = table.coverage().unwrap();
14     assert_eq!(coverage.iter().count(), 10);
15 }
16 
17 #[test]
singleposformat2()18 fn singleposformat2() {
19     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-3-singleposformat2-subtable
20     let table = SinglePosFormat2::read(test_data::SINGLEPOSFORMAT2.into()).unwrap();
21     assert_eq!(
22         table.value_format(),
23         ValueFormat::X_PLACEMENT | ValueFormat::X_ADVANCE
24     );
25     assert_eq!(table.value_count(), 3);
26     assert_eq!(
27         table.value_records().get(0).unwrap().x_placement(),
28         Some(50)
29     );
30     assert_eq!(table.value_records().get(1).unwrap().x_advance(), Some(25));
31     assert_eq!(
32         table.value_records().get(2).unwrap().x_placement(),
33         Some(10)
34     );
35 
36     assert!(table.value_records().get(3).is_err());
37 }
38 
39 #[test]
pairposformat1()40 fn pairposformat1() {
41     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-4-pairposformat1-subtable
42 
43     let table = PairPosFormat1::read(test_data::PAIRPOSFORMAT1.into()).unwrap();
44     assert_eq!(table.value_format1(), ValueFormat::X_ADVANCE);
45     assert_eq!(table.value_format2(), ValueFormat::X_PLACEMENT);
46     assert_eq!(table.pair_set_count(), 2);
47 
48     let set1 = table.pair_sets().get(0).unwrap();
49     let set2 = table.pair_sets().get(1).unwrap();
50     assert_eq!(set1.pair_value_records().iter().count(), 1);
51     assert_eq!(set2.pair_value_records().iter().count(), 1);
52 
53     let rec1 = set1.pair_value_records().get(0).unwrap();
54     let rec2 = set2.pair_value_records().get(0).unwrap();
55 
56     assert_eq!(rec1.second_glyph(), GlyphId::new(0x59));
57     assert_eq!(rec1.value_record1().x_advance(), Some(-30));
58     assert!(rec1.value_record1().x_placement().is_none());
59     assert_eq!(rec1.value_record2().x_placement(), Some(-20));
60 
61     assert_eq!(rec2.second_glyph(), GlyphId::new(0x59));
62     assert_eq!(rec2.value_record1().x_advance(), Some(-40));
63     assert_eq!(rec2.value_record2().x_placement(), Some(-25));
64 }
65 
66 #[test]
pairposformat2()67 fn pairposformat2() {
68     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-5-pairposformat2-subtable
69 
70     let table = PairPosFormat2::read(test_data::PAIRPOSFORMAT2.into()).unwrap();
71     assert_eq!(table.value_format1().record_byte_len(), 2);
72     assert_eq!(table.value_format2().record_byte_len(), 0);
73     assert_eq!(table.class1_count(), 2);
74     assert_eq!(table.class1_records().iter().count(), 2);
75 
76     let class2 = table.class_def2().unwrap();
77     match class2 {
78         ClassDef::Format1(_) => panic!("expected format2"),
79         ClassDef::Format2(cls) => {
80             assert_eq!(
81                 cls.class_range_records()[0].start_glyph_id.get(),
82                 GlyphId::new(0x6A)
83             );
84         }
85     }
86 }
87 
88 #[test]
cursiveposformat1()89 fn cursiveposformat1() {
90     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-6-cursiveposformat1-subtable
91 
92     let table = CursivePosFormat1::read(test_data::CURSIVEPOSFORMAT1.into()).unwrap();
93     assert_eq!(table.entry_exit_count(), 2);
94     assert_eq!(table.entry_exit_record().len(), 2);
95 
96     let record2 = &table.entry_exit_record()[1];
97     let entry2: AnchorFormat1 = record2
98         .entry_anchor_offset()
99         .resolve(table.offset_data())
100         .unwrap()
101         .unwrap();
102     assert_eq!(entry2.x_coordinate(), 1500);
103     assert_eq!(entry2.y_coordinate(), 44);
104 }
105 
106 #[test]
markbaseposformat1()107 fn markbaseposformat1() {
108     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-7-markbaseposformat1-subtable
109     let table = MarkBasePosFormat1::read(test_data::MARKBASEPOSFORMAT1.into()).unwrap();
110     let base_array = table.base_array().unwrap();
111     assert_eq!(base_array.base_records().iter().count(), 1);
112     let record = base_array.base_records().get(0).unwrap();
113     assert_eq!(record.base_anchor_offsets.len(), 2);
114     let anchor1: AnchorFormat1 = record.base_anchor_offsets[1]
115         .get()
116         .resolve(base_array.offset_data())
117         .unwrap()
118         .unwrap();
119     assert_eq!(anchor1.x_coordinate(), 830);
120 }
121 
122 #[test]
markligposformat1()123 fn markligposformat1() {
124     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-8-markligposformat1-subtable
125 
126     let table = MarkLigPosFormat1::read(test_data::MARKLIGPOSFORMAT1.into()).unwrap();
127     let lig_array = table.ligature_array().unwrap();
128     assert_eq!(lig_array.ligature_count(), 1);
129     let lig_attach = lig_array.ligature_attaches().get(0).unwrap();
130     assert_eq!(lig_attach.component_count(), 3);
131     let comp_record = lig_attach
132         .component_records()
133         .iter()
134         .nth(2)
135         .unwrap()
136         .unwrap();
137     assert!(comp_record.ligature_anchor_offsets[0].get().is_null());
138     assert!(comp_record.ligature_anchor_offsets[1].get().is_null());
139 }
140 
141 #[test]
markmarkposformat1()142 fn markmarkposformat1() {
143     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-9-markmarkposformat1-subtable
144 
145     let table = MarkMarkPosFormat1::read(test_data::MARKMARKPOSFORMAT1.into()).unwrap();
146     assert_eq!(table.mark_class_count(), 1);
147     let mark2array = table.mark2_array().unwrap();
148     dbg!(mark2array.offset_data());
149     assert_eq!(mark2array.mark2_count(), 1);
150     assert_eq!(mark2array.mark2_records().iter().count(), 1);
151     let record = mark2array.mark2_records().get(0).unwrap();
152     assert_eq!(record.mark2_anchor_offsets.len(), 1);
153     let anchor_off = record.mark2_anchor_offsets[0].get();
154     let anchor: AnchorFormat1 = anchor_off
155         .resolve(mark2array.offset_data())
156         .unwrap()
157         .unwrap();
158     assert_eq!(anchor.x_coordinate(), 221);
159     assert_eq!(anchor.y_coordinate(), 301);
160 }
161 
162 #[test]
contextualposformat1()163 fn contextualposformat1() {
164     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-10-contextual-positioning-format-1
165 
166     let _table =
167         crate::tables::layout::SequenceContextFormat1::read(test_data::CONTEXTUALPOSFORMAT1.into())
168             .unwrap();
169 }
170 
171 #[test]
contextualposformat2()172 fn contextualposformat2() {
173     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-11-contextual-positioning-format-1
174     let _table =
175         crate::tables::layout::SequenceContextFormat2::read(test_data::CONTEXTUALPOSFORMAT2.into())
176             .unwrap();
177 }
178 
179 #[test]
contextualposformat3()180 fn contextualposformat3() {
181     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-12-contextual-positioning-format-3
182 
183     let _table =
184         crate::tables::layout::SequenceContextFormat3::read(test_data::CONTEXTUALPOSFORMAT3.into())
185             .unwrap();
186 }
187 
188 //FIXME: we don't have a way to instantiate individual records right now?
189 #[test]
sequencelookuprecord()190 fn sequencelookuprecord() {
191     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-13-sequencelookuprecord
192     let record = FontData::new(test_data::SEQUENCELOOKUPRECORD)
193         .read_ref_at::<crate::tables::layout::SequenceLookupRecord>(0)
194         .unwrap();
195     assert_eq!(record.sequence_index(), 1);
196     assert_eq!(record.lookup_list_index(), 1);
197 }
198 
199 #[test]
valueformattable()200 fn valueformattable() {
201     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-14-valueformat-table-and-valuerecord
202 
203     let table = SinglePosFormat1::read(test_data::VALUEFORMATTABLE.into()).unwrap();
204     assert_eq!(
205         table.value_format(),
206         ValueFormat::X_PLACEMENT
207             | ValueFormat::Y_ADVANCE
208             | ValueFormat::X_PLACEMENT_DEVICE
209             | ValueFormat::Y_ADVANCE_DEVICE
210     );
211     let record = table.value_record();
212     assert_eq!(record.y_advance(), Some(210));
213     let DeviceOrVariationIndex::Device(device) = record
214         .y_advance_device(table.offset_data())
215         .unwrap()
216         .unwrap()
217     else {
218         panic!("not a device");
219     };
220 
221     assert_eq!((device.start_size(), device.end_size()), (11, 15));
222     assert_eq!(device.delta_format(), DeltaFormat::Local2BitDeltas);
223     assert_eq!(device.delta_value(), [0x5540]);
224 }
225 
226 #[test]
anchorformat1()227 fn anchorformat1() {
228     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-15-anchorformat1-table
229 
230     let table = AnchorFormat1::read(test_data::ANCHORFORMAT1.into()).unwrap();
231     assert_eq!(table.x_coordinate(), 189);
232     assert_eq!(table.y_coordinate(), -103);
233 }
234 
235 #[test]
anchorformat2()236 fn anchorformat2() {
237     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-16-anchorformat2-table
238 
239     let table = AnchorFormat2::read(test_data::ANCHORFORMAT2.into()).unwrap();
240     assert_eq!(table.x_coordinate(), 322);
241     assert_eq!(table.anchor_point(), 13);
242 }
243 
244 #[test]
anchorformat3()245 fn anchorformat3() {
246     // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-17-anchorformat3-table
247 
248     let table = AnchorFormat3::read(test_data::ANCHORFORMAT3.into()).unwrap();
249     assert_eq!(table.x_coordinate(), 279);
250     assert_eq!(table.y_coordinate(), 1301);
251 
252     let x_dev = table.x_device().unwrap().unwrap();
253     let y_dev = table.y_device().unwrap().unwrap();
254 
255     let (DeviceOrVariationIndex::Device(x_dev), DeviceOrVariationIndex::Device(y_dev)) =
256         (x_dev, y_dev)
257     else {
258         panic!("missing device tables");
259     };
260 
261     assert_eq!(x_dev.delta_format(), DeltaFormat::Local4BitDeltas);
262     assert_eq!(x_dev.delta_value(), [0x1111, 0x2200]);
263 
264     assert_eq!(y_dev.delta_format(), DeltaFormat::Local4BitDeltas);
265     assert_eq!(y_dev.delta_value(), [0x1111, 0x2200]);
266 }
267 
268 //NOTE: I think the sample bytes are missing the actual anchor tables??
269 // and so we can't really round-trip this...
270 //#[test]
271 //fn markarraytable() {
272 //// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#example-18-markarray-table-and-markrecord
273 
274 //let bytes = [0x00, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x10];
275 //let table = MarkArray::read(&bytes).unwrap();
276 //let owned = table.to_owned_obj(&[]).unwrap();
277 //let dumped = crate::write::dump_table(&owned);
278 
279 //assert_hex_eq!(&bytes, &dumped);
280 //}[1, 1, 1, 1, 1]
281