read_fonts/tables/gpos/
closure.rs

1//! support closure for GPOS
2
3use super::{
4    CursivePosFormat1, Gpos, MarkBasePosFormat1, MarkLigPosFormat1, MarkMarkPosFormat1, PairPos,
5    PairPosFormat1, PairPosFormat2, PairSet, PositionLookup, PositionLookupList, PositionSubtables,
6    SinglePos, SinglePosFormat1, SinglePosFormat2,
7};
8use crate::{collections::IntSet, GlyphId, ReadError, Tag};
9
10#[cfg(feature = "std")]
11use crate::tables::layout::{Intersect, LayoutLookupList, LookupClosure, LookupClosureCtx};
12
13impl Gpos<'_> {
14    /// Return a set of all feature indices underneath the specified scripts, languages and features
15    pub fn collect_features(
16        &self,
17        scripts: &IntSet<Tag>,
18        languages: &IntSet<Tag>,
19        features: &IntSet<Tag>,
20    ) -> Result<IntSet<u16>, ReadError> {
21        let feature_list = self.feature_list()?;
22        let script_list = self.script_list()?;
23        let head_ptr = self.offset_data().as_bytes().as_ptr() as usize;
24        script_list.collect_features(head_ptr, &feature_list, scripts, languages, features)
25    }
26
27    /// Return a set of lookups referenced by the specified features
28    pub fn collect_lookups(&self, feature_indices: &IntSet<u16>) -> Result<IntSet<u16>, ReadError> {
29        let feature_list = self.feature_list()?;
30        let mut lookup_indices = feature_list.collect_lookups(feature_indices)?;
31
32        if let Some(feature_variations) = self.feature_variations().transpose()? {
33            let subs_lookup_indices = feature_variations.collect_lookups(feature_indices)?;
34            lookup_indices.union(&subs_lookup_indices);
35        }
36        Ok(lookup_indices)
37    }
38
39    /// Update the set of lookup indices with all lookups reachable from specified glyph set and lookup_indices.
40    pub fn closure_lookups(
41        &self,
42        glyphs: &IntSet<GlyphId>,
43        lookup_indices: &mut IntSet<u16>,
44    ) -> Result<(), ReadError> {
45        let lookup_list = self.lookup_list()?;
46        lookup_list.closure_lookups(glyphs, lookup_indices)
47    }
48}
49
50impl PositionLookupList<'_> {
51    pub fn closure_lookups(
52        &self,
53        glyph_set: &IntSet<GlyphId>,
54        lookup_indices: &mut IntSet<u16>,
55    ) -> Result<(), ReadError> {
56        let lookup_list = LayoutLookupList::Gpos(self);
57        let mut c = LookupClosureCtx::new(glyph_set, &lookup_list);
58
59        let lookups = self.lookups();
60        for idx in lookup_indices.iter() {
61            let lookup = lookups.get(idx as usize)?;
62            lookup.closure_lookups(&mut c, idx)?;
63        }
64
65        lookup_indices.union(c.visited_lookups());
66        lookup_indices.subtract(c.inactive_lookups());
67        Ok(())
68    }
69}
70
71impl LookupClosure for PositionLookup<'_> {
72    fn closure_lookups(
73        &self,
74        c: &mut LookupClosureCtx,
75        lookup_index: u16,
76    ) -> Result<(), ReadError> {
77        if !c.should_visit_lookup(lookup_index) {
78            return Ok(());
79        }
80
81        if !self.intersects(c.glyphs())? {
82            c.set_lookup_inactive(lookup_index);
83            return Ok(());
84        }
85
86        self.subtables()?.closure_lookups(c, lookup_index)
87    }
88}
89
90impl Intersect for PositionLookup<'_> {
91    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
92        self.subtables()?.intersects(glyph_set)
93    }
94}
95
96impl LookupClosure for PositionSubtables<'_> {
97    fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
98        match self {
99            PositionSubtables::Contextual(subtables) => subtables.closure_lookups(c, arg),
100            PositionSubtables::ChainContextual(subtables) => subtables.closure_lookups(c, arg),
101            _ => Ok(()),
102        }
103    }
104}
105
106impl Intersect for PositionSubtables<'_> {
107    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
108        match self {
109            PositionSubtables::Single(subtables) => subtables.intersects(glyph_set),
110            PositionSubtables::Pair(subtables) => subtables.intersects(glyph_set),
111            PositionSubtables::Cursive(subtables) => subtables.intersects(glyph_set),
112            PositionSubtables::MarkToBase(subtables) => subtables.intersects(glyph_set),
113            PositionSubtables::MarkToLig(subtables) => subtables.intersects(glyph_set),
114            PositionSubtables::MarkToMark(subtables) => subtables.intersects(glyph_set),
115            PositionSubtables::Contextual(subtables) => subtables.intersects(glyph_set),
116            PositionSubtables::ChainContextual(subtables) => subtables.intersects(glyph_set),
117        }
118    }
119}
120
121impl Intersect for SinglePos<'_> {
122    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
123        match self {
124            Self::Format1(item) => item.intersects(glyph_set),
125            Self::Format2(item) => item.intersects(glyph_set),
126        }
127    }
128}
129
130impl Intersect for SinglePosFormat1<'_> {
131    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
132        Ok(self.coverage()?.intersects(glyph_set))
133    }
134}
135
136impl Intersect for SinglePosFormat2<'_> {
137    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
138        Ok(self.coverage()?.intersects(glyph_set))
139    }
140}
141
142impl Intersect for PairPos<'_> {
143    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
144        match self {
145            Self::Format1(item) => item.intersects(glyph_set),
146            Self::Format2(item) => item.intersects(glyph_set),
147        }
148    }
149}
150
151impl Intersect for PairPosFormat1<'_> {
152    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
153        let coverage = self.coverage()?;
154        let pair_sets = self.pair_sets();
155
156        let num_pair_sets = self.pair_set_count();
157        let num_bits = 16 - num_pair_sets.leading_zeros();
158        if num_pair_sets as u64 > glyph_set.len() * num_bits as u64 {
159            for g in glyph_set.iter() {
160                let Some(i) = coverage.get(g) else {
161                    continue;
162                };
163
164                let pair_set = pair_sets.get(i as usize)?;
165                if pair_set.intersects(glyph_set)? {
166                    return Ok(true);
167                }
168            }
169        } else {
170            for (g, pair_set) in coverage.iter().zip(pair_sets.iter()) {
171                if !glyph_set.contains(GlyphId::from(g)) {
172                    continue;
173                }
174                if pair_set?.intersects(glyph_set)? {
175                    return Ok(true);
176                }
177            }
178        }
179        Ok(false)
180    }
181}
182
183impl Intersect for PairSet<'_> {
184    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
185        for record in self.pair_value_records().iter() {
186            let second_glyph = record?.second_glyph();
187            if glyph_set.contains(GlyphId::from(second_glyph)) {
188                return Ok(true);
189            }
190        }
191        Ok(false)
192    }
193}
194
195impl Intersect for PairPosFormat2<'_> {
196    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
197        Ok(self.coverage()?.intersects(glyph_set) && self.class_def2()?.intersects(glyph_set)?)
198    }
199}
200
201impl Intersect for CursivePosFormat1<'_> {
202    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
203        Ok(self.coverage()?.intersects(glyph_set))
204    }
205}
206
207impl Intersect for MarkBasePosFormat1<'_> {
208    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
209        Ok(self.mark_coverage()?.intersects(glyph_set)
210            && self.base_coverage()?.intersects(glyph_set))
211    }
212}
213
214impl Intersect for MarkLigPosFormat1<'_> {
215    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
216        Ok(self.mark_coverage()?.intersects(glyph_set)
217            && self.ligature_coverage()?.intersects(glyph_set))
218    }
219}
220
221impl Intersect for MarkMarkPosFormat1<'_> {
222    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
223        Ok(self.mark1_coverage()?.intersects(glyph_set)
224            && self.mark2_coverage()?.intersects(glyph_set))
225    }
226}