1include!("../../generated/generated_gpos.rs");
6
7impl ReadArgs for PositionLookupList2 {
8 type Args = ();
9}
10
11impl<'a> FontRead<'a> for PositionLookupList2 {
12 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
13 read_fonts::tables::gpos::PositionLookupList2::read(data).map(|x| x.to_owned_table())
14 }
15}
16
17use std::collections::HashSet;
18
19use super::{
21 layout::{
22 ChainedSequenceContext, ClassDef, CoverageTable, DeviceOrVariationIndex, FeatureList,
23 FeatureVariations, Lookup, LookupList, LookupSubtable, LookupType, ScriptList,
24 SequenceContext, VariationIndex,
25 },
26 variations::{common_builder::RemapVarStore, ivs_builder::VariationIndexRemapping},
27};
28
29#[cfg(test)]
30mod extended;
31#[cfg(test)]
32mod spec_tests;
33
34pub mod builders;
35mod value_record;
36pub use value_record::ValueRecord;
37
38pub type PositionLookupList = LookupList<PositionLookup>;
40
41pub type OriginalPositionLookupList = PositionLookupList;
43
44pub type PositionLookupList2 = super::layout::LookupList2<PositionLookup>;
46
47super::layout::table_newtype!(
48 PositionSequenceContext,
49 SequenceContext,
50 read_fonts::tables::layout::SequenceContext<'a>
51);
52
53super::layout::table_newtype!(
54 PositionChainContext,
55 ChainedSequenceContext,
56 read_fonts::tables::layout::ChainedSequenceContext<'a>
57);
58
59impl Gpos {
60 pub fn new(
62 script_list: ScriptList,
63 feature_list: FeatureList,
64 lookup_list: PositionLookupList,
65 ) -> Self {
66 Self {
67 script_list: Some(script_list).into(),
68 feature_list: Some(feature_list).into(),
69 lookup_list: Some(lookup_list).into(),
70 ..Default::default()
71 }
72 }
73
74 fn compute_version(&self) -> MajorMinor {
75 if self.script_list2.is_some()
76 || self.feature_list2.is_some()
77 || self.lookup_list2.is_some()
78 {
79 MajorMinor::new(1, 2)
80 } else if self.feature_variations.is_none() {
81 MajorMinor::VERSION_1_0
82 } else {
83 MajorMinor::VERSION_1_1
84 }
85 }
86}
87
88super::layout::lookup_type!(gpos, SinglePos, 1);
89super::layout::lookup_type!(gpos, PairPos, 2);
90super::layout::lookup_type!(gpos, CursivePosFormat1, 3);
91super::layout::lookup_type!(gpos, MarkBasePosFormat1, 4);
92super::layout::lookup_type!(gpos, MarkLigPosFormat1, 5);
93super::layout::lookup_type!(gpos, MarkMarkPosFormat1, 6);
94super::layout::lookup_type!(gpos, PositionSequenceContext, 7);
95super::layout::lookup_type!(gpos, PositionChainContext, 8);
96super::layout::lookup_type!(gpos, ExtensionSubtable, 9);
97
98impl<T: LookupSubtable + FontWrite> FontWrite for ExtensionPosFormat1<T> {
99 fn write_into(&self, writer: &mut TableWriter) {
100 1u16.write_into(writer);
101 T::TYPE.write_into(writer);
102 self.extension.write_into(writer);
103 }
104}
105
106impl ReadArgs for PositionLookup {
108 type Args = ();
109}
110
111impl<'a> FontRead<'a> for PositionLookup {
112 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
113 read_fonts::tables::gpos::PositionLookup::read(data).map(|x| x.to_owned_table())
114 }
115}
116
117impl ReadArgs for PositionLookupList {
118 type Args = ();
119}
120
121impl<'a> FontRead<'a> for PositionLookupList {
122 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
123 read_fonts::tables::gpos::OriginalPositionLookupList::read(data).map(|x| x.to_owned_table())
124 }
125}
126
127impl SinglePosFormat1 {
128 fn compute_value_format(&self) -> ValueFormat {
129 self.value_record.format()
130 }
131}
132
133impl SinglePosFormat2 {
134 fn compute_value_format(&self) -> ValueFormat {
135 self.value_records
136 .first()
137 .map(ValueRecord::format)
138 .unwrap_or(ValueFormat::empty())
139 }
140}
141
142impl PairPosFormat1 {
143 fn compute_value_format1(&self) -> ValueFormat {
144 self.pair_sets
145 .first()
146 .and_then(|pairset| pairset.pair_value_records.first())
147 .map(|rec| rec.value_record1.format())
148 .unwrap_or(ValueFormat::empty())
149 }
150
151 fn compute_value_format2(&self) -> ValueFormat {
152 self.pair_sets
153 .first()
154 .and_then(|pairset| pairset.pair_value_records.first())
155 .map(|rec| rec.value_record2.format())
156 .unwrap_or(ValueFormat::empty())
157 }
158
159 fn check_format_consistency(&self, ctx: &mut ValidationCtx) {
160 let vf1 = self.compute_value_format1();
161 let vf2 = self.compute_value_format2();
162 ctx.with_array_items(self.pair_sets.iter(), |ctx, item| {
163 ctx.in_field("pair_value_records", |ctx| {
164 if item.pair_value_records.iter().any(|pairset| {
165 pairset.value_record1.format() != vf1 || pairset.value_record2.format() != vf2
166 }) {
167 ctx.report("all ValueRecords must have same format")
168 }
169 })
170 })
171 }
172}
173
174impl PairPosFormat2 {
175 fn compute_value_format1(&self) -> ValueFormat {
176 self.class1_records
177 .first()
178 .and_then(|rec| rec.class2_records.first())
179 .map(|rec| rec.value_record1.format())
180 .unwrap_or(ValueFormat::empty())
181 }
182
183 fn compute_value_format2(&self) -> ValueFormat {
184 self.class1_records
185 .first()
186 .and_then(|rec| rec.class2_records.first())
187 .map(|rec| rec.value_record2.format())
188 .unwrap_or(ValueFormat::empty())
189 }
190
191 fn compute_class1_count(&self) -> u16 {
192 self.class_def1.class_count().try_into().unwrap_or(0)
193 }
194
195 fn compute_class2_count(&self) -> u16 {
196 self.class_def2.class_count().try_into().unwrap_or(0)
197 }
198
199 fn check_length_and_format_conformance(&self, ctx: &mut ValidationCtx) {
200 let n_class_1s = self.class_def1.class_count();
201 let n_class_2s = self.class_def2.class_count();
202 if n_class_1s > u16::MAX as u32 || n_class_2s > u16::MAX as u32 {
203 ctx.report("pair positioning class counts must fit in 16 bits");
204 }
205 let format_1 = self.compute_value_format1();
206 let format_2 = self.compute_value_format2();
207 if self.class1_records.len() != n_class_1s as usize {
208 ctx.report("class1_records length must match number of class1 classes");
209 }
210 ctx.in_field("class1_records", |ctx| {
211 ctx.with_array_items(self.class1_records.iter(), |ctx, c1rec| {
212 if c1rec.class2_records.len() != n_class_2s as usize {
213 ctx.report("class2_records length must match number of class2 classes ");
214 }
215 if c1rec.class2_records.iter().any(|rec| {
216 rec.value_record1.format() != format_1 || rec.value_record2.format() != format_2
217 }) {
218 ctx.report("all value records should report the same format");
219 }
220 })
221 });
222 }
223}
224
225impl MarkBasePosFormat1 {
226 fn compute_mark_class_count(&self) -> u16 {
227 self.mark_array.class_count()
228 }
229}
230
231impl MarkMarkPosFormat1 {
232 fn compute_mark_class_count(&self) -> u16 {
233 self.mark1_array.class_count()
234 }
235}
236
237impl MarkLigPosFormat1 {
238 fn compute_mark_class_count(&self) -> u16 {
239 self.mark_array.class_count()
240 }
241}
242
243impl MarkArray {
244 fn class_count(&self) -> u16 {
245 self.mark_records
246 .iter()
247 .map(|rec| rec.mark_class)
248 .collect::<HashSet<_>>()
249 .len() as u16
250 }
251}
252
253impl RemapVarStore<VariationIndex> for ValueRecord {
254 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
255 for table in [
256 self.x_placement_device.as_mut(),
257 self.y_placement_device.as_mut(),
258 self.x_advance_device.as_mut(),
259 self.y_advance_device.as_mut(),
260 ]
261 .into_iter()
262 .flatten()
263 {
264 table.remap_variation_indices(key_map)
265 }
266 }
267}
268
269impl RemapVarStore<VariationIndex> for DeviceOrVariationIndex {
270 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
271 if let DeviceOrVariationIndex::PendingVariationIndex(table) = self {
272 *self = key_map.get(table.delta_set_id).unwrap().into();
273 }
274 }
275}
276
277impl RemapVarStore<VariationIndex> for AnchorTable {
278 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
279 if let AnchorTable::Format3(table) = self {
280 table
281 .x_device
282 .as_mut()
283 .into_iter()
284 .chain(table.y_device.as_mut())
285 .for_each(|x| x.remap_variation_indices(key_map))
286 }
287 }
288}
289
290impl RemapVarStore<VariationIndex> for Gpos {
291 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
292 if let Some(list) = self.lookup_list.as_mut() {
293 for lookup in &mut list.lookups {
294 lookup.remap_variation_indices(key_map);
295 }
296 }
297 if let Some(list) = self.lookup_list2.as_mut() {
298 for lookup in &mut list.lookups {
299 lookup.remap_variation_indices(key_map);
300 }
301 }
302 }
303}
304
305impl RemapVarStore<VariationIndex> for PositionLookupList {
306 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
307 for lookup in &mut self.lookups {
308 lookup.remap_variation_indices(key_map)
309 }
310 }
311}
312
313impl RemapVarStore<VariationIndex> for PositionLookup {
314 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
315 match self {
316 PositionLookup::Single(lookup) => lookup.remap_variation_indices(key_map),
317 PositionLookup::Pair(lookup) => lookup.remap_variation_indices(key_map),
318 PositionLookup::Cursive(lookup) => lookup.remap_variation_indices(key_map),
319 PositionLookup::MarkToBase(lookup) => lookup.remap_variation_indices(key_map),
320 PositionLookup::MarkToLig(lookup) => lookup.remap_variation_indices(key_map),
321 PositionLookup::MarkToMark(lookup) => lookup.remap_variation_indices(key_map),
322 PositionLookup::Extension(lookup) => lookup.remap_variation_indices(key_map),
323
324 PositionLookup::Contextual(_) | PositionLookup::ChainContextual(_) => (),
326 }
327 }
328}
329
330impl RemapVarStore<VariationIndex> for ExtensionSubtable {
331 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
332 match self {
333 ExtensionSubtable::Single(table) => table.remap_variation_indices(key_map),
334 ExtensionSubtable::Pair(table) => table.remap_variation_indices(key_map),
335 ExtensionSubtable::Cursive(table) => table.remap_variation_indices(key_map),
336 ExtensionSubtable::MarkToBase(table) => table.remap_variation_indices(key_map),
337 ExtensionSubtable::MarkToLig(table) => table.remap_variation_indices(key_map),
338 ExtensionSubtable::MarkToMark(table) => table.remap_variation_indices(key_map),
339
340 ExtensionSubtable::Contextual(_) | ExtensionSubtable::ChainContextual(_) => (),
342 }
343 }
344}
345
346impl<T: RemapVarStore<VariationIndex>> RemapVarStore<VariationIndex> for ExtensionPosFormat1<T> {
347 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
348 self.extension.as_mut().remap_variation_indices(key_map)
349 }
350}
351
352impl<T: RemapVarStore<VariationIndex>> RemapVarStore<VariationIndex> for Lookup<T> {
353 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
354 for subtable in &mut self.subtables {
355 subtable.remap_variation_indices(key_map)
356 }
357 }
358}
359
360impl RemapVarStore<VariationIndex> for SinglePos {
361 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
362 match self {
363 SinglePos::Format1(table) => table.remap_variation_indices(key_map),
364 SinglePos::Format2(table) => table.remap_variation_indices(key_map),
365 SinglePos::Format3(table) => table.remap_variation_indices(key_map),
366 SinglePos::Format4(table) => table.remap_variation_indices(key_map),
367 }
368 }
369}
370
371impl RemapVarStore<VariationIndex> for SinglePosFormat1 {
372 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
373 self.value_record.remap_variation_indices(key_map);
374 }
375}
376
377impl RemapVarStore<VariationIndex> for SinglePosFormat2 {
378 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
379 for rec in &mut self.value_records {
380 rec.remap_variation_indices(key_map);
381 }
382 }
383}
384
385impl RemapVarStore<VariationIndex> for PairPosFormat1 {
386 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
387 for pairset in &mut self.pair_sets {
388 for pairrec in &mut pairset.pair_value_records {
389 pairrec.value_record1.remap_variation_indices(key_map);
390 pairrec.value_record2.remap_variation_indices(key_map);
391 }
392 }
393 }
394}
395
396impl RemapVarStore<VariationIndex> for PairPosFormat2 {
397 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
398 for class1rec in &mut self.class1_records {
399 for class2rec in &mut class1rec.class2_records {
400 class2rec.value_record1.remap_variation_indices(key_map);
401 class2rec.value_record2.remap_variation_indices(key_map);
402 }
403 }
404 }
405}
406
407impl RemapVarStore<VariationIndex> for PairPos {
408 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
409 match self {
410 PairPos::Format1(table) => table.remap_variation_indices(key_map),
411 PairPos::Format2(table) => table.remap_variation_indices(key_map),
412 PairPos::Format3(table) => table.remap_variation_indices(key_map),
413 PairPos::Format4(table) => table.remap_variation_indices(key_map),
414 }
415 }
416}
417
418impl RemapVarStore<VariationIndex> for MarkBasePosFormat1 {
419 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
420 self.mark_array.as_mut().remap_variation_indices(key_map);
421 for rec in &mut self.base_array.as_mut().base_records {
422 for anchor in &mut rec.base_anchors {
423 if let Some(anchor) = anchor.as_mut() {
424 anchor.remap_variation_indices(key_map);
425 }
426 }
427 }
428 }
429}
430
431impl RemapVarStore<VariationIndex> for MarkMarkPosFormat1 {
432 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
433 self.mark1_array.as_mut().remap_variation_indices(key_map);
434 for rec in &mut self.mark2_array.as_mut().mark2_records {
435 for anchor in &mut rec.mark2_anchors {
436 if let Some(anchor) = anchor.as_mut() {
437 anchor.remap_variation_indices(key_map);
438 }
439 }
440 }
441 }
442}
443
444impl RemapVarStore<VariationIndex> for MarkLigPosFormat1 {
445 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
446 self.mark_array.as_mut().remap_variation_indices(key_map);
447 for lig in &mut self.ligature_array.as_mut().ligature_attaches {
448 for rec in &mut lig.component_records {
449 for anchor in &mut rec.ligature_anchors {
450 if let Some(anchor) = anchor.as_mut() {
451 anchor.remap_variation_indices(key_map);
452 }
453 }
454 }
455 }
456 }
457}
458
459impl RemapVarStore<VariationIndex> for CursivePosFormat1 {
460 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
461 for rec in &mut self.entry_exit_record {
462 for anchor in [rec.entry_anchor.as_mut(), rec.exit_anchor.as_mut()]
463 .into_iter()
464 .flatten()
465 {
466 anchor.remap_variation_indices(key_map);
467 }
468 }
469 }
470}
471
472impl RemapVarStore<VariationIndex> for MarkArray {
473 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
474 for rec in &mut self.mark_records {
475 rec.mark_anchor.remap_variation_indices(key_map);
476 }
477 }
478}
479
480super::layout::lookup_type!(gpos, CursivePos, 3);
481super::layout::lookup_type!(gpos, CursivePosFormat2, 3);
482super::layout::original_lookup!(PositionLookup, Cursive, CursivePosFormat1, CursivePos);
483
484super::layout::lookup_type!(gpos, MarkBasePos, 4);
485super::layout::lookup_type!(gpos, MarkBasePosFormat2, 4);
486super::layout::original_lookup!(PositionLookup, MarkToBase, MarkBasePosFormat1, MarkBasePos);
487
488super::layout::lookup_type!(gpos, MarkLigPos, 5);
489super::layout::lookup_type!(gpos, MarkLigPosFormat2, 5);
490super::layout::original_lookup!(PositionLookup, MarkToLig, MarkLigPosFormat1, MarkLigPos);
491
492super::layout::lookup_type!(gpos, MarkMarkPos, 6);
493super::layout::lookup_type!(gpos, MarkMarkPosFormat2, 6);
494super::layout::original_lookup!(PositionLookup, MarkToMark, MarkMarkPosFormat1, MarkMarkPos);
495
496impl SinglePosFormat3 {
497 fn compute_value_format(&self) -> ValueFormat {
498 self.value_record.format()
499 }
500}
501
502impl SinglePosFormat4 {
503 fn compute_value_format(&self) -> ValueFormat {
504 self.value_records
505 .first()
506 .map(ValueRecord::format)
507 .unwrap_or(ValueFormat::empty())
508 }
509
510 fn check_format_consistency(&self, ctx: &mut ValidationCtx) {
511 let format = self.compute_value_format();
512 if self
513 .value_records
514 .iter()
515 .any(|record| record.format() != format)
516 {
517 ctx.report("all ValueRecords must have same format");
518 }
519 }
520}
521
522impl PairPosFormat3 {
523 fn compute_value_format1(&self) -> ValueFormat {
524 self.pair_sets
525 .iter()
526 .find_map(|pairset| pairset.pair_value_records.first())
527 .map(|rec| rec.value_record1.format())
528 .unwrap_or(ValueFormat::empty())
529 }
530
531 fn compute_value_format2(&self) -> ValueFormat {
532 self.pair_sets
533 .iter()
534 .find_map(|pairset| pairset.pair_value_records.first())
535 .map(|rec| rec.value_record2.format())
536 .unwrap_or(ValueFormat::empty())
537 }
538
539 fn check_format_consistency(&self, ctx: &mut ValidationCtx) {
540 let vf1 = self.compute_value_format1();
541 let vf2 = self.compute_value_format2();
542 ctx.with_array_items(self.pair_sets.iter(), |ctx, item| {
543 ctx.in_field("pair_value_records", |ctx| {
544 if item.pair_value_records.iter().any(|pairset| {
545 pairset.value_record1.format() != vf1 || pairset.value_record2.format() != vf2
546 }) {
547 ctx.report("all ValueRecords must have same format")
548 }
549 })
550 })
551 }
552}
553
554impl PairPosFormat4 {
555 fn compute_value_format1(&self) -> ValueFormat {
556 self.class1_records
557 .first()
558 .and_then(|rec| rec.class2_records.first())
559 .map(|rec| rec.value_record1.format())
560 .unwrap_or(ValueFormat::empty())
561 }
562
563 fn compute_value_format2(&self) -> ValueFormat {
564 self.class1_records
565 .first()
566 .and_then(|rec| rec.class2_records.first())
567 .map(|rec| rec.value_record2.format())
568 .unwrap_or(ValueFormat::empty())
569 }
570
571 fn compute_class1_count(&self) -> u16 {
572 self.class1_records
573 .len()
574 .try_into()
575 .expect("class count exceeds 16 bits")
576 }
577
578 fn compute_class2_count(&self) -> u16 {
579 self.class1_records
580 .first()
581 .map_or(0, |row| row.class2_records.len())
582 .try_into()
583 .expect("class count exceeds 16 bits")
584 }
585
586 fn check_length_and_format_conformance(&self, ctx: &mut ValidationCtx) {
587 let n_class_1s = self.class1_records.len();
588 let n_class_2s = self
589 .class1_records
590 .first()
591 .map_or(0, |row| row.class2_records.len());
592 if n_class_1s > u16::MAX as usize || n_class_2s > u16::MAX as usize {
593 ctx.report("pair positioning class counts must fit in 16 bits");
594 }
595 let format_1 = self.compute_value_format1();
596 let format_2 = self.compute_value_format2();
597 if self
598 .class_def1
599 .iter()
600 .any(|(_, class)| class as usize >= n_class_1s)
601 || self
602 .class_def2
603 .iter()
604 .any(|(_, class)| class as usize >= n_class_2s)
605 {
606 ctx.report("class definitions must index the value record matrix");
607 }
608 ctx.in_field("class1_records", |ctx| {
609 ctx.with_array_items(self.class1_records.iter(), |ctx, c1rec| {
610 if c1rec.class2_records.len() != n_class_2s {
611 ctx.report("class2_records length must match number of class2 classes ");
612 }
613 if c1rec.class2_records.iter().any(|rec| {
614 rec.value_record1.format() != format_1 || rec.value_record2.format() != format_2
615 }) {
616 ctx.report("all value records should report the same format");
617 }
618 })
619 });
620 }
621}
622
623impl MarkBasePosFormat2 {
624 fn compute_mark_class_count(&self) -> u16 {
625 self.mark_array
626 .class_count()
627 .try_into()
628 .expect("mark class count exceeds 16 bits")
629 }
630
631 fn check_class_dimensions(&self, ctx: &mut ValidationCtx) {
632 let classes = self.mark_array.class_count() as usize;
633 if (self
634 .base_array
635 .base_records
636 .iter()
637 .map(|rec| rec.base_anchors.len()))
638 .any(|len| len != classes)
639 {
640 ctx.report("anchor arrays must match the mark class count");
641 }
642 }
643}
644
645impl MarkMarkPosFormat2 {
646 fn compute_mark_class_count(&self) -> u16 {
647 self.mark1_array
648 .class_count()
649 .try_into()
650 .expect("mark class count exceeds 16 bits")
651 }
652
653 fn check_class_dimensions(&self, ctx: &mut ValidationCtx) {
654 let classes = self.mark1_array.class_count() as usize;
655 if (self
656 .mark2_array
657 .mark2_records
658 .iter()
659 .map(|rec| rec.mark2_anchors.len()))
660 .any(|len| len != classes)
661 {
662 ctx.report("anchor arrays must match the mark class count");
663 }
664 }
665}
666
667impl MarkLigPosFormat2 {
668 fn compute_mark_class_count(&self) -> u16 {
669 self.mark_array
670 .class_count()
671 .try_into()
672 .expect("mark class count exceeds 16 bits")
673 }
674
675 fn check_class_dimensions(&self, ctx: &mut ValidationCtx) {
676 let classes = self.mark_array.class_count() as usize;
677 if (self
678 .ligature_array
679 .ligature_attaches
680 .iter()
681 .flat_map(|lig| lig.component_records.iter())
682 .map(|rec| rec.ligature_anchors.len()))
683 .any(|len| len != classes)
684 {
685 ctx.report("anchor arrays must match the mark class count");
686 }
687 }
688}
689
690impl MarkArray2 {
691 fn class_count(&self) -> u32 {
692 self.mark_records
693 .iter()
694 .map(|rec| u32::from(rec.mark_class) + 1)
695 .max()
696 .unwrap_or(0)
697 }
698
699 fn check_class_count(&self, ctx: &mut ValidationCtx) {
700 if self.class_count() > u16::MAX as u32 {
701 ctx.report("mark class count must fit in 16 bits");
702 }
703 }
704}
705
706impl RemapVarStore<VariationIndex> for SinglePosFormat3 {
707 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
708 self.value_record.remap_variation_indices(key_map);
709 }
710}
711
712impl RemapVarStore<VariationIndex> for SinglePosFormat4 {
713 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
714 for rec in &mut self.value_records {
715 rec.remap_variation_indices(key_map);
716 }
717 }
718}
719
720impl RemapVarStore<VariationIndex> for PairPosFormat3 {
721 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
722 for pairset in &mut self.pair_sets {
723 for pairrec in &mut pairset.pair_value_records {
724 pairrec.value_record1.remap_variation_indices(key_map);
725 pairrec.value_record2.remap_variation_indices(key_map);
726 }
727 }
728 }
729}
730
731impl RemapVarStore<VariationIndex> for PairPosFormat4 {
732 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
733 for class1rec in &mut self.class1_records {
734 for class2rec in &mut class1rec.class2_records {
735 class2rec.value_record1.remap_variation_indices(key_map);
736 class2rec.value_record2.remap_variation_indices(key_map);
737 }
738 }
739 }
740}
741
742impl RemapVarStore<VariationIndex> for MarkBasePosFormat2 {
743 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
744 self.mark_array.as_mut().remap_variation_indices(key_map);
745 for rec in &mut self.base_array.as_mut().base_records {
746 for anchor in &mut rec.base_anchors {
747 if let Some(anchor) = anchor.as_mut() {
748 anchor.remap_variation_indices(key_map);
749 }
750 }
751 }
752 }
753}
754
755impl RemapVarStore<VariationIndex> for MarkMarkPosFormat2 {
756 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
757 self.mark1_array.as_mut().remap_variation_indices(key_map);
758 for rec in &mut self.mark2_array.as_mut().mark2_records {
759 for anchor in &mut rec.mark2_anchors {
760 if let Some(anchor) = anchor.as_mut() {
761 anchor.remap_variation_indices(key_map);
762 }
763 }
764 }
765 }
766}
767
768impl RemapVarStore<VariationIndex> for MarkLigPosFormat2 {
769 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
770 self.mark_array.as_mut().remap_variation_indices(key_map);
771 for lig in &mut self.ligature_array.as_mut().ligature_attaches {
772 for rec in &mut lig.component_records {
773 for anchor in &mut rec.ligature_anchors {
774 if let Some(anchor) = anchor.as_mut() {
775 anchor.remap_variation_indices(key_map);
776 }
777 }
778 }
779 }
780 }
781}
782
783impl RemapVarStore<VariationIndex> for CursivePosFormat2 {
784 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
785 for rec in &mut self.entry_exit_record {
786 for anchor in [rec.entry_anchor.as_mut(), rec.exit_anchor.as_mut()]
787 .into_iter()
788 .flatten()
789 {
790 anchor.remap_variation_indices(key_map);
791 }
792 }
793 }
794}
795
796impl RemapVarStore<VariationIndex> for MarkArray2 {
797 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
798 for rec in &mut self.mark_records {
799 rec.mark_anchor.remap_variation_indices(key_map);
800 }
801 }
802}
803
804impl RemapVarStore<VariationIndex> for CursivePos {
805 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
806 match self {
807 Self::Format1(t) => t.remap_variation_indices(key_map),
808 Self::Format2(t) => t.remap_variation_indices(key_map),
809 }
810 }
811}
812
813impl RemapVarStore<VariationIndex> for MarkBasePos {
814 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
815 match self {
816 Self::Format1(t) => t.remap_variation_indices(key_map),
817 Self::Format2(t) => t.remap_variation_indices(key_map),
818 }
819 }
820}
821
822impl RemapVarStore<VariationIndex> for MarkMarkPos {
823 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
824 match self {
825 Self::Format1(t) => t.remap_variation_indices(key_map),
826 Self::Format2(t) => t.remap_variation_indices(key_map),
827 }
828 }
829}
830
831impl RemapVarStore<VariationIndex> for MarkLigPos {
832 fn remap_variation_indices(&mut self, key_map: &VariationIndexRemapping) {
833 match self {
834 Self::Format1(t) => t.remap_variation_indices(key_map),
835 Self::Format2(t) => t.remap_variation_indices(key_map),
836 }
837 }
838}
839
840#[cfg(test)]
841mod tests {
842
843 use read_fonts::tables::{gpos as read_gpos, layout::LookupFlag};
844
845 use crate::tables::layout::VariationIndex;
846
847 use super::*;
848
849 #[test]
850 fn remap_variation_indices_through_extension() {
851 use crate::tables::variations::{
852 ivs_builder::VariationStoreBuilder, RegionAxisCoordinates, VariationRegion,
853 };
854 use font_types::F2Dot14;
855
856 let region = VariationRegion::new(vec![RegionAxisCoordinates {
857 start_coord: F2Dot14::from_f32(0.0),
858 peak_coord: F2Dot14::from_f32(1.0),
859 end_coord: F2Dot14::from_f32(1.0),
860 }]);
861 let mut var_store = VariationStoreBuilder::new(1);
862 let temp_id = var_store.add_deltas(vec![(region, -20)]);
863 let (_, key_map) = var_store.build();
864
865 let value_record = ValueRecord::new()
866 .with_x_advance(-20)
867 .with_x_advance_device(DeviceOrVariationIndex::pending_variation_index(temp_id));
868 let pair_set = PairSet::new(vec![PairValueRecord::new(
869 GlyphId16::new(2),
870 value_record,
871 ValueRecord::default(),
872 )]);
873 let pair_pos = PairPos::format_1(
874 CoverageTable::format_1(vec![GlyphId16::new(1)]),
875 vec![pair_set],
876 );
877
878 let mut plain =
880 PositionLookup::Pair(Lookup::new(LookupFlag::default(), vec![pair_pos.clone()]));
881 let mut extension = PositionLookup::Extension(Lookup::new(
882 LookupFlag::default(),
883 vec![ExtensionSubtable::Pair(ExtensionPosFormat1::new(
884 <PairPos as LookupSubtable>::TYPE.to_raw(),
885 pair_pos,
886 ))],
887 ));
888
889 plain.remap_variation_indices(&key_map);
890 extension.remap_variation_indices(&key_map);
891
892 let plain_bytes = crate::dump_table(&plain).unwrap();
894 let ext_bytes = crate::dump_table(&extension).unwrap();
895
896 let parsed = read_gpos::PositionLookup::read(FontData::new(&ext_bytes)).unwrap();
897 let read_gpos::PositionSubtables::Pair(subtables) = parsed.subtables().unwrap() else {
898 panic!("expected a pair lookup");
899 };
900 let read_gpos::PairPos::Format1(subtable) = subtables.get(0).unwrap() else {
901 panic!("expected format 1");
902 };
903 let pair_set = subtable.pair_sets().get(0).unwrap();
904 let record = &pair_set.pair_value_records().get(0).unwrap();
905 let read_fonts::tables::layout::DeviceOrVariationIndex::VariationIndex(var_idx) =
906 record.value_record1.x_advance_device().unwrap().unwrap()
907 else {
908 panic!("expected a VariationIndex");
909 };
910 let expected = key_map.get(temp_id).unwrap();
911 assert_eq!(
912 (
913 var_idx.delta_set_outer_index(),
914 var_idx.delta_set_inner_index()
915 ),
916 (
917 expected.delta_set_outer_index,
918 expected.delta_set_inner_index
919 )
920 );
921 assert_eq!(plain_bytes.len() + 8, ext_bytes.len());
923 }
924
925 #[test]
927 fn gpos_1_zero() {
928 let cov_one = CoverageTable::format_1(vec![GlyphId16::new(2)]);
929 let cov_two = CoverageTable::format_1(vec![GlyphId16::new(4)]);
930 let sub1 = SinglePos::format_1(cov_one, ValueRecord::default());
931 let sub2 = SinglePos::format_1(cov_two, ValueRecord::default().with_x_advance(500));
932 let lookup = Lookup::new(LookupFlag::default(), vec![sub1, sub2]);
933 let bytes = crate::dump_table(&lookup).unwrap();
934
935 let parsed = read_gpos::PositionLookup::read(FontData::new(&bytes)).unwrap();
936 let read_gpos::PositionLookup::Single(table) = parsed else {
937 panic!("something has gone seriously wrong");
938 };
939
940 assert_eq!(table.lookup_flag(), LookupFlag::empty());
941 assert_eq!(table.sub_table_count(), 2);
942 let read_gpos::SinglePos::Format1(sub1) = table.subtables().get(0).unwrap() else {
943 panic!("wrong table type");
944 };
945 let read_gpos::SinglePos::Format1(sub2) = table.subtables().get(1).unwrap() else {
946 panic!("wrong table type");
947 };
948
949 assert_eq!(sub1.value_format(), ValueFormat::empty());
950 assert!(sub1.value_record().is_empty());
951
952 assert_eq!(sub2.value_format(), ValueFormat::X_ADVANCE);
953 assert_eq!(sub2.value_record().x_advance(), Some(500));
954 assert_eq!(sub2.value_record().y_advance(), None);
955 }
956
957 fn make_rec(i: u16) -> ValueRecord {
959 if i == 0 {
961 return ValueRecord::new().with_explicit_value_format(ValueFormat::X_ADVANCE_DEVICE);
962 }
963 ValueRecord::new().with_x_advance_device(VariationIndex::new(0xff, i))
964 }
965
966 #[test]
967 fn compile_devices_pairpos2() {
968 let class1 = ClassDef::from_iter([(GlyphId16::new(5), 0), (GlyphId16::new(6), 1)]);
969 let class2 = ClassDef::from_iter([(GlyphId16::new(8), 1)]);
971
972 let class1recs = vec![
974 Class1Record::new(vec![
975 Class2Record::new(make_rec(0), make_rec(0)),
976 Class2Record::new(make_rec(1), make_rec(2)),
977 ]),
978 Class1Record::new(vec![
979 Class2Record::new(make_rec(0), make_rec(0)),
980 Class2Record::new(make_rec(2), make_rec(3)),
981 ]),
982 ];
983 let coverage = class1.iter().map(|(gid, _)| gid).collect();
984 let a_table = PairPos::format_2(coverage, class1, class2, class1recs);
985
986 let bytes = crate::dump_table(&a_table).unwrap();
987 let read_back = PairPosFormat2::read(bytes.as_slice().into()).unwrap();
988
989 assert!(read_back.class1_records[0].class2_records[0]
990 .value_record1
991 .x_advance_device
992 .is_none());
993 assert!(read_back.class1_records[1].class2_records[1]
994 .value_record1
995 .x_advance_device
996 .is_some());
997
998 let DeviceOrVariationIndex::VariationIndex(dev2) = read_back.class1_records[0]
999 .class2_records[1]
1000 .value_record2
1001 .x_advance_device
1002 .as_ref()
1003 .unwrap()
1004 else {
1005 panic!("not a variation index")
1006 };
1007 assert_eq!(dev2.delta_set_inner_index, 2);
1008 }
1009
1010 #[should_panic(expected = "all value records should report the same format")]
1011 #[test]
1012 fn validate_bad_pairpos2() {
1013 let class1 = ClassDef::from_iter([(GlyphId16::new(5), 0), (GlyphId16::new(6), 1)]);
1014 let class2 = ClassDef::from_iter([(GlyphId16::new(8), 1)]);
1016 let coverage = class1.iter().map(|(gid, _)| gid).collect();
1017
1018 let class1recs = vec![
1020 Class1Record::new(vec![
1021 Class2Record::new(make_rec(0), make_rec(0)),
1022 Class2Record::new(make_rec(1), make_rec(2)),
1023 ]),
1024 Class1Record::new(vec![
1025 Class2Record::new(make_rec(0), make_rec(0)),
1026 Class2Record::new(make_rec(2), make_rec(3).with_x_advance(0x514)),
1028 ]),
1029 ];
1030 let ppf2 = PairPos::format_2(coverage, class1, class2, class1recs);
1031 crate::dump_table(&ppf2).unwrap();
1032 }
1033
1034 #[test]
1035 fn validate_pairpos1() {
1036 let coverage: CoverageTable = [1, 2].into_iter().map(GlyphId16::new).collect();
1037 let good_table = PairPosFormat1::new(
1038 coverage.clone(),
1039 vec![
1040 PairSet::new(vec![PairValueRecord::new(
1041 GlyphId16::new(5),
1042 ValueRecord::new().with_x_advance(5),
1043 ValueRecord::new(),
1044 )]),
1045 PairSet::new(vec![PairValueRecord::new(
1046 GlyphId16::new(1),
1047 ValueRecord::new().with_x_advance(42),
1048 ValueRecord::new(),
1049 )]),
1050 ],
1051 );
1052
1053 let bad_table = PairPosFormat1::new(
1054 coverage,
1055 vec![
1056 PairSet::new(vec![PairValueRecord::new(
1057 GlyphId16::new(5),
1058 ValueRecord::new().with_x_advance(5),
1059 ValueRecord::new(),
1060 )]),
1061 PairSet::new(vec![PairValueRecord::new(
1062 GlyphId16::new(1),
1063 ValueRecord::new().with_x_placement(42),
1065 ValueRecord::new(),
1066 )]),
1067 ],
1068 );
1069
1070 assert!(crate::dump_table(&good_table).is_ok());
1071 assert!(matches!(
1072 crate::dump_table(&bad_table),
1073 Err(crate::error::Error::ValidationFailed(_))
1074 ));
1075 }
1076}