read_fonts/tables/layout/
closure.rs

1//! Support Layout Closure
2
3use 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    /// Return true if the script limit has been exceeded or the script is visited before
62    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    /// Return true if the Langsys limit has been exceeded or the Langsys is visited before
74    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    /// Returns true if the feature limit has been exceeded
86    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    /// Return a set of all feature indices underneath the specified scripts, languages and features
99    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    // return false if lookup limit exceeded or lookup visited,and visited set is not modified
318    // Otherwise return true and insert lookup index into the visited set
319    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
356/// Compute the transitive closure of lookups
357pub(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 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        }
404        Ok(false)
405    }
406}
407
408impl Intersect for ClassDefFormat2<'_> {
409    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
410        let num_ranges = self.class_range_count();
411        let num_bits = 16 - num_ranges.leading_zeros();
412        if num_ranges as u64 > glyph_set.len() * num_bits as u64 {
413            for g in glyph_set.iter().map(|g| GlyphId16::from(g.to_u32() as u16)) {
414                if self.get(g) != 0 {
415                    return Ok(true);
416                }
417            }
418        } else {
419            for record in self.class_range_records() {
420                let first = GlyphId::from(record.start_glyph_id());
421                let last = GlyphId::from(record.end_glyph_id());
422                if glyph_set.intersects_range(first..=last) && record.class() != 0 {
423                    return Ok(true);
424                }
425            }
426        }
427        Ok(false)
428    }
429}
430
431impl<'a, T, Ext> LookupClosure for Subtables<'a, T, Ext>
432where
433    T: LookupClosure + Intersect + FontRead<'a> + 'a,
434    Ext: ExtensionLookup<'a, T> + 'a,
435{
436    fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
437        for sub in self.iter() {
438            sub?.closure_lookups(c, arg)?;
439        }
440        Ok(())
441    }
442}
443
444impl<'a, T, Ext> Intersect for Subtables<'a, T, Ext>
445where
446    T: Intersect + FontRead<'a> + 'a,
447    Ext: ExtensionLookup<'a, T> + 'a,
448{
449    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
450        for sub in self.iter() {
451            if sub?.intersects(glyph_set)? {
452                return Ok(true);
453            }
454        }
455        Ok(false)
456    }
457}
458
459// these are basically the same; but we need to jump through some hoops
460// to get the fields to line up
461pub(crate) enum ContextFormat1<'a> {
462    Plain(SequenceContextFormat1<'a>),
463    Chain(ChainedSequenceContextFormat1<'a>),
464}
465
466pub(crate) enum Format1RuleSet<'a> {
467    Plain(SequenceRuleSet<'a>),
468    Chain(ChainedSequenceRuleSet<'a>),
469}
470
471pub(crate) enum Format1Rule<'a> {
472    Plain(SequenceRule<'a>),
473    Chain(ChainedSequenceRule<'a>),
474}
475
476impl ContextFormat1<'_> {
477    pub(crate) fn coverage(&self) -> Result<CoverageTable<'_>, ReadError> {
478        match self {
479            ContextFormat1::Plain(table) => table.coverage(),
480            ContextFormat1::Chain(table) => table.coverage(),
481        }
482    }
483
484    pub(crate) fn rule_sets(
485        &self,
486    ) -> impl Iterator<Item = Option<Result<Format1RuleSet<'_>, ReadError>>> {
487        let (left, right) = match self {
488            ContextFormat1::Plain(table) => (
489                Some(
490                    table
491                        .seq_rule_sets()
492                        .iter()
493                        .map(|rs| rs.map(|rs| rs.map(Format1RuleSet::Plain))),
494                ),
495                None,
496            ),
497            ContextFormat1::Chain(table) => (
498                None,
499                Some(
500                    table
501                        .chained_seq_rule_sets()
502                        .iter()
503                        .map(|rs| rs.map(|rs| rs.map(Format1RuleSet::Chain))),
504                ),
505            ),
506        };
507        left.into_iter()
508            .flatten()
509            .chain(right.into_iter().flatten())
510    }
511}
512
513impl Format1RuleSet<'_> {
514    pub(crate) fn rules(&self) -> impl Iterator<Item = Result<Format1Rule<'_>, ReadError>> {
515        let (left, right) = match self {
516            Self::Plain(table) => (
517                Some(
518                    table
519                        .seq_rules()
520                        .iter()
521                        .map(|rule| rule.map(Format1Rule::Plain)),
522                ),
523                None,
524            ),
525            Self::Chain(table) => (
526                None,
527                Some(
528                    table
529                        .chained_seq_rules()
530                        .iter()
531                        .map(|rule| rule.map(Format1Rule::Chain)),
532                ),
533            ),
534        };
535        left.into_iter()
536            .flatten()
537            .chain(right.into_iter().flatten())
538    }
539}
540
541impl Format1Rule<'_> {
542    pub(crate) fn input_sequence(&self) -> &[BigEndian<GlyphId16>] {
543        match self {
544            Self::Plain(table) => table.input_sequence(),
545            Self::Chain(table) => table.input_sequence(),
546        }
547    }
548
549    pub(crate) fn lookup_records(&self) -> &[SequenceLookupRecord] {
550        match self {
551            Self::Plain(table) => table.seq_lookup_records(),
552            Self::Chain(table) => table.seq_lookup_records(),
553        }
554    }
555}
556
557impl Intersect for &[BigEndian<GlyphId16>] {
558    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
559        Ok(self
560            .iter()
561            .all(|g| glyph_set.contains(GlyphId::from(g.get()))))
562    }
563}
564
565impl Intersect for Format1Rule<'_> {
566    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
567        match self {
568            Self::Plain(table) => table.input_sequence().intersects(glyph_set),
569            Self::Chain(table) => Ok(table.backtrack_sequence().intersects(glyph_set)?
570                && table.input_sequence().intersects(glyph_set)?
571                && table.lookahead_sequence().intersects(glyph_set)?),
572        }
573    }
574}
575
576impl LookupClosure for Format1Rule<'_> {
577    fn closure_lookups(&self, c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
578        if c.lookup_limit_exceed() || !self.intersects(c.glyphs())? {
579            return Ok(());
580        }
581
582        for lookup_record in self.lookup_records() {
583            let index = lookup_record.lookup_list_index();
584            c.recurse(index)?;
585        }
586        Ok(())
587    }
588}
589
590pub(crate) enum ContextFormat2<'a> {
591    Plain(SequenceContextFormat2<'a>),
592    Chain(ChainedSequenceContextFormat2<'a>),
593}
594
595pub(crate) enum Format2RuleSet<'a> {
596    Plain(ClassSequenceRuleSet<'a>),
597    Chain(ChainedClassSequenceRuleSet<'a>),
598}
599
600pub(crate) enum Format2Rule<'a> {
601    Plain(ClassSequenceRule<'a>),
602    Chain(ChainedClassSequenceRule<'a>),
603}
604
605impl ContextFormat2<'_> {
606    pub(crate) fn coverage(&self) -> Result<CoverageTable<'_>, ReadError> {
607        match self {
608            ContextFormat2::Plain(table) => table.coverage(),
609            ContextFormat2::Chain(table) => table.coverage(),
610        }
611    }
612
613    pub(crate) fn input_class_def(&self) -> Result<ClassDef<'_>, ReadError> {
614        match self {
615            ContextFormat2::Plain(table_ref) => table_ref.class_def(),
616            ContextFormat2::Chain(table_ref) => table_ref.input_class_def(),
617        }
618    }
619
620    pub(crate) fn rule_sets(
621        &self,
622    ) -> impl Iterator<Item = Option<Result<Format2RuleSet<'_>, ReadError>>> {
623        let (left, right) = match self {
624            ContextFormat2::Plain(table) => (
625                Some(
626                    table
627                        .class_seq_rule_sets()
628                        .iter()
629                        .map(|rs| rs.map(|rs| rs.map(Format2RuleSet::Plain))),
630                ),
631                None,
632            ),
633            ContextFormat2::Chain(table) => (
634                None,
635                Some(
636                    table
637                        .chained_class_seq_rule_sets()
638                        .iter()
639                        .map(|rs| rs.map(|rs| rs.map(Format2RuleSet::Chain))),
640                ),
641            ),
642        };
643        left.into_iter()
644            .flatten()
645            .chain(right.into_iter().flatten())
646    }
647}
648
649impl Format2RuleSet<'_> {
650    pub(crate) fn rules(&self) -> impl Iterator<Item = Result<Format2Rule<'_>, ReadError>> {
651        let (left, right) = match self {
652            Format2RuleSet::Plain(table) => (
653                Some(
654                    table
655                        .class_seq_rules()
656                        .iter()
657                        .map(|rule| rule.map(Format2Rule::Plain)),
658                ),
659                None,
660            ),
661            Format2RuleSet::Chain(table) => (
662                None,
663                Some(
664                    table
665                        .chained_class_seq_rules()
666                        .iter()
667                        .map(|rule| rule.map(Format2Rule::Chain)),
668                ),
669            ),
670        };
671        left.into_iter()
672            .flatten()
673            .chain(right.into_iter().flatten())
674    }
675}
676
677impl Format2Rule<'_> {
678    pub(crate) fn input_sequence(&self) -> &[BigEndian<u16>] {
679        match self {
680            Self::Plain(table) => table.input_sequence(),
681            Self::Chain(table) => table.input_sequence(),
682        }
683    }
684
685    pub(crate) fn lookup_records(&self) -> &[SequenceLookupRecord] {
686        match self {
687            Self::Plain(table) => table.seq_lookup_records(),
688            Self::Chain(table) => table.seq_lookup_records(),
689        }
690    }
691
692    pub(crate) fn intersects(
693        &self,
694        input_classes: &IntSet<u16>,
695        backtrack_classes: &IntSet<u16>,
696        lookahead_classes: &IntSet<u16>,
697    ) -> bool {
698        match self {
699            Self::Plain(table) => table.intersects(input_classes),
700            Self::Chain(table) => {
701                table.intersects(input_classes, backtrack_classes, lookahead_classes)
702            }
703        }
704    }
705}
706
707impl ClassSequenceRule<'_> {
708    fn intersects(&self, input_classes: &IntSet<u16>) -> bool {
709        self.input_sequence()
710            .iter()
711            .all(|c| input_classes.contains(c.get()))
712    }
713}
714
715impl ChainedClassSequenceRule<'_> {
716    fn intersects(
717        &self,
718        input_classes: &IntSet<u16>,
719        backtrack_classes: &IntSet<u16>,
720        lookahead_classes: &IntSet<u16>,
721    ) -> bool {
722        self.input_sequence()
723            .iter()
724            .all(|c| input_classes.contains(c.get()))
725            && self
726                .backtrack_sequence()
727                .iter()
728                .all(|c| backtrack_classes.contains(c.get()))
729            && self
730                .lookahead_sequence()
731                .iter()
732                .all(|c| lookahead_classes.contains(c.get()))
733    }
734}
735
736pub(crate) enum ContextFormat3<'a> {
737    Plain(SequenceContextFormat3<'a>),
738    Chain(ChainedSequenceContextFormat3<'a>),
739}
740
741impl ContextFormat3<'_> {
742    pub(crate) fn coverages(&self) -> ArrayOfOffsets<'_, CoverageTable<'_>> {
743        match self {
744            ContextFormat3::Plain(table) => table.coverages(),
745            ContextFormat3::Chain(table) => table.input_coverages(),
746        }
747    }
748
749    pub(crate) fn lookup_records(&self) -> &[SequenceLookupRecord] {
750        match self {
751            ContextFormat3::Plain(table) => table.seq_lookup_records(),
752            ContextFormat3::Chain(table) => table.seq_lookup_records(),
753        }
754    }
755
756    pub(crate) fn matches_glyphs(&self, glyphs: &IntSet<GlyphId>) -> Result<bool, ReadError> {
757        let (backtrack, lookahead) = match self {
758            Self::Plain(_) => (None, None),
759            Self::Chain(table) => (
760                Some(table.backtrack_coverages()),
761                Some(table.lookahead_coverages()),
762            ),
763        };
764
765        for coverage in self
766            .coverages()
767            .iter()
768            .chain(backtrack.into_iter().flat_map(|x| x.iter()))
769            .chain(lookahead.into_iter().flat_map(|x| x.iter()))
770        {
771            if !coverage?.intersects(glyphs) {
772                return Ok(false);
773            }
774        }
775        Ok(true)
776    }
777}
778
779impl Intersect for ContextFormat1<'_> {
780    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
781        let coverage = self.coverage()?;
782        for rule_set in coverage
783            .iter()
784            .zip(self.rule_sets())
785            .filter_map(|(g, rule_set)| rule_set.filter(|_| glyph_set.contains(GlyphId::from(g))))
786        {
787            for rule in rule_set?.rules() {
788                if rule?.intersects(glyph_set)? {
789                    return Ok(true);
790                }
791            }
792        }
793        Ok(false)
794    }
795}
796
797impl LookupClosure for ContextFormat1<'_> {
798    fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
799        let coverage = self.coverage()?;
800        let glyph_set = c.glyphs();
801
802        let intersected_idxes: IntSet<u16> = coverage
803            .iter()
804            .enumerate()
805            .filter(|&(_, g)| glyph_set.contains(GlyphId::from(g)))
806            .map(|(idx, _)| idx as u16)
807            .collect();
808
809        for rule_set in self.rule_sets().enumerate().filter_map(|(idx, rule_set)| {
810            rule_set.filter(|_| intersected_idxes.contains(idx as u16))
811        }) {
812            if c.lookup_limit_exceed() {
813                return Ok(());
814            }
815            for rule in rule_set?.rules() {
816                rule?.closure_lookups(c, arg)?;
817            }
818        }
819
820        Ok(())
821    }
822}
823
824impl Intersect for ContextFormat2<'_> {
825    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
826        let coverage = self.coverage()?;
827        let retained_coverage_glyphs = coverage.intersect_set(glyph_set);
828        if retained_coverage_glyphs.is_empty() {
829            return Ok(false);
830        }
831
832        let input_class_def = self.input_class_def()?;
833        let coverage_glyph_classes = input_class_def.intersect_classes(&retained_coverage_glyphs);
834        let input_glyph_classes = input_class_def.intersect_classes(glyph_set);
835
836        let backtrack_classes = match self {
837            Self::Plain(_) => IntSet::empty(),
838            Self::Chain(table) => table.backtrack_class_def()?.intersect_classes(glyph_set),
839        };
840
841        let lookahead_classes = match self {
842            Self::Plain(_) => IntSet::empty(),
843            Self::Chain(table) => table.lookahead_class_def()?.intersect_classes(glyph_set),
844        };
845
846        for rule_set in self.rule_sets().enumerate().filter_map(|(c, rule_set)| {
847            coverage_glyph_classes
848                .contains(c as u16)
849                .then_some(rule_set)
850                .flatten()
851        }) {
852            for rule in rule_set?.rules() {
853                if rule?.intersects(&input_glyph_classes, &backtrack_classes, &lookahead_classes) {
854                    return Ok(true);
855                }
856            }
857        }
858        Ok(false)
859    }
860}
861
862impl LookupClosure for ContextFormat2<'_> {
863    fn closure_lookups(&self, c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
864        let glyph_set = c.glyphs();
865        let coverage = self.coverage()?;
866        let retained_coverage_glyphs = coverage.intersect_set(glyph_set);
867        if retained_coverage_glyphs.is_empty() {
868            return Ok(());
869        }
870
871        let input_class_def = self.input_class_def()?;
872        let coverage_glyph_classes = input_class_def.intersect_classes(&retained_coverage_glyphs);
873        let input_glyph_classes = input_class_def.intersect_classes(glyph_set);
874
875        let backtrack_classes = match self {
876            Self::Plain(_) => IntSet::empty(),
877            Self::Chain(table) => table.backtrack_class_def()?.intersect_classes(glyph_set),
878        };
879
880        let lookahead_classes = match self {
881            Self::Plain(_) => IntSet::empty(),
882            Self::Chain(table) => table.lookahead_class_def()?.intersect_classes(glyph_set),
883        };
884
885        for rule_set in self.rule_sets().enumerate().filter_map(|(c, rule_set)| {
886            coverage_glyph_classes
887                .contains(c as u16)
888                .then_some(rule_set)
889                .flatten()
890        }) {
891            if c.lookup_limit_exceed() {
892                return Ok(());
893            }
894
895            for rule in rule_set?.rules() {
896                let rule = rule?;
897                if c.lookup_limit_exceed()
898                    || !rule.intersects(
899                        &input_glyph_classes,
900                        &backtrack_classes,
901                        &lookahead_classes,
902                    )
903                {
904                    return Ok(());
905                }
906
907                for lookup_record in rule.lookup_records() {
908                    let index = lookup_record.lookup_list_index();
909                    c.recurse(index)?;
910                }
911            }
912        }
913        Ok(())
914    }
915}
916
917impl Intersect for ContextFormat3<'_> {
918    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
919        self.matches_glyphs(glyph_set)
920    }
921}
922
923impl LookupClosure for ContextFormat3<'_> {
924    fn closure_lookups(&self, c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
925        if !self.intersects(c.glyphs())? {
926            return Ok(());
927        }
928
929        for lookup_record in self.lookup_records() {
930            let index = lookup_record.lookup_list_index();
931            c.recurse(index)?;
932        }
933
934        Ok(())
935    }
936}
937
938impl Intersect for SequenceContext<'_> {
939    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
940        match self {
941            Self::Format1(table) => ContextFormat1::Plain(table.clone()).intersects(glyph_set),
942            Self::Format2(table) => ContextFormat2::Plain(table.clone()).intersects(glyph_set),
943            Self::Format3(table) => ContextFormat3::Plain(table.clone()).intersects(glyph_set),
944        }
945    }
946}
947
948impl LookupClosure for SequenceContext<'_> {
949    fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
950        match self {
951            Self::Format1(table) => ContextFormat1::Plain(table.clone()).closure_lookups(c, arg),
952            Self::Format2(table) => ContextFormat2::Plain(table.clone()).closure_lookups(c, arg),
953            Self::Format3(table) => ContextFormat3::Plain(table.clone()).closure_lookups(c, arg),
954        }
955    }
956}
957
958impl Intersect for ChainedSequenceContext<'_> {
959    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
960        match self {
961            Self::Format1(table) => ContextFormat1::Chain(table.clone()).intersects(glyph_set),
962            Self::Format2(table) => ContextFormat2::Chain(table.clone()).intersects(glyph_set),
963            Self::Format3(table) => ContextFormat3::Chain(table.clone()).intersects(glyph_set),
964        }
965    }
966}
967
968impl LookupClosure for ChainedSequenceContext<'_> {
969    fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
970        match self {
971            Self::Format1(table) => ContextFormat1::Chain(table.clone()).closure_lookups(c, arg),
972            Self::Format2(table) => ContextFormat2::Chain(table.clone()).closure_lookups(c, arg),
973            Self::Format3(table) => ContextFormat3::Chain(table.clone()).closure_lookups(c, arg),
974        }
975    }
976}