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::{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
28impl PositionLookupList<'_> {
29    pub fn closure_lookups(
30        &self,
31        glyph_set: &IntSet<GlyphId>,
32        lookup_indices: &mut IntSet<u16>,
33    ) -> Result<(), ReadError> {
34        let mut c = LookupClosureCtx::new(glyph_set);
35
36        let lookups = self.lookups();
37        for idx in lookup_indices.iter() {
38            let lookup = lookups.get(idx as usize)?;
39            lookup.closure_lookups(&mut c, idx)?;
40        }
41
42        lookup_indices.union(c.visited_lookups());
43        lookup_indices.subtract(c.inactive_lookups());
44        Ok(())
45    }
46}
47
48impl LookupClosure for PositionLookup<'_> {
49    fn closure_lookups(
50        &self,
51        c: &mut LookupClosureCtx,
52        lookup_index: u16,
53    ) -> Result<(), ReadError> {
54        if !c.should_visit_lookup(lookup_index) {
55            return Ok(());
56        }
57
58        if !self.intersects(c.glyphs())? {
59            c.set_lookup_inactive(lookup_index);
60            return Ok(());
61        }
62
63        let lookup_type = self.lookup_type();
64        self.subtables()?.closure_lookups(c, lookup_type)
65    }
66
67    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
68        self.subtables()?.intersects(glyph_set)
69    }
70}
71
72impl LookupClosure for PositionSubtables<'_> {
73    fn closure_lookups(&self, _c: &mut LookupClosureCtx, _arg: u16) -> Result<(), ReadError> {
74        Ok(())
75    }
76
77    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
78        match self {
79            PositionSubtables::Single(subtables) => subtables.intersects(glyph_set),
80            PositionSubtables::Pair(subtables) => subtables.intersects(glyph_set),
81            PositionSubtables::Cursive(subtables) => subtables.intersects(glyph_set),
82            PositionSubtables::MarkToBase(subtables) => subtables.intersects(glyph_set),
83            PositionSubtables::MarkToLig(subtables) => subtables.intersects(glyph_set),
84            PositionSubtables::MarkToMark(subtables) => subtables.intersects(glyph_set),
85            PositionSubtables::Contextual(subtables) => subtables.intersects(glyph_set),
86            PositionSubtables::ChainContextual(subtables) => subtables.intersects(glyph_set),
87        }
88    }
89}
90
91impl LookupClosure for SinglePos<'_> {
92    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
93        match self {
94            Self::Format1(item) => item.intersects(glyph_set),
95            Self::Format2(item) => item.intersects(glyph_set),
96        }
97    }
98}
99
100impl LookupClosure for SinglePosFormat1<'_> {
101    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
102        Ok(self.coverage()?.intersects(glyph_set))
103    }
104}
105
106impl LookupClosure for SinglePosFormat2<'_> {
107    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
108        Ok(self.coverage()?.intersects(glyph_set))
109    }
110}
111
112impl LookupClosure for PairPos<'_> {
113    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
114        match self {
115            Self::Format1(item) => item.intersects(glyph_set),
116            Self::Format2(item) => item.intersects(glyph_set),
117        }
118    }
119}
120
121impl LookupClosure for PairPosFormat1<'_> {
122    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
123        let coverage = self.coverage()?;
124        let pair_sets = self.pair_sets();
125
126        let num_pair_sets = self.pair_set_count();
127        let num_bits = 16 - num_pair_sets.leading_zeros();
128        if num_pair_sets as u64 > glyph_set.len() * num_bits as u64 {
129            for g in glyph_set.iter() {
130                let Some(i) = coverage.get(g) else {
131                    continue;
132                };
133
134                let pair_set = pair_sets.get(i as usize)?;
135                if pair_set.intersects(glyph_set)? {
136                    return Ok(true);
137                }
138            }
139        } else {
140            for (g, pair_set) in coverage.iter().zip(pair_sets.iter()) {
141                if !glyph_set.contains(GlyphId::from(g)) {
142                    continue;
143                }
144                if pair_set?.intersects(glyph_set)? {
145                    return Ok(true);
146                }
147            }
148        }
149        Ok(false)
150    }
151}
152
153impl LookupClosure for PairSet<'_> {
154    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
155        for record in self.pair_value_records().iter() {
156            let second_glyph = record?.second_glyph();
157            if glyph_set.contains(GlyphId::from(second_glyph)) {
158                return Ok(true);
159            }
160        }
161        Ok(false)
162    }
163}
164
165impl LookupClosure for PairPosFormat2<'_> {
166    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
167        Ok(self.coverage()?.intersects(glyph_set) && self.class_def2()?.intersects(glyph_set)?)
168    }
169}
170
171impl LookupClosure for CursivePosFormat1<'_> {
172    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
173        Ok(self.coverage()?.intersects(glyph_set))
174    }
175}
176
177impl LookupClosure for MarkBasePosFormat1<'_> {
178    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
179        Ok(self.mark_coverage()?.intersects(glyph_set)
180            && self.base_coverage()?.intersects(glyph_set))
181    }
182}
183
184impl LookupClosure for MarkLigPosFormat1<'_> {
185    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
186        Ok(self.mark_coverage()?.intersects(glyph_set)
187            && self.ligature_coverage()?.intersects(glyph_set))
188    }
189}
190
191impl LookupClosure for MarkMarkPosFormat1<'_> {
192    fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
193        Ok(self.mark1_coverage()?.intersects(glyph_set)
194            && self.mark2_coverage()?.intersects(glyph_set))
195    }
196}