1use types::{BigEndian, GlyphId16};
4
5use super::{
6 ArrayOfOffsets, ChainedClassSequenceRule, ChainedClassSequenceRuleSet, ChainedSequenceContext,
7 ChainedSequenceContextFormat1, ChainedSequenceContextFormat2, ChainedSequenceContextFormat3,
8 ChainedSequenceRule, ChainedSequenceRuleSet, ClassDef, ClassDefFormat1, ClassDefFormat2,
9 ClassSequenceRule, ClassSequenceRuleSet, CoverageTable, ExtensionLookup, Feature, FeatureList,
10 FeatureVariations, FontRead, GlyphId, LangSys, ReadError, Script, ScriptList, SequenceContext,
11 SequenceContextFormat1, SequenceContextFormat2, SequenceContextFormat3, SequenceLookupRecord,
12 SequenceRule, SequenceRuleSet, Subtables, Tag,
13};
14use crate::{
15 collections::IntSet,
16 tables::{gpos::PositionLookupList, gsub::SubstitutionLookupList},
17};
18
19const MAX_SCRIPTS: u16 = 500;
20const MAX_LANGSYS: u16 = 2000;
21const MAX_FEATURE_INDICES: u16 = 1500;
22pub(crate) const MAX_NESTING_LEVEL: u8 = 64;
23pub(crate) const MAX_LOOKUP_VISIT_COUNT: u16 = 35000;
24
25struct CollectFeaturesContext<'a> {
26 script_count: u16,
27 langsys_count: u16,
28 feature_index_count: u16,
29 visited_script: IntSet<u32>,
30 visited_langsys: IntSet<u32>,
31 feature_indices: &'a mut IntSet<u16>,
32 feature_indices_filter: IntSet<u16>,
33 table_head: usize,
34}
35
36impl<'a> CollectFeaturesContext<'a> {
37 pub(crate) fn new(
38 features: &IntSet<Tag>,
39 table_head: usize,
40 feature_list: &'a FeatureList<'a>,
41 feature_indices: &'a mut IntSet<u16>,
42 ) -> Self {
43 Self {
44 script_count: 0,
45 langsys_count: 0,
46 feature_index_count: 0,
47 visited_script: IntSet::empty(),
48 visited_langsys: IntSet::empty(),
49 feature_indices,
50 feature_indices_filter: feature_list
51 .feature_records()
52 .iter()
53 .enumerate()
54 .filter(|(_i, record)| features.contains(record.feature_tag()))
55 .map(|(idx, _)| idx as u16)
56 .collect(),
57 table_head,
58 }
59 }
60
61 pub(crate) fn script_visited(&mut self, s: &Script) -> bool {
63 if self.script_count > MAX_SCRIPTS {
64 return true;
65 }
66
67 self.script_count += 1;
68
69 let delta = (s.offset_data().as_bytes().as_ptr() as usize - self.table_head) as u32;
70 !self.visited_script.insert(delta)
71 }
72
73 pub(crate) fn langsys_visited(&mut self, langsys: &LangSys) -> bool {
75 if self.langsys_count > MAX_LANGSYS {
76 return true;
77 }
78
79 self.langsys_count += 1;
80
81 let delta = (langsys.offset_data().as_bytes().as_ptr() as usize - self.table_head) as u32;
82 !self.visited_langsys.insert(delta)
83 }
84
85 pub(crate) fn feature_indices_limit_exceeded(&mut self, count: u16) -> bool {
87 let (new_count, overflow) = self.feature_index_count.overflowing_add(count);
88 if overflow {
89 self.feature_index_count = MAX_FEATURE_INDICES;
90 return true;
91 }
92 self.feature_index_count = new_count;
93 new_count > MAX_FEATURE_INDICES
94 }
95}
96
97impl ScriptList<'_> {
98 pub(crate) fn collect_features(
100 &self,
101 layout_table_head: usize,
102 feature_list: &FeatureList,
103 scripts: &IntSet<Tag>,
104 languages: &IntSet<Tag>,
105 features: &IntSet<Tag>,
106 ) -> Result<IntSet<u16>, ReadError> {
107 let mut out = IntSet::empty();
108 let mut c =
109 CollectFeaturesContext::new(features, layout_table_head, feature_list, &mut out);
110 let script_records = self.script_records();
111 let font_data = self.offset_data();
112 if scripts.is_inverted() {
113 for record in script_records {
114 let tag = record.script_tag();
115 if !scripts.contains(tag) {
116 continue;
117 }
118 let script = record.script(font_data)?;
119 script.collect_features(&mut c, languages)?;
120 }
121 } else {
122 for idx in scripts.iter().filter_map(|tag| self.index_for_tag(tag)) {
123 let script = script_records[idx as usize].script(font_data)?;
124 script.collect_features(&mut c, languages)?;
125 }
126 }
127 Ok(out)
128 }
129}
130
131impl Script<'_> {
132 fn collect_features(
133 &self,
134 c: &mut CollectFeaturesContext,
135 languages: &IntSet<Tag>,
136 ) -> Result<(), ReadError> {
137 if c.script_visited(self) {
138 return Ok(());
139 }
140
141 let lang_sys_records = self.lang_sys_records();
142 let font_data = self.offset_data();
143
144 if let Some(default_lang_sys) = self.default_lang_sys().transpose()? {
145 default_lang_sys.collect_features(c);
146 }
147
148 if languages.is_inverted() {
149 for record in lang_sys_records {
150 let tag = record.lang_sys_tag();
151 if !languages.contains(tag) {
152 continue;
153 }
154 let lang_sys = record.lang_sys(font_data)?;
155 lang_sys.collect_features(c);
156 }
157 } else {
158 for idx in languages
159 .iter()
160 .filter_map(|tag| self.lang_sys_index_for_tag(tag))
161 {
162 let lang_sys = lang_sys_records[idx as usize].lang_sys(font_data)?;
163 lang_sys.collect_features(c);
164 }
165 }
166 Ok(())
167 }
168}
169
170impl LangSys<'_> {
171 fn collect_features(&self, c: &mut CollectFeaturesContext) {
172 if c.langsys_visited(self) {
173 return;
174 }
175
176 if c.feature_indices_filter.is_empty() {
177 return;
178 }
179
180 let required_feature_idx = self.required_feature_index();
181 if required_feature_idx != 0xFFFF
182 && !c.feature_indices_limit_exceeded(1)
183 && c.feature_indices_filter.contains(required_feature_idx)
184 {
185 c.feature_indices.insert(required_feature_idx);
186 }
187
188 if c.feature_indices_limit_exceeded(self.feature_index_count()) {
189 return;
190 }
191
192 for feature_index in self.feature_indices() {
193 let idx = feature_index.get();
194 if !c.feature_indices_filter.contains(idx) {
195 continue;
196 }
197 c.feature_indices.insert(idx);
198 c.feature_indices_filter.remove(idx);
199 }
200 }
201}
202
203impl Feature<'_> {
204 pub(crate) fn collect_lookups(&self) -> Vec<u16> {
205 self.lookup_list_indices()
206 .iter()
207 .map(|idx| idx.get())
208 .collect()
209 }
210}
211
212impl FeatureList<'_> {
213 pub(crate) fn collect_lookups(
214 &self,
215 feature_indices: &IntSet<u16>,
216 ) -> Result<IntSet<u16>, ReadError> {
217 let features_records = self.feature_records();
218 let num_features = self.feature_count();
219 let font_data = self.offset_data();
220 let mut lookup_idxes = IntSet::empty();
221
222 if feature_indices.is_inverted() {
223 for feature_rec in (0..num_features).filter_map(|i| {
224 feature_indices
225 .contains(i)
226 .then(|| features_records.get(i as usize))
227 .flatten()
228 }) {
229 lookup_idxes.extend_unsorted(feature_rec.feature(font_data)?.collect_lookups());
230 }
231 } else {
232 for feature_rec in feature_indices
233 .iter()
234 .filter_map(|i| features_records.get(i as usize))
235 {
236 lookup_idxes.extend_unsorted(feature_rec.feature(font_data)?.collect_lookups());
237 }
238 }
239 Ok(lookup_idxes)
240 }
241}
242
243impl FeatureVariations<'_> {
244 pub(crate) fn collect_lookups(
245 &self,
246 feature_indices: &IntSet<u16>,
247 ) -> Result<IntSet<u16>, ReadError> {
248 let mut out = IntSet::empty();
249
250 for variation_rec in self.feature_variation_records() {
251 let Some(subs) = variation_rec
252 .feature_table_substitution(self.offset_data())
253 .transpose()?
254 else {
255 continue;
256 };
257
258 for sub_record in subs
259 .substitutions()
260 .iter()
261 .filter(|sub_rec| feature_indices.contains(sub_rec.feature_index()))
262 {
263 let sub_f = sub_record.alternate_feature(subs.offset_data())?;
264 out.extend_unsorted(sub_f.lookup_list_indices().iter().map(|i| i.get()));
265 }
266 }
267 Ok(out)
268 }
269}
270
271pub(crate) enum LayoutLookupList<'a> {
272 Gsub(&'a SubstitutionLookupList<'a>),
273 Gpos(&'a PositionLookupList<'a>),
274}
275
276pub(crate) struct LookupClosureCtx<'a> {
277 visited_lookups: IntSet<u16>,
278 inactive_lookups: IntSet<u16>,
279 glyph_set: &'a IntSet<GlyphId>,
280 lookup_count: u16,
281 nesting_level_left: u8,
282 lookup_list: &'a LayoutLookupList<'a>,
283}
284
285impl<'a> LookupClosureCtx<'a> {
286 pub(crate) fn new(glyph_set: &'a IntSet<GlyphId>, lookup_list: &'a LayoutLookupList) -> Self {
287 Self {
288 visited_lookups: IntSet::empty(),
289 inactive_lookups: IntSet::empty(),
290 glyph_set,
291 lookup_count: 0,
292 nesting_level_left: MAX_NESTING_LEVEL,
293 lookup_list,
294 }
295 }
296
297 pub(crate) fn visited_lookups(&self) -> &IntSet<u16> {
298 &self.visited_lookups
299 }
300
301 pub(crate) fn inactive_lookups(&self) -> &IntSet<u16> {
302 &self.inactive_lookups
303 }
304
305 pub(crate) fn glyphs(&self) -> &IntSet<GlyphId> {
306 self.glyph_set
307 }
308
309 pub(crate) fn set_lookup_inactive(&mut self, lookup_index: u16) {
310 self.inactive_lookups.insert(lookup_index);
311 }
312
313 pub(crate) fn lookup_limit_exceed(&self) -> bool {
314 self.lookup_count > MAX_LOOKUP_VISIT_COUNT
315 }
316
317 pub(crate) fn should_visit_lookup(&mut self, lookup_index: u16) -> bool {
320 if self.lookup_count > MAX_LOOKUP_VISIT_COUNT {
321 return false;
322 }
323 self.lookup_count += 1;
324 self.visited_lookups.insert(lookup_index)
325 }
326
327 pub(crate) fn recurse(&mut self, lookup_index: u16) -> Result<(), ReadError> {
328 if self.nesting_level_left == 0 {
329 return Ok(());
330 }
331
332 if self.lookup_limit_exceed() || self.visited_lookups.contains(lookup_index) {
333 return Ok(());
334 }
335
336 self.nesting_level_left -= 1;
337 match self.lookup_list {
338 LayoutLookupList::Gpos(lookuplist) => {
339 lookuplist
340 .lookups()
341 .get(lookup_index as usize)?
342 .closure_lookups(self, lookup_index)?;
343 }
344 LayoutLookupList::Gsub(lookuplist) => {
345 lookuplist
346 .lookups()
347 .get(lookup_index as usize)?
348 .closure_lookups(self, lookup_index)?;
349 }
350 }
351 self.nesting_level_left += 1;
352 Ok(())
353 }
354}
355
356pub(crate) trait LookupClosure {
358 fn closure_lookups(&self, _c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
359 Ok(())
360 }
361}
362
363pub trait Intersect {
364 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError>;
365}
366
367impl Intersect for ClassDef<'_> {
368 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
369 match self {
370 ClassDef::Format1(table) => table.intersects(glyph_set),
371 ClassDef::Format2(table) => table.intersects(glyph_set),
372 }
373 }
374}
375
376impl Intersect for ClassDefFormat1<'_> {
377 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
378 let glyph_count = self.glyph_count();
379 if glyph_count == 0 {
380 return Ok(false);
381 }
382
383 let start = self.start_glyph_id().to_u32();
384 let end = start + glyph_count as u32;
385
386 let mut start_glyph = GlyphId::from(start);
387 let class_values = self.class_value_array();
388 if glyph_set.contains(start_glyph) && class_values[0] != 0 {
389 return Ok(true);
390 }
391
392 while let Some(g) = glyph_set.iter_after(start_glyph).next() {
393 let g = g.to_u32();
394 if g >= end {
395 break;
396 }
397 let Some(class) = class_values.get((g - start) as usize) else {
398 break;
399 };
400 if class.get() != 0 {
401 return Ok(true);
402 }
403 start_glyph = GlyphId::from(g);
404 }
405 Ok(false)
406 }
407}
408
409impl Intersect for ClassDefFormat2<'_> {
410 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
411 let num_ranges = self.class_range_count();
412 let num_bits = 16 - num_ranges.leading_zeros();
413 if num_ranges as u64 > glyph_set.len() * num_bits as u64 {
414 for g in glyph_set.iter().map(|g| GlyphId16::from(g.to_u32() as u16)) {
415 if self.get(g) != 0 {
416 return Ok(true);
417 }
418 }
419 } else {
420 for record in self.class_range_records() {
421 let first = GlyphId::from(record.start_glyph_id());
422 let last = GlyphId::from(record.end_glyph_id());
423 if glyph_set.intersects_range(first..=last) && record.class() != 0 {
424 return Ok(true);
425 }
426 }
427 }
428 Ok(false)
429 }
430}
431
432impl<'a, T, Ext> LookupClosure for Subtables<'a, T, Ext>
433where
434 T: LookupClosure + Intersect + FontRead<'a> + 'a,
435 Ext: ExtensionLookup<'a, T> + 'a,
436{
437 fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
438 for sub in self.iter() {
439 sub?.closure_lookups(c, arg)?;
440 }
441 Ok(())
442 }
443}
444
445impl<'a, T, Ext> Intersect for Subtables<'a, T, Ext>
446where
447 T: Intersect + FontRead<'a> + 'a,
448 Ext: ExtensionLookup<'a, T> + 'a,
449{
450 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
451 for sub in self.iter() {
452 if sub?.intersects(glyph_set)? {
453 return Ok(true);
454 }
455 }
456 Ok(false)
457 }
458}
459
460pub(crate) enum ContextFormat1<'a> {
463 Plain(SequenceContextFormat1<'a>),
464 Chain(ChainedSequenceContextFormat1<'a>),
465}
466
467pub(crate) enum Format1RuleSet<'a> {
468 Plain(SequenceRuleSet<'a>),
469 Chain(ChainedSequenceRuleSet<'a>),
470}
471
472pub(crate) enum Format1Rule<'a> {
473 Plain(SequenceRule<'a>),
474 Chain(ChainedSequenceRule<'a>),
475}
476
477impl ContextFormat1<'_> {
478 pub(crate) fn coverage(&self) -> Result<CoverageTable<'_>, ReadError> {
479 match self {
480 ContextFormat1::Plain(table) => table.coverage(),
481 ContextFormat1::Chain(table) => table.coverage(),
482 }
483 }
484
485 pub(crate) fn rule_sets(
486 &self,
487 ) -> impl Iterator<Item = Option<Result<Format1RuleSet<'_>, ReadError>>> {
488 let (left, right) = match self {
489 ContextFormat1::Plain(table) => (
490 Some(
491 table
492 .seq_rule_sets()
493 .iter()
494 .map(|rs| rs.map(|rs| rs.map(Format1RuleSet::Plain))),
495 ),
496 None,
497 ),
498 ContextFormat1::Chain(table) => (
499 None,
500 Some(
501 table
502 .chained_seq_rule_sets()
503 .iter()
504 .map(|rs| rs.map(|rs| rs.map(Format1RuleSet::Chain))),
505 ),
506 ),
507 };
508 left.into_iter()
509 .flatten()
510 .chain(right.into_iter().flatten())
511 }
512}
513
514impl Format1RuleSet<'_> {
515 pub(crate) fn rules(&self) -> impl Iterator<Item = Result<Format1Rule<'_>, ReadError>> {
516 let (left, right) = match self {
517 Self::Plain(table) => (
518 Some(
519 table
520 .seq_rules()
521 .iter()
522 .map(|rule| rule.map(Format1Rule::Plain)),
523 ),
524 None,
525 ),
526 Self::Chain(table) => (
527 None,
528 Some(
529 table
530 .chained_seq_rules()
531 .iter()
532 .map(|rule| rule.map(Format1Rule::Chain)),
533 ),
534 ),
535 };
536 left.into_iter()
537 .flatten()
538 .chain(right.into_iter().flatten())
539 }
540}
541
542impl Format1Rule<'_> {
543 pub(crate) fn input_sequence(&self) -> &[BigEndian<GlyphId16>] {
544 match self {
545 Self::Plain(table) => table.input_sequence(),
546 Self::Chain(table) => table.input_sequence(),
547 }
548 }
549
550 pub(crate) fn lookup_records(&self) -> &[SequenceLookupRecord] {
551 match self {
552 Self::Plain(table) => table.seq_lookup_records(),
553 Self::Chain(table) => table.seq_lookup_records(),
554 }
555 }
556}
557
558impl Intersect for &[BigEndian<GlyphId16>] {
559 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
560 Ok(self
561 .iter()
562 .all(|g| glyph_set.contains(GlyphId::from(g.get()))))
563 }
564}
565
566impl Intersect for Format1Rule<'_> {
567 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
568 match self {
569 Self::Plain(table) => table.input_sequence().intersects(glyph_set),
570 Self::Chain(table) => Ok(table.backtrack_sequence().intersects(glyph_set)?
571 && table.input_sequence().intersects(glyph_set)?
572 && table.lookahead_sequence().intersects(glyph_set)?),
573 }
574 }
575}
576
577impl LookupClosure for Format1Rule<'_> {
578 fn closure_lookups(&self, c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
579 if c.lookup_limit_exceed() || !self.intersects(c.glyphs())? {
580 return Ok(());
581 }
582
583 for lookup_record in self.lookup_records() {
584 let index = lookup_record.lookup_list_index();
585 c.recurse(index)?;
586 }
587 Ok(())
588 }
589}
590
591pub(crate) enum ContextFormat2<'a> {
592 Plain(SequenceContextFormat2<'a>),
593 Chain(ChainedSequenceContextFormat2<'a>),
594}
595
596pub(crate) enum Format2RuleSet<'a> {
597 Plain(ClassSequenceRuleSet<'a>),
598 Chain(ChainedClassSequenceRuleSet<'a>),
599}
600
601pub(crate) enum Format2Rule<'a> {
602 Plain(ClassSequenceRule<'a>),
603 Chain(ChainedClassSequenceRule<'a>),
604}
605
606impl ContextFormat2<'_> {
607 pub(crate) fn coverage(&self) -> Result<CoverageTable<'_>, ReadError> {
608 match self {
609 ContextFormat2::Plain(table) => table.coverage(),
610 ContextFormat2::Chain(table) => table.coverage(),
611 }
612 }
613
614 pub(crate) fn input_class_def(&self) -> Result<ClassDef<'_>, ReadError> {
615 match self {
616 ContextFormat2::Plain(table_ref) => table_ref.class_def(),
617 ContextFormat2::Chain(table_ref) => table_ref.input_class_def(),
618 }
619 }
620
621 pub(crate) fn rule_sets(
622 &self,
623 ) -> impl Iterator<Item = Option<Result<Format2RuleSet<'_>, ReadError>>> {
624 let (left, right) = match self {
625 ContextFormat2::Plain(table) => (
626 Some(
627 table
628 .class_seq_rule_sets()
629 .iter()
630 .map(|rs| rs.map(|rs| rs.map(Format2RuleSet::Plain))),
631 ),
632 None,
633 ),
634 ContextFormat2::Chain(table) => (
635 None,
636 Some(
637 table
638 .chained_class_seq_rule_sets()
639 .iter()
640 .map(|rs| rs.map(|rs| rs.map(Format2RuleSet::Chain))),
641 ),
642 ),
643 };
644 left.into_iter()
645 .flatten()
646 .chain(right.into_iter().flatten())
647 }
648}
649
650impl Format2RuleSet<'_> {
651 pub(crate) fn rules(&self) -> impl Iterator<Item = Result<Format2Rule<'_>, ReadError>> {
652 let (left, right) = match self {
653 Format2RuleSet::Plain(table) => (
654 Some(
655 table
656 .class_seq_rules()
657 .iter()
658 .map(|rule| rule.map(Format2Rule::Plain)),
659 ),
660 None,
661 ),
662 Format2RuleSet::Chain(table) => (
663 None,
664 Some(
665 table
666 .chained_class_seq_rules()
667 .iter()
668 .map(|rule| rule.map(Format2Rule::Chain)),
669 ),
670 ),
671 };
672 left.into_iter()
673 .flatten()
674 .chain(right.into_iter().flatten())
675 }
676}
677
678impl Format2Rule<'_> {
679 pub(crate) fn input_sequence(&self) -> &[BigEndian<u16>] {
680 match self {
681 Self::Plain(table) => table.input_sequence(),
682 Self::Chain(table) => table.input_sequence(),
683 }
684 }
685
686 pub(crate) fn lookup_records(&self) -> &[SequenceLookupRecord] {
687 match self {
688 Self::Plain(table) => table.seq_lookup_records(),
689 Self::Chain(table) => table.seq_lookup_records(),
690 }
691 }
692
693 pub(crate) fn intersects(
694 &self,
695 input_classes: &IntSet<u16>,
696 backtrack_classes: &IntSet<u16>,
697 lookahead_classes: &IntSet<u16>,
698 ) -> bool {
699 match self {
700 Self::Plain(table) => table.intersects(input_classes),
701 Self::Chain(table) => {
702 table.intersects(input_classes, backtrack_classes, lookahead_classes)
703 }
704 }
705 }
706}
707
708impl ClassSequenceRule<'_> {
709 fn intersects(&self, input_classes: &IntSet<u16>) -> bool {
710 self.input_sequence()
711 .iter()
712 .all(|c| input_classes.contains(c.get()))
713 }
714}
715
716impl ChainedClassSequenceRule<'_> {
717 fn intersects(
718 &self,
719 input_classes: &IntSet<u16>,
720 backtrack_classes: &IntSet<u16>,
721 lookahead_classes: &IntSet<u16>,
722 ) -> bool {
723 self.input_sequence()
724 .iter()
725 .all(|c| input_classes.contains(c.get()))
726 && self
727 .backtrack_sequence()
728 .iter()
729 .all(|c| backtrack_classes.contains(c.get()))
730 && self
731 .lookahead_sequence()
732 .iter()
733 .all(|c| lookahead_classes.contains(c.get()))
734 }
735}
736
737pub(crate) enum ContextFormat3<'a> {
738 Plain(SequenceContextFormat3<'a>),
739 Chain(ChainedSequenceContextFormat3<'a>),
740}
741
742impl ContextFormat3<'_> {
743 pub(crate) fn coverages(&self) -> ArrayOfOffsets<'_, CoverageTable<'_>> {
744 match self {
745 ContextFormat3::Plain(table) => table.coverages(),
746 ContextFormat3::Chain(table) => table.input_coverages(),
747 }
748 }
749
750 pub(crate) fn lookup_records(&self) -> &[SequenceLookupRecord] {
751 match self {
752 ContextFormat3::Plain(table) => table.seq_lookup_records(),
753 ContextFormat3::Chain(table) => table.seq_lookup_records(),
754 }
755 }
756
757 pub(crate) fn matches_glyphs(&self, glyphs: &IntSet<GlyphId>) -> Result<bool, ReadError> {
758 let (backtrack, lookahead) = match self {
759 Self::Plain(_) => (None, None),
760 Self::Chain(table) => (
761 Some(table.backtrack_coverages()),
762 Some(table.lookahead_coverages()),
763 ),
764 };
765
766 for coverage in self
767 .coverages()
768 .iter()
769 .chain(backtrack.into_iter().flat_map(|x| x.iter()))
770 .chain(lookahead.into_iter().flat_map(|x| x.iter()))
771 {
772 if !coverage?.intersects(glyphs) {
773 return Ok(false);
774 }
775 }
776 Ok(true)
777 }
778}
779
780impl Intersect for ContextFormat1<'_> {
781 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
782 let coverage = self.coverage()?;
783 for rule_set in coverage
784 .iter()
785 .zip(self.rule_sets())
786 .filter_map(|(g, rule_set)| rule_set.filter(|_| glyph_set.contains(GlyphId::from(g))))
787 {
788 for rule in rule_set?.rules() {
789 if rule?.intersects(glyph_set)? {
790 return Ok(true);
791 }
792 }
793 }
794 Ok(false)
795 }
796}
797
798impl LookupClosure for ContextFormat1<'_> {
799 fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
800 let coverage = self.coverage()?;
801 let glyph_set = c.glyphs();
802
803 let intersected_idxes: IntSet<u16> = coverage
804 .iter()
805 .enumerate()
806 .filter(|&(_, g)| glyph_set.contains(GlyphId::from(g)))
807 .map(|(idx, _)| idx as u16)
808 .collect();
809
810 for rule_set in self.rule_sets().enumerate().filter_map(|(idx, rule_set)| {
811 rule_set.filter(|_| intersected_idxes.contains(idx as u16))
812 }) {
813 if c.lookup_limit_exceed() {
814 return Ok(());
815 }
816 for rule in rule_set?.rules() {
817 rule?.closure_lookups(c, arg)?;
818 }
819 }
820
821 Ok(())
822 }
823}
824
825impl Intersect for ContextFormat2<'_> {
826 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
827 let coverage = self.coverage()?;
828 let retained_coverage_glyphs = coverage.intersect_set(glyph_set);
829 if retained_coverage_glyphs.is_empty() {
830 return Ok(false);
831 }
832
833 let input_class_def = self.input_class_def()?;
834 let coverage_glyph_classes = input_class_def.intersect_classes(&retained_coverage_glyphs);
835 let input_glyph_classes = input_class_def.intersect_classes(glyph_set);
836
837 let backtrack_classes = match self {
838 Self::Plain(_) => IntSet::empty(),
839 Self::Chain(table) => table.backtrack_class_def()?.intersect_classes(glyph_set),
840 };
841
842 let lookahead_classes = match self {
843 Self::Plain(_) => IntSet::empty(),
844 Self::Chain(table) => table.lookahead_class_def()?.intersect_classes(glyph_set),
845 };
846
847 for rule_set in self.rule_sets().enumerate().filter_map(|(c, rule_set)| {
848 coverage_glyph_classes
849 .contains(c as u16)
850 .then_some(rule_set)
851 .flatten()
852 }) {
853 for rule in rule_set?.rules() {
854 if rule?.intersects(&input_glyph_classes, &backtrack_classes, &lookahead_classes) {
855 return Ok(true);
856 }
857 }
858 }
859 Ok(false)
860 }
861}
862
863impl LookupClosure for ContextFormat2<'_> {
864 fn closure_lookups(&self, c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
865 let glyph_set = c.glyphs();
866 let coverage = self.coverage()?;
867 let retained_coverage_glyphs = coverage.intersect_set(glyph_set);
868 if retained_coverage_glyphs.is_empty() {
869 return Ok(());
870 }
871
872 let input_class_def = self.input_class_def()?;
873 let coverage_glyph_classes = input_class_def.intersect_classes(&retained_coverage_glyphs);
874 let input_glyph_classes = input_class_def.intersect_classes(glyph_set);
875
876 let backtrack_classes = match self {
877 Self::Plain(_) => IntSet::empty(),
878 Self::Chain(table) => table.backtrack_class_def()?.intersect_classes(glyph_set),
879 };
880
881 let lookahead_classes = match self {
882 Self::Plain(_) => IntSet::empty(),
883 Self::Chain(table) => table.lookahead_class_def()?.intersect_classes(glyph_set),
884 };
885
886 for rule_set in self.rule_sets().enumerate().filter_map(|(c, rule_set)| {
887 coverage_glyph_classes
888 .contains(c as u16)
889 .then_some(rule_set)
890 .flatten()
891 }) {
892 if c.lookup_limit_exceed() {
893 return Ok(());
894 }
895
896 for rule in rule_set?.rules() {
897 let rule = rule?;
898 if c.lookup_limit_exceed()
899 || !rule.intersects(
900 &input_glyph_classes,
901 &backtrack_classes,
902 &lookahead_classes,
903 )
904 {
905 return Ok(());
906 }
907
908 for lookup_record in rule.lookup_records() {
909 let index = lookup_record.lookup_list_index();
910 c.recurse(index)?;
911 }
912 }
913 }
914 Ok(())
915 }
916}
917
918impl Intersect for ContextFormat3<'_> {
919 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
920 self.matches_glyphs(glyph_set)
921 }
922}
923
924impl LookupClosure for ContextFormat3<'_> {
925 fn closure_lookups(&self, c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
926 if !self.intersects(c.glyphs())? {
927 return Ok(());
928 }
929
930 for lookup_record in self.lookup_records() {
931 let index = lookup_record.lookup_list_index();
932 c.recurse(index)?;
933 }
934
935 Ok(())
936 }
937}
938
939impl Intersect for SequenceContext<'_> {
940 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
941 match self {
942 Self::Format1(table) => ContextFormat1::Plain(table.clone()).intersects(glyph_set),
943 Self::Format2(table) => ContextFormat2::Plain(table.clone()).intersects(glyph_set),
944 Self::Format3(table) => ContextFormat3::Plain(table.clone()).intersects(glyph_set),
945 }
946 }
947}
948
949impl LookupClosure for SequenceContext<'_> {
950 fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
951 match self {
952 Self::Format1(table) => ContextFormat1::Plain(table.clone()).closure_lookups(c, arg),
953 Self::Format2(table) => ContextFormat2::Plain(table.clone()).closure_lookups(c, arg),
954 Self::Format3(table) => ContextFormat3::Plain(table.clone()).closure_lookups(c, arg),
955 }
956 }
957}
958
959impl Intersect for ChainedSequenceContext<'_> {
960 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
961 match self {
962 Self::Format1(table) => ContextFormat1::Chain(table.clone()).intersects(glyph_set),
963 Self::Format2(table) => ContextFormat2::Chain(table.clone()).intersects(glyph_set),
964 Self::Format3(table) => ContextFormat3::Chain(table.clone()).intersects(glyph_set),
965 }
966 }
967}
968
969impl LookupClosure for ChainedSequenceContext<'_> {
970 fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
971 match self {
972 Self::Format1(table) => ContextFormat1::Chain(table.clone()).closure_lookups(c, arg),
973 Self::Format2(table) => ContextFormat2::Chain(table.clone()).closure_lookups(c, arg),
974 Self::Format3(table) => ContextFormat3::Chain(table.clone()).closure_lookups(c, arg),
975 }
976 }
977}