1use itertools::Itertools;
9use tinyvec::tiny_vec;
10use unicode_general_category::GeneralCategory;
11
12use crate::context::{ContextLookupHelper, Glyph, LookupFlag, MatchType};
13use crate::error::ParseError;
14use crate::gdef::gdef_is_mark;
15use crate::gsub::{FeatureInfo, Features, RawGlyph};
16use crate::layout::{
17 chain_context_lookup_info, context_lookup_info, Adjust, Anchor, ChainContextLookup,
18 ContextLookup, CursivePos, GDEFTable, LangSys, LayoutCache, LayoutTable, LookupList,
19 MarkBasePos, MarkLigPos, PairPos, PosLookup, SinglePos, ValueRecord, VariationIndex, GPOS,
20};
21use crate::scripts;
22use crate::scripts::ScriptType;
23use crate::tables::variable_fonts::fvar::Tuple;
24use crate::tables::variable_fonts::owned;
25use crate::tag;
26
27type PosContext<'a> = ContextLookupHelper<'a, GPOS>;
28
29pub fn apply(
31 gpos_cache: &LayoutCache<GPOS>,
32 opt_gdef_table: Option<&GDEFTable>,
33 kerning: bool,
34 features: &Features,
35 tuple: Option<Tuple<'_>>,
36 script_tag: u32,
37 opt_lang_tag: Option<u32>,
38 infos: &mut [Info],
39) -> Result<(), ParseError> {
40 let gpos_table = &gpos_cache.layout_table;
41 let script_type = ScriptType::from(script_tag);
42
43 let script = match script_type {
44 ScriptType::Indic => {
45 let indic2_tag = scripts::indic::indic2_tag(script_tag);
46 match gpos_table.find_script(indic2_tag)? {
47 Some(script) => script,
48 None => match gpos_table.find_script_or_default(script_tag)? {
49 Some(script) => script,
50 None => return Ok(()),
51 },
52 }
53 }
54 _ => match gpos_table.find_script_or_default(script_tag)? {
55 Some(script) => script,
56 None => return Ok(()),
57 },
58 };
59
60 let langsys = match script.find_langsys_or_default(opt_lang_tag)? {
61 Some(langsys) => langsys,
62 None => return Ok(()),
63 };
64
65 let base_features: &[u32] = match script_type {
66 ScriptType::Arabic => &[tag::CURS, tag::KERN, tag::MARK, tag::MKMK],
67 ScriptType::Indic => &[
68 tag::ABVM,
69 tag::BLWM,
70 tag::DIST,
71 tag::KERN,
72 tag::MARK,
73 tag::MKMK,
74 ],
75 ScriptType::Khmer => &[tag::ABVM, tag::BLWM, tag::DIST, tag::MARK, tag::MKMK],
76 ScriptType::Syriac => &[tag::CURS, tag::KERN, tag::MARK, tag::MKMK],
77 ScriptType::ThaiLao => &[tag::KERN, tag::MARK, tag::MKMK],
78 ScriptType::Default if kerning => &[tag::DIST, tag::KERN, tag::MARK, tag::MKMK],
79 ScriptType::Default => &[tag::DIST, tag::MARK, tag::MKMK],
80 };
81
82 apply_features(
83 gpos_cache,
84 gpos_table,
85 opt_gdef_table,
86 langsys,
87 base_features.iter().map(|&feature_tag| FeatureInfo {
88 feature_tag,
89 alternate: None,
90 }),
91 tuple,
92 infos,
93 )?;
94 match features {
95 Features::Custom(custom) => apply_features(
96 gpos_cache,
97 gpos_table,
98 opt_gdef_table,
99 langsys,
100 custom.iter().copied(),
101 tuple,
102 infos,
103 ),
104 Features::Mask(mask) => apply_features(
105 gpos_cache,
106 gpos_table,
107 opt_gdef_table,
108 langsys,
109 mask.iter(),
110 tuple,
111 infos,
112 ),
113 }
114}
115
116pub fn apply_features(
121 gpos_cache: &LayoutCache<GPOS>,
122 gpos_table: &LayoutTable<GPOS>,
123 opt_gdef_table: Option<&GDEFTable>,
124 langsys: &LangSys,
125 features: impl Iterator<Item = FeatureInfo>,
126 tuple: Option<Tuple<'_>>,
127 infos: &mut [Info],
128) -> Result<(), ParseError> {
129 let mut lookup_indices = tiny_vec!([u16; 128]);
130 let feature_variations = gpos_table.feature_variations(tuple)?;
131 for feature in features {
132 let feature_table = gpos_table.find_langsys_feature(
133 langsys,
134 feature.feature_tag,
135 feature_variations.as_ref(),
136 )?;
137
138 if let Some(feature_table) = feature_table {
139 lookup_indices.clear();
141 lookup_indices.extend_from_slice(&feature_table.lookup_indices);
142 lookup_indices.sort_unstable();
143 for lookup_index in lookup_indices.iter().copied().dedup().map(usize::from) {
144 gpos_apply_lookup(
145 gpos_cache,
146 gpos_table,
147 opt_gdef_table,
148 lookup_index,
149 tuple,
150 infos,
151 )?;
152 }
153 }
154 }
155 Ok(())
156}
157
158pub fn apply_fallback(infos: &mut [Info]) {
162 for info in infos.iter_mut() {
163 if !info.is_mark && unicodes_are_marks(&info.glyph.unicodes) {
164 info.is_mark = true;
165 }
166 }
167 let mut base_index = 0;
168 for (i, info) in infos.iter_mut().enumerate().skip(1) {
169 if info.is_mark {
170 info.placement = Placement::MarkOverprint(base_index);
171 } else {
172 base_index = i;
173 }
174 }
175}
176
177fn unicodes_are_marks(unicodes: &[char]) -> bool {
178 unicodes
179 .iter()
180 .copied()
181 .map(unicode_general_category::get_general_category)
182 .all(|cat| cat == GeneralCategory::NonspacingMark)
183}
184
185fn gpos_apply_lookup(
186 gpos_cache: &LayoutCache<GPOS>,
187 gpos_table: &LayoutTable<GPOS>,
188 opt_gdef_table: Option<&GDEFTable>,
189 lookup_index: usize,
190 tuple: Option<Tuple<'_>>,
191 infos: &mut [Info],
192) -> Result<(), ParseError> {
193 if let Some(ref lookup_list) = gpos_table.opt_lookup_list {
194 let lookup = lookup_list.lookup_cache_gpos(gpos_cache, lookup_index)?;
195 let match_type = MatchType::from_lookup_flag(lookup.lookup_flag);
196 match lookup.lookup_subtables {
197 PosLookup::SinglePos(ref subtables) => {
198 forall_glyphs_match(match_type, opt_gdef_table, infos, |i, infos| {
199 singlepos(subtables, tuple, opt_gdef_table, &mut infos[i])
200 })
201 }
202 PosLookup::PairPos(ref subtables) => {
203 forall_glyph_pairs_match(match_type, opt_gdef_table, infos, |i1, i2, infos| {
207 pairpos(subtables, tuple, opt_gdef_table, i1, i2, infos)
208 })
209 }
210 PosLookup::CursivePos(ref subtables) => forall_glyph_pairs_match(
211 MatchType::ignore_marks(),
212 opt_gdef_table,
213 infos,
214 |i1, i2, infos| cursivepos(subtables, i1, i2, lookup.lookup_flag, infos),
215 ),
216 PosLookup::MarkBasePos(ref subtables) => {
217 forall_base_mark_glyph_pairs(infos, |i1, i2, infos| {
218 markbasepos(subtables, i1, i2, infos)
219 })
220 }
221 PosLookup::MarkLigPos(ref subtables) => {
222 forall_base_mark_glyph_pairs(infos, |i1, i2, infos| {
223 markligpos(subtables, i1, i2, infos)
224 })
225 }
226 PosLookup::MarkMarkPos(ref subtables) => {
227 forall_mark_mark_glyph_pairs(infos, |i1, i2, infos| {
228 markmarkpos(subtables, i1, i2, infos)
229 })
230 }
231 PosLookup::ContextPos(ref subtables) => {
232 forall_glyphs_match(match_type, opt_gdef_table, infos, |i, infos| {
233 contextpos(
234 gpos_cache,
235 lookup_list,
236 opt_gdef_table,
237 tuple,
238 match_type,
239 subtables,
240 i,
241 infos,
242 )
243 })
244 }
245 PosLookup::ChainContextPos(ref subtables) => {
246 forall_glyphs_match(match_type, opt_gdef_table, infos, |i, infos| {
247 chaincontextpos(
248 gpos_cache,
249 lookup_list,
250 opt_gdef_table,
251 tuple,
252 match_type,
253 subtables,
254 i,
255 infos,
256 )
257 })
258 }
259 }
260 } else {
261 Ok(())
262 }
263}
264
265fn gpos_lookup_singlepos(
266 subtables: &[SinglePos],
267 glyph_index: u16,
268) -> Result<ValueRecord, ParseError> {
269 for singlepos in subtables {
270 if let Some(val) = singlepos.apply(glyph_index)? {
271 return Ok(Some(val));
272 }
273 }
274 Ok(None)
275}
276
277fn gpos_lookup_pairpos(
278 subtables: &[PairPos],
279 glyph_index1: u16,
280 glyph_index2: u16,
281) -> Result<Option<(ValueRecord, ValueRecord)>, ParseError> {
282 for pairpos in subtables {
283 if let Some((val1, val2)) = pairpos.apply(glyph_index1, glyph_index2)? {
284 return Ok(Some((val1, val2)));
285 }
286 }
287 Ok(None)
288}
289
290fn gpos_lookup_cursivepos(
291 subtables: &[CursivePos],
292 glyph_index1: u16,
293 glyph_index2: u16,
294) -> Result<Option<(Anchor, Anchor)>, ParseError> {
295 for cursivepos in subtables {
296 if let Some((an1, an2)) = cursivepos.apply(glyph_index1, glyph_index2)? {
297 return Ok(Some((an1, an2)));
298 }
299 }
300 Ok(None)
301}
302
303fn gpos_lookup_markbasepos(
304 subtables: &[MarkBasePos],
305 glyph_index1: u16,
306 glyph_index2: u16,
307) -> Result<Option<(Anchor, Anchor)>, ParseError> {
308 for markbasepos in subtables {
309 if let Some((an1, an2)) = markbasepos.apply(glyph_index1, glyph_index2)? {
310 return Ok(Some((an1, an2)));
311 }
312 }
313 Ok(None)
314}
315
316fn gpos_lookup_markligpos(
317 subtables: &[MarkLigPos],
318 glyph_index1: u16,
319 glyph_index2: u16,
320 liga_component_index: u16,
321) -> Result<Option<(Anchor, Anchor)>, ParseError> {
322 for markligpos in subtables {
323 if let Some((an1, an2)) = markligpos.apply(
324 glyph_index1,
325 glyph_index2,
326 usize::from(liga_component_index),
327 )? {
328 return Ok(Some((an1, an2)));
329 }
330 }
331 Ok(None)
332}
333
334fn gpos_lookup_markmarkpos(
335 subtables: &[MarkBasePos],
336 glyph_index1: u16,
337 glyph_index2: u16,
338) -> Result<Option<(Anchor, Anchor)>, ParseError> {
339 for markmarkpos in subtables {
340 if let Some((an1, an2)) = markmarkpos.apply(glyph_index1, glyph_index2)? {
341 return Ok(Some((an1, an2)));
342 }
343 }
344 Ok(None)
345}
346
347fn gpos_lookup_contextpos<'a>(
348 opt_gdef_table: Option<&GDEFTable>,
349 match_type: MatchType,
350 subtables: &'a [ContextLookup<GPOS>],
351 glyph_index: u16,
352 i: usize,
353 infos: &mut [Info],
354) -> Result<Option<Box<PosContext<'a>>>, ParseError> {
355 for context_lookup in subtables {
356 if let Some(context) = context_lookup_info(context_lookup, glyph_index, |context| {
357 context.matches(opt_gdef_table, match_type, infos, i)
358 })? {
359 return Ok(Some(context));
360 }
361 }
362 Ok(None)
363}
364
365fn gpos_lookup_chaincontextpos<'a>(
366 opt_gdef_table: Option<&GDEFTable>,
367 match_type: MatchType,
368 subtables: &'a [ChainContextLookup<GPOS>],
369 glyph_index: u16,
370 i: usize,
371 infos: &mut [Info],
372) -> Result<Option<Box<PosContext<'a>>>, ParseError> {
373 for chain_context_lookup in subtables {
374 if let Some(context) =
375 chain_context_lookup_info(chain_context_lookup, glyph_index, |context| {
376 context.matches(opt_gdef_table, match_type, infos, i)
377 })?
378 {
379 return Ok(Some(context));
380 }
381 }
382 Ok(None)
383}
384
385#[derive(Copy, Clone, Eq, PartialEq, Debug)]
388pub enum Placement {
389 None,
390 Distance(i32, i32),
395 MarkAnchor(usize, Anchor, Anchor),
402 MarkOverprint(usize),
409 CursiveAnchor(usize, bool, Anchor, Anchor),
419}
420
421impl Placement {
422 fn combine_distance(&mut self, x2: i32, y2: i32) {
423 use Placement::*;
424
425 *self = match *self {
426 None | MarkOverprint(_) => Distance(x2, y2),
427 CursiveAnchor(..) => Distance(x2, y2),
430 Distance(x1, y1) => Distance(x1 + x2, y1 + y2),
431 MarkAnchor(i, an1, an2) => {
432 let x = an1.x + (x2 as i16);
433 let y = an1.y + (y2 as i16);
434 MarkAnchor(i, Anchor { x, y }, an2)
435 }
436 }
437 }
438}
439
440#[derive(Clone, Debug)]
448pub struct Info {
449 pub glyph: RawGlyph<()>,
451 pub kerning: i16,
453 pub placement: Placement,
456 is_mark: bool,
457}
458
459impl Glyph for Info {
460 fn get_glyph_index(&self) -> u16 {
461 self.glyph.glyph_index
462 }
463}
464
465impl Info {
466 pub fn init_from_glyphs(
467 opt_gdef_table: Option<&GDEFTable>,
468 glyphs: Vec<RawGlyph<()>>,
469 ) -> Vec<Info> {
470 let mut infos = Vec::with_capacity(glyphs.len());
471 for glyph in glyphs {
472 let is_mark = gdef_is_mark(opt_gdef_table, glyph.glyph_index);
473 let info = Info {
474 glyph,
475 kerning: 0,
476 placement: Placement::None,
477 is_mark,
478 };
479 infos.push(info);
480 }
481 infos
482 }
483}
484
485impl Adjust {
486 fn apply(&self, tuple: Option<Tuple<'_>>, opt_gdef_table: Option<&GDEFTable>, info: &mut Info) {
487 let variation_store =
488 opt_gdef_table.and_then(|gdef| gdef.opt_item_variation_store.as_ref());
489 if self.x_placement == 0 && self.y_placement == 0 {
490 if self.x_advance != 0 && self.y_advance == 0 {
491 info.kerning +=
492 self.x_advance + self.x_advance_delta(tuple, variation_store).round() as i16;
493 } else if self.y_advance != 0 {
494 } else {
496 let x_advance_delta = self.x_advance_delta(tuple, variation_store).round() as i16;
498 info.kerning += x_advance_delta;
499 }
500 } else if self.y_advance == 0 {
501 let x_placement = i32::from(self.x_placement)
502 + self.x_placement_delta(tuple, variation_store).round() as i32;
503 let y_placement = i32::from(self.y_placement)
504 + self.y_placement_delta(tuple, variation_store).round() as i32;
505 info.placement.combine_distance(x_placement, y_placement);
506
507 let x_advance =
508 self.x_advance + self.x_advance_delta(tuple, variation_store).round() as i16;
509 info.kerning += x_advance;
510 } else {
511 }
513 }
514
515 pub fn x_advance_delta(
516 &self,
517 tuple: Option<Tuple<'_>>,
518 variation_store: Option<&owned::ItemVariationStore>,
519 ) -> f32 {
520 Self::delta(self.x_advance_variation.as_ref(), tuple, variation_store)
521 }
522
523 pub fn y_advance_delta(
524 &self,
525 tuple: Option<Tuple<'_>>,
526 variation_store: Option<&owned::ItemVariationStore>,
527 ) -> f32 {
528 Self::delta(self.y_advance_variation.as_ref(), tuple, variation_store)
529 }
530
531 pub fn x_placement_delta(
532 &self,
533 tuple: Option<Tuple<'_>>,
534 variation_store: Option<&owned::ItemVariationStore>,
535 ) -> f32 {
536 Self::delta(self.x_placement_variation.as_ref(), tuple, variation_store)
537 }
538
539 pub fn y_placement_delta(
540 &self,
541 tuple: Option<Tuple<'_>>,
542 variation_store: Option<&owned::ItemVariationStore>,
543 ) -> f32 {
544 Self::delta(self.y_placement_variation.as_ref(), tuple, variation_store)
545 }
546
547 fn delta(
548 variation: Option<&VariationIndex>,
549 tuple: Option<Tuple<'_>>,
550 variation_store: Option<&owned::ItemVariationStore>,
551 ) -> f32 {
552 match (tuple, variation_store, variation) {
553 (Some(tuple), Some(store), Some(placement_variation)) => {
554 store.adjustment(*placement_variation, tuple).unwrap_or(0.0)
555 }
556 _ => 0.0,
557 }
558 }
559}
560
561fn forall_glyphs_match(
562 match_type: MatchType,
563 opt_gdef_table: Option<&GDEFTable>,
564 infos: &mut [Info],
565 f: impl Fn(usize, &mut [Info]) -> Result<(), ParseError>,
566) -> Result<(), ParseError> {
567 for i in 0..infos.len() {
568 if match_type.match_glyph(opt_gdef_table, &infos[i]) {
569 f(i, infos)?;
570 }
571 }
572 Ok(())
573}
574
575fn forall_glyph_pairs_match(
576 match_type: MatchType,
577 opt_gdef_table: Option<&GDEFTable>,
578 infos: &mut [Info],
579 f: impl Fn(usize, usize, &mut [Info]) -> Result<(), ParseError>,
580) -> Result<(), ParseError> {
581 if let Some(mut i1) = match_type.find_first(opt_gdef_table, infos) {
582 while let Some(i2) = match_type.find_next(opt_gdef_table, infos, i1) {
583 f(i1, i2, infos)?;
584 i1 = i2;
585 }
586 }
587 Ok(())
588}
589
590fn forall_base_mark_glyph_pairs(
591 infos: &mut [Info],
592 f: impl Fn(usize, usize, &mut [Info]) -> Result<(), ParseError>,
593) -> Result<(), ParseError> {
594 let mut i = 0;
595 'outer: while i + 1 < infos.len() {
596 if !infos[i].is_mark {
597 for j in i + 1..infos.len() {
598 f(i, j, infos)?;
599 if !infos[j].is_mark {
600 i = j;
601 continue 'outer;
602 }
603 }
604 }
605 i += 1;
606 }
607 Ok(())
608}
609
610fn forall_mark_mark_glyph_pairs(
611 infos: &mut [Info],
612 f: impl Fn(usize, usize, &mut [Info]) -> Result<(), ParseError>,
613) -> Result<(), ParseError> {
614 let mut start = 0;
615 'outer: loop {
616 let mut i = start;
617 while i + 1 < infos.len() {
618 if infos[i].is_mark {
619 for j in i + 1..infos.len() {
621 if !infos[j].is_mark {
622 start = i + 1;
623 continue 'outer;
624 }
625
626 if infos[i].glyph.liga_component_pos == infos[j].glyph.liga_component_pos {
628 f(i, j, infos)?;
629 } else if infos[i].glyph.ligature() || infos[j].glyph.ligature() {
630 f(i, j, infos)?;
631 }
632 }
633 }
634 i += 1;
635 }
636 break;
637 }
638 Ok(())
639}
640
641fn singlepos(
642 subtables: &[SinglePos],
643 tuple: Option<Tuple<'_>>,
644 opt_gdef_table: Option<&GDEFTable>,
645 i: &mut Info,
646) -> Result<(), ParseError> {
647 let glyph_index = i.glyph.glyph_index;
648 if let Some(adj) = gpos_lookup_singlepos(subtables, glyph_index)? {
649 adj.apply(tuple, opt_gdef_table, i);
650 }
651 Ok(())
652}
653
654fn pairpos(
655 subtables: &[PairPos],
656 tuple: Option<Tuple<'_>>,
657 opt_gdef_table: Option<&GDEFTable>,
658 i1: usize,
659 i2: usize,
660 infos: &mut [Info],
661) -> Result<(), ParseError> {
662 match gpos_lookup_pairpos(
663 subtables,
664 infos[i1].glyph.glyph_index,
665 infos[i2].glyph.glyph_index,
666 )? {
667 Some((opt_adj1, opt_adj2)) => {
668 if let Some(adj1) = opt_adj1 {
669 adj1.apply(tuple, opt_gdef_table, &mut infos[i1]);
670 }
671 if let Some(adj2) = opt_adj2 {
672 adj2.apply(tuple, opt_gdef_table, &mut infos[i2]);
673 }
674 Ok(())
675 }
676 None => Ok(()),
677 }
678}
679
680fn cursivepos(
681 subtables: &[CursivePos],
682 i1: usize,
683 i2: usize,
684 lookup_flag: LookupFlag,
685 infos: &mut [Info],
686) -> Result<(), ParseError> {
687 match gpos_lookup_cursivepos(
688 subtables,
689 infos[i1].glyph.glyph_index,
690 infos[i2].glyph.glyph_index,
691 )? {
692 Some((anchor1, anchor2)) => {
693 infos[i1].placement =
694 Placement::CursiveAnchor(i2, lookup_flag.get_rtl(), anchor2, anchor1);
695 Ok(())
696 }
697 None => Ok(()),
698 }
699}
700
701fn markbasepos(
702 subtables: &[MarkBasePos],
703 i1: usize,
704 i2: usize,
705 infos: &mut [Info],
706) -> Result<(), ParseError> {
707 match gpos_lookup_markbasepos(
708 subtables,
709 infos[i1].glyph.glyph_index,
710 infos[i2].glyph.glyph_index,
711 )? {
712 Some((anchor1, anchor2)) => {
713 infos[i2].placement = Placement::MarkAnchor(i1, anchor1, anchor2);
714 infos[i2].is_mark = true;
715 Ok(())
716 }
717 None => Ok(()),
718 }
719}
720
721fn markligpos(
722 subtables: &[MarkLigPos],
723 i1: usize,
724 i2: usize,
725 infos: &mut [Info],
726) -> Result<(), ParseError> {
727 match gpos_lookup_markligpos(
728 subtables,
729 infos[i1].glyph.glyph_index,
730 infos[i2].glyph.glyph_index,
731 infos[i2].glyph.liga_component_pos,
732 )? {
733 Some((anchor1, anchor2)) => {
734 infos[i2].placement = Placement::MarkAnchor(i1, anchor1, anchor2);
735 infos[i2].is_mark = true;
736 Ok(())
737 }
738 None => Ok(()),
739 }
740}
741
742fn markmarkpos(
743 subtables: &[MarkBasePos],
744 i1: usize,
745 i2: usize,
746 infos: &mut [Info],
747) -> Result<(), ParseError> {
748 match gpos_lookup_markmarkpos(
749 subtables,
750 infos[i1].glyph.glyph_index,
751 infos[i2].glyph.glyph_index,
752 )? {
753 Some((anchor1, anchor2)) => {
754 infos[i2].placement = Placement::MarkAnchor(i1, anchor1, anchor2);
755 infos[i2].is_mark = true;
756 Ok(())
757 }
758 None => Ok(()),
759 }
760}
761
762fn contextpos(
763 gpos_cache: &LayoutCache<GPOS>,
764 lookup_list: &LookupList<GPOS>,
765 opt_gdef_table: Option<&GDEFTable>,
766 tuple: Option<Tuple<'_>>,
767 match_type: MatchType,
768 subtables: &[ContextLookup<GPOS>],
769 i: usize,
770 infos: &mut [Info],
771) -> Result<(), ParseError> {
772 let glyph_index = infos[i].glyph.glyph_index;
773 match gpos_lookup_contextpos(opt_gdef_table, match_type, subtables, glyph_index, i, infos)? {
774 Some(pos) => apply_pos_context(
775 gpos_cache,
776 lookup_list,
777 opt_gdef_table,
778 tuple,
779 match_type,
780 &pos,
781 i,
782 infos,
783 ),
784 None => Ok(()),
785 }
786}
787
788fn chaincontextpos(
789 gpos_cache: &LayoutCache<GPOS>,
790 lookup_list: &LookupList<GPOS>,
791 opt_gdef_table: Option<&GDEFTable>,
792 tuple: Option<Tuple<'_>>,
793 match_type: MatchType,
794 subtables: &[ChainContextLookup<GPOS>],
795 i: usize,
796 infos: &mut [Info],
797) -> Result<(), ParseError> {
798 let glyph_index = infos[i].glyph.glyph_index;
799 match gpos_lookup_chaincontextpos(opt_gdef_table, match_type, subtables, glyph_index, i, infos)?
800 {
801 Some(pos) => apply_pos_context(
802 gpos_cache,
803 lookup_list,
804 opt_gdef_table,
805 tuple,
806 match_type,
807 &pos,
808 i,
809 infos,
810 ),
811 None => Ok(()),
812 }
813}
814
815fn apply_pos_context(
816 gpos_cache: &LayoutCache<GPOS>,
817 lookup_list: &LookupList<GPOS>,
818 opt_gdef_table: Option<&GDEFTable>,
819 tuple: Option<Tuple<'_>>,
820 _match_type: MatchType,
821 pos: &PosContext<'_>,
822 i: usize,
823 infos: &mut [Info],
824) -> Result<(), ParseError> {
825 for (pos_index, pos_lookup_index) in pos.lookup_array {
826 apply_pos(
827 gpos_cache,
828 lookup_list,
829 opt_gdef_table,
830 tuple,
831 usize::from(*pos_index),
832 usize::from(*pos_lookup_index),
833 infos,
834 i,
835 )?;
836 }
837 Ok(())
838}
839
840fn apply_pos(
841 gpos_cache: &LayoutCache<GPOS>,
842 lookup_list: &LookupList<GPOS>,
843 opt_gdef_table: Option<&GDEFTable>,
844 tuple: Option<Tuple<'_>>,
845 pos_index: usize,
846 lookup_index: usize,
847 infos: &mut [Info],
848 index: usize,
849) -> Result<(), ParseError> {
850 let lookup = lookup_list.lookup_cache_gpos(gpos_cache, lookup_index)?;
851 let match_type = MatchType::from_lookup_flag(lookup.lookup_flag);
852 let i1 = match match_type.find_nth(opt_gdef_table, infos, index, pos_index) {
853 Some(index1) => index1,
854 None => return Ok(()),
855 };
856 match lookup.lookup_subtables {
857 PosLookup::SinglePos(ref subtables) => {
858 singlepos(subtables, tuple, opt_gdef_table, &mut infos[i1])
859 }
860 PosLookup::PairPos(ref subtables) => {
861 if let Some(i2) = match_type.find_next(opt_gdef_table, infos, i1) {
862 pairpos(subtables, tuple, opt_gdef_table, i1, i2, infos)
863 } else {
864 Ok(())
865 }
866 }
867 PosLookup::CursivePos(ref subtables) => {
868 if let Some(i2) = match_type.find_next(opt_gdef_table, infos, i1) {
869 cursivepos(subtables, i1, i2, lookup.lookup_flag, infos)
870 } else {
871 Ok(())
872 }
873 }
874 PosLookup::MarkBasePos(ref subtables) => {
875 if let Some(base_index) = MatchType::ignore_marks().find_prev(opt_gdef_table, infos, i1)
877 {
878 markbasepos(subtables, base_index, i1, infos)
879 } else {
880 Ok(())
881 }
882 }
883 PosLookup::MarkLigPos(ref subtables) => {
884 if let Some(base_index) = MatchType::ignore_marks().find_prev(opt_gdef_table, infos, i1)
886 {
887 markligpos(subtables, base_index, i1, infos)
888 } else {
889 Ok(())
890 }
891 }
892 PosLookup::MarkMarkPos(ref subtables) => {
893 if let Some(base_index) = match_type.find_prev(opt_gdef_table, infos, i1) {
895 markmarkpos(subtables, base_index, i1, infos)
896 } else {
897 Ok(())
898 }
899 }
900 PosLookup::ContextPos(ref _subtables) => Ok(()),
901 PosLookup::ChainContextPos(ref _subtables) => Ok(()),
902 }
903}