1use super::{
4 CursivePosFormat1, ExtensionPosFormat1, ExtensionSubtable, Gpos, MarkBasePosFormat1,
5 MarkLigPosFormat1, MarkMarkPosFormat1, PairPos, PairPosFormat1, PairPosFormat2, PairSet,
6 PositionLookup, PositionLookupList, PositionSubtables, SinglePos, SinglePosFormat1,
7 SinglePosFormat2,
8};
9use crate::{collections::IntSet, FontRead, GlyphId, ReadError, Tag};
10
11#[cfg(feature = "std")]
12use crate::tables::layout::{Intersect, LayoutLookupList, LookupClosure, LookupClosureCtx};
13
14impl Gpos<'_> {
15 pub fn collect_features(
17 &self,
18 scripts: &IntSet<Tag>,
19 languages: &IntSet<Tag>,
20 features: &IntSet<Tag>,
21 ) -> Result<IntSet<u16>, ReadError> {
22 if self.script_list_offset().is_null() || self.feature_list_offset().is_null() {
23 return Ok(IntSet::empty());
24 }
25 let feature_list = self.feature_list()?;
26 let script_list = self.script_list()?;
27 let head_ptr = self.offset_data().as_bytes().as_ptr() as usize;
28 script_list.collect_features(head_ptr, &feature_list, scripts, languages, features)
29 }
30
31 pub fn collect_lookups(&self, feature_indices: &IntSet<u16>) -> Result<IntSet<u16>, ReadError> {
33 if self.feature_list_offset().is_null() {
34 return Ok(IntSet::empty());
35 }
36 let feature_list = self.feature_list()?;
37 let mut lookup_indices = feature_list.collect_lookups(feature_indices)?;
38
39 if let Some(feature_variations) = self.feature_variations().transpose()? {
40 let subs_lookup_indices = feature_variations.collect_lookups(feature_indices)?;
41 lookup_indices.union(&subs_lookup_indices);
42 }
43 Ok(lookup_indices)
44 }
45
46 pub fn closure_lookups(
48 &self,
49 glyphs: &IntSet<GlyphId>,
50 lookup_indices: &mut IntSet<u16>,
51 ) -> Result<(), ReadError> {
52 if self.lookup_list_offset().is_null() {
53 return Ok(());
54 }
55 let lookup_list = self.lookup_list()?;
56 lookup_list.closure_lookups(glyphs, lookup_indices)
57 }
58}
59
60impl PositionLookupList<'_> {
61 pub fn closure_lookups(
62 &self,
63 glyph_set: &IntSet<GlyphId>,
64 lookup_indices: &mut IntSet<u16>,
65 ) -> Result<(), ReadError> {
66 lookup_indices.remove_range(self.lookup_count()..=u16::MAX);
67 if lookup_indices.is_empty() {
68 return Ok(());
69 }
70 let lookup_list = LayoutLookupList::Gpos(self);
71 let mut c = LookupClosureCtx::new(glyph_set, &lookup_list);
72
73 let lookups = self.lookups();
74 for idx in lookup_indices.iter() {
75 let lookup = match lookups.get(idx as usize) {
76 Err(ReadError::NullOffset) => {
77 c.set_lookup_inactive(idx);
78 continue;
79 }
80 other => other,
81 }?;
82 lookup.closure_lookups(&mut c, idx)?;
83 }
84
85 lookup_indices.union(c.visited_lookups());
86 lookup_indices.subtract(c.inactive_lookups());
87 Ok(())
88 }
89}
90
91impl LookupClosure for PositionLookup<'_> {
92 fn closure_lookups(
93 &self,
94 c: &mut LookupClosureCtx,
95 lookup_index: u16,
96 ) -> Result<(), ReadError> {
97 if !c.should_visit_lookup(lookup_index) {
98 return Ok(());
99 }
100
101 if !self.intersects(c.glyphs())? {
102 c.set_lookup_inactive(lookup_index);
103 return Ok(());
104 }
105 self.subtables()?.closure_lookups(c, lookup_index)
106 }
107}
108
109impl LookupClosure for PositionSubtables<'_> {
110 fn closure_lookups(&self, c: &mut LookupClosureCtx, arg: u16) -> Result<(), ReadError> {
111 match self {
112 PositionSubtables::Contextual(subtables) => subtables.closure_lookups(c, arg),
113 PositionSubtables::ChainContextual(subtables) => subtables.closure_lookups(c, arg),
114 _ => Ok(()),
115 }
116 }
117}
118
119impl Intersect for PositionLookup<'_> {
120 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
121 match self {
122 PositionLookup::Single(inner) => inner.subtables().intersects(glyph_set),
123 PositionLookup::Pair(inner) => inner.subtables().intersects(glyph_set),
124 PositionLookup::Cursive(inner) => inner.subtables().intersects(glyph_set),
125 PositionLookup::MarkToBase(inner) => inner.subtables().intersects(glyph_set),
126 PositionLookup::MarkToLig(inner) => inner.subtables().intersects(glyph_set),
127 PositionLookup::MarkToMark(inner) => inner.subtables().intersects(glyph_set),
128 PositionLookup::Contextual(inner) => inner.subtables().intersects(glyph_set),
129 PositionLookup::ChainContextual(inner) => inner.subtables().intersects(glyph_set),
130 PositionLookup::Extension(inner) => inner.subtables().intersects(glyph_set),
131 }
132 }
133}
134
135impl Intersect for ExtensionSubtable<'_> {
136 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
137 match self {
138 ExtensionSubtable::Single(inner) => inner.intersects(glyph_set),
139 ExtensionSubtable::Pair(inner) => inner.intersects(glyph_set),
140 ExtensionSubtable::Cursive(inner) => inner.intersects(glyph_set),
141 ExtensionSubtable::MarkToBase(inner) => inner.intersects(glyph_set),
142 ExtensionSubtable::MarkToLig(inner) => inner.intersects(glyph_set),
143 ExtensionSubtable::MarkToMark(inner) => inner.intersects(glyph_set),
144 ExtensionSubtable::Contextual(inner) => inner.intersects(glyph_set),
145 ExtensionSubtable::ChainContextual(inner) => inner.intersects(glyph_set),
146 }
147 }
148}
149
150impl<'a, T> Intersect for ExtensionPosFormat1<'a, T>
151where
152 T: Intersect + FontRead<'a>,
153{
154 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
155 if self.extension_offset().is_null() {
156 return Ok(false);
157 }
158 self.extension()?.intersects(glyph_set)
159 }
160}
161
162impl Intersect for SinglePos<'_> {
163 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
164 match self {
165 Self::Format1(item) => item.intersects(glyph_set),
166 Self::Format2(item) => item.intersects(glyph_set),
167 }
168 }
169}
170
171impl Intersect for SinglePosFormat1<'_> {
172 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
173 if self.coverage_offset().is_null() {
174 return Ok(false);
175 }
176 Ok(self.coverage()?.intersects(glyph_set))
177 }
178}
179
180impl Intersect for SinglePosFormat2<'_> {
181 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
182 if self.coverage_offset().is_null() {
183 return Ok(false);
184 }
185 Ok(self.coverage()?.intersects(glyph_set))
186 }
187}
188
189impl Intersect for PairPos<'_> {
190 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
191 match self {
192 Self::Format1(item) => item.intersects(glyph_set),
193 Self::Format2(item) => item.intersects(glyph_set),
194 }
195 }
196}
197
198impl Intersect for PairPosFormat1<'_> {
199 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
200 if self.coverage_offset().is_null() {
201 return Ok(false);
202 }
203 let coverage = self.coverage()?;
204 let pair_sets = self.pair_sets();
205
206 let num_pair_sets = self.pair_set_count();
207 let num_bits = 16 - num_pair_sets.leading_zeros();
208 if num_pair_sets as u64 > glyph_set.len() * num_bits as u64 {
209 for g in glyph_set.iter() {
210 let Some(i) = coverage.get(g) else {
211 continue;
212 };
213 let pair_set = match pair_sets.get(i as usize) {
214 Err(ReadError::NullOffset) => continue,
215 other => other,
216 }?;
217 if pair_set.intersects(glyph_set)? {
218 return Ok(true);
219 }
220 }
221 } else {
222 for (g, pair_set) in coverage.iter().zip(pair_sets.iter_as_nullable()) {
223 if !glyph_set.contains(GlyphId::from(g)) {
224 continue;
225 }
226 let Some(pair_set) = pair_set.transpose()? else {
227 continue;
228 };
229 if pair_set.intersects(glyph_set)? {
230 return Ok(true);
231 }
232 }
233 }
234 Ok(false)
235 }
236}
237
238impl Intersect for PairSet<'_> {
239 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
240 for record in self.pair_value_records().iter() {
241 let second_glyph = record?.second_glyph();
242 if glyph_set.contains(GlyphId::from(second_glyph)) {
243 return Ok(true);
244 }
245 }
246 Ok(false)
247 }
248}
249
250impl Intersect for PairPosFormat2<'_> {
251 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
252 if self.coverage_offset().is_null()
253 || self.class_def1_offset().is_null()
254 || self.class_def2_offset().is_null()
255 {
256 return Ok(false);
257 }
258 Ok(self.coverage()?.intersects(glyph_set) && self.class_def2()?.intersects(glyph_set)?)
259 }
260}
261
262impl Intersect for CursivePosFormat1<'_> {
263 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
264 if self.coverage_offset().is_null() {
265 return Ok(false);
266 }
267 Ok(self.coverage()?.intersects(glyph_set))
268 }
269}
270
271impl Intersect for MarkBasePosFormat1<'_> {
272 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
273 if self.mark_coverage_offset().is_null() || self.base_coverage_offset().is_null() {
274 return Ok(false);
275 }
276 Ok(self.mark_coverage()?.intersects(glyph_set)
277 && self.base_coverage()?.intersects(glyph_set))
278 }
279}
280
281impl Intersect for MarkLigPosFormat1<'_> {
282 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
283 if self.mark_coverage_offset().is_null() || self.ligature_coverage_offset().is_null() {
284 return Ok(false);
285 }
286 Ok(self.mark_coverage()?.intersects(glyph_set)
287 && self.ligature_coverage()?.intersects(glyph_set))
288 }
289}
290
291impl Intersect for MarkMarkPosFormat1<'_> {
292 fn intersects(&self, glyph_set: &IntSet<GlyphId>) -> Result<bool, ReadError> {
293 if self.mark1_coverage_offset().is_null() || self.mark2_coverage_offset().is_null() {
294 return Ok(false);
295 }
296 Ok(self.mark1_coverage()?.intersects(glyph_set)
297 && self.mark2_coverage()?.intersects(glyph_set))
298 }
299}