1use super::{coverage_binary_cached, coverage_index, covered, glyph_class};
2use crate::hb::buffer::GlyphInfo;
3use crate::hb::ot::{ClassDefInfo, CoverageInfo};
4use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t;
5use crate::hb::ot_layout_gsubgpos::{
6 apply_lookup, match_always, match_backtrack, match_glyph, match_input, match_lookahead,
7 may_skip_t, skipping_iterator_t, Apply, BinaryCache, ChainContextFormat2Cache,
8 ContextFormat2Cache, SubtableExternalCache, SubtableExternalCacheMode, WouldApply,
9 WouldApplyContext,
10};
11use read_fonts::tables::gsub::ClassDef;
12use read_fonts::tables::layout::{
13 ChainedClassSequenceRule, ChainedSequenceContextFormat1, ChainedSequenceContextFormat2,
14 ChainedSequenceContextFormat3, ChainedSequenceRule, ClassSequenceRule, SequenceContextFormat1,
15 SequenceContextFormat2, SequenceContextFormat3, SequenceLookupRecord, SequenceRule,
16};
17use read_fonts::types::{BigEndian, FixedSize, GlyphId, Offset16};
18use read_fonts::{ArrayOfOffsets, FontData, FontRead};
19
20impl WouldApply for SequenceContextFormat1<'_> {
21 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
22 coverage_index(self.coverage(), ctx.glyphs[0])
23 .and_then(|index| {
24 self.seq_rule_sets()
25 .get(index as usize)
26 .transpose()
27 .ok()
28 .flatten()
29 })
30 .is_some_and(|set| {
31 set.seq_rules().iter().any(|rule| {
32 rule.is_ok_and(|rule| {
33 let input = rule.input_sequence();
34 ctx.glyphs.len() == input.len() + 1
35 && input.iter().enumerate().all(|(i, value)| {
36 let mut info = GlyphInfo {
37 glyph_id: ctx.glyphs[i + 1].into(),
38 ..GlyphInfo::default()
39 };
40 match_glyph(&mut info, value.get().to_u32())
41 })
42 })
43 })
44 })
45 }
46}
47
48impl Apply for SequenceContextFormat1<'_> {
49 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
50 let glyph = ctx.buffer.cur(0).as_glyph();
51 let index = self.coverage().ok()?.get(glyph)? as usize;
52 let set = self.seq_rule_sets().get(index)?.ok()?;
53 apply_context_rules(ctx, &set.seq_rules(), match_glyph)
54 }
55}
56
57impl WouldApply for SequenceContextFormat2<'_> {
58 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
59 let class_def = self.class_def().ok();
60 let match_fn = &match_class(&class_def);
61 let class = glyph_class(self.class_def(), ctx.glyphs[0]);
62 self.class_seq_rule_sets()
63 .get(class as usize)
64 .transpose()
65 .ok()
66 .flatten()
67 .is_some_and(|set| {
68 set.class_seq_rules().iter().any(|rule| {
69 rule.is_ok_and(|rule| {
70 let input = rule.input_sequence();
71 ctx.glyphs.len() == input.len() + 1
72 && input.iter().enumerate().all(|(i, value)| {
73 let mut info = GlyphInfo {
74 glyph_id: ctx.glyphs[i + 1].into(),
75 ..GlyphInfo::default()
76 };
77 match_fn(&mut info, value.get() as u32)
78 })
79 })
80 })
81 })
82 }
83}
84
85impl Apply for SequenceContextFormat2<'_> {
86 fn apply_with_external_cache(
87 &self,
88 ctx: &mut hb_ot_apply_context_t,
89 external_cache: &SubtableExternalCache,
90 ) -> Option<()> {
91 let glyph = ctx.buffer.cur(0).as_glyph();
92 let SubtableExternalCache::ContextFormat2Cache(cache) = external_cache else {
93 return None;
94 };
95 let offset_data = self.offset_data();
96 coverage_binary_cached(
97 |gid| cache.coverage.index(&offset_data, gid),
98 glyph,
99 &cache.coverage_cache,
100 )?;
101 let input_class = |gid| cache.input.class(&offset_data, gid);
102 let index = input_class(glyph) as usize;
103 let set = self.class_seq_rule_sets().get(index)?.ok()?;
104 apply_context_rules(ctx, &set.class_seq_rules(), |info, value| {
105 u32::from(input_class(info.as_glyph())) == value
106 })
107 }
108
109 fn apply_cached(
110 &self,
111 ctx: &mut hb_ot_apply_context_t,
112 external_cache: &SubtableExternalCache,
113 ) -> Option<()> {
114 let glyph = ctx.buffer.cur(0).as_glyph();
115 let SubtableExternalCache::ContextFormat2Cache(cache) = external_cache else {
116 return None;
117 };
118 let offset_data = self.offset_data();
119 coverage_binary_cached(
120 |gid| cache.coverage.index(&offset_data, gid),
121 glyph,
122 &cache.coverage_cache,
123 )?;
124 let input_class = |gid| cache.input.class(&offset_data, gid);
125 let index = get_class_cached(&input_class, &mut ctx.buffer.info[ctx.buffer.idx]) as usize;
126 let set = self.class_seq_rule_sets().get(index)?.ok()?;
127 apply_context_rules(
128 ctx,
129 &set.class_seq_rules(),
130 match_class_cached(&input_class),
131 )
132 }
133
134 fn cache_cost(&self) -> u32 {
135 self.class_def()
136 .ok()
137 .map_or(0, |class_def| class_def.cost())
138 }
139
140 fn external_cache_create(&self, _mode: SubtableExternalCacheMode) -> SubtableExternalCache {
141 let data = self.offset_data();
142 SubtableExternalCache::ContextFormat2Cache(ContextFormat2Cache {
143 coverage_cache: BinaryCache::new(),
144 coverage: CoverageInfo::new(&data, self.coverage_offset().to_u32() as u16)
145 .unwrap_or_default(),
146 input: ClassDefInfo::new(&data, self.class_def_offset().to_u32() as u16)
147 .unwrap_or_default(),
148 })
149 }
150}
151
152impl WouldApply for SequenceContextFormat3<'_> {
153 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
154 let coverages = self.coverages();
155 ctx.glyphs.len() == coverages.len() + 1
156 && coverages
157 .iter()
158 .enumerate()
159 .all(|(i, coverage)| covered(coverage, ctx.glyphs[i + 1]))
160 }
161}
162
163impl Apply for SequenceContextFormat3<'_> {
164 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
165 let glyph = ctx.buffer.cur(0).as_glyph();
166 let input_coverages = self.coverages();
167 input_coverages.get(0).ok()?.get(glyph)?;
168 let input = |info: &mut GlyphInfo, index: u32| {
169 input_coverages
170 .get(index as usize + 1)
171 .is_ok_and(|cov| cov.get(info.glyph_id).is_some())
172 };
173 let mut match_end = 0;
174 if match_input(
175 ctx,
176 input_coverages.len() as u16 - 1,
177 input,
178 &mut match_end,
179 None,
180 ) {
181 ctx.buffer
182 .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end));
183 apply_lookup(
184 ctx,
185 input_coverages.len() - 1,
186 match_end,
187 self.seq_lookup_records(),
188 );
189 Some(())
190 } else {
191 ctx.buffer
192 .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end));
193 None
194 }
195 }
196}
197
198impl WouldApply for ChainedSequenceContextFormat1<'_> {
199 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
200 coverage_index(self.coverage(), ctx.glyphs[0])
201 .and_then(|index| {
202 self.chained_seq_rule_sets()
203 .get(index as usize)
204 .transpose()
205 .ok()
206 .flatten()
207 })
208 .is_some_and(|set| {
209 set.chained_seq_rules().iter().any(|rule| {
210 rule.is_ok_and(|rule| {
211 let input = rule.input_sequence();
212 (!ctx.zero_context
213 || (rule.backtrack_glyph_count() == 0
214 && rule.lookahead_glyph_count() == 0))
215 && ctx.glyphs.len() == input.len() + 1
216 && input.iter().enumerate().all(|(i, value)| {
217 let mut info = GlyphInfo {
218 glyph_id: ctx.glyphs[i + 1].into(),
219 ..GlyphInfo::default()
220 };
221 match_glyph(&mut info, value.get().to_u32())
222 })
223 })
224 })
225 })
226 }
227}
228
229impl Apply for ChainedSequenceContextFormat1<'_> {
230 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
231 let glyph = ctx.buffer.cur(0).as_glyph();
232 let index = self.coverage().ok()?.get(glyph)? as usize;
233 let set = self.chained_seq_rule_sets().get(index)?.ok()?;
234 apply_chain_context_rules(
235 ctx,
236 &set.chained_seq_rules(),
237 (match_glyph, match_glyph, match_glyph),
238 )
239 }
240}
241
242impl WouldApply for ChainedSequenceContextFormat2<'_> {
243 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
244 let class_def = self.input_class_def().ok();
245 let match_fn = &match_class(&class_def);
246 let class = glyph_class(self.input_class_def(), ctx.glyphs[0]);
247 self.chained_class_seq_rule_sets()
248 .get(class as usize)
249 .transpose()
250 .ok()
251 .flatten()
252 .is_some_and(|set| {
253 set.chained_class_seq_rules().iter().any(|rule| {
254 rule.is_ok_and(|rule| {
255 let input = rule.input_sequence();
256 (!ctx.zero_context
257 || (rule.backtrack_glyph_count() == 0
258 && rule.lookahead_glyph_count() == 0))
259 && ctx.glyphs.len() == input.len() + 1
260 && input.iter().enumerate().all(|(i, value)| {
261 let mut info = GlyphInfo {
262 glyph_id: ctx.glyphs[i + 1].into(),
263 ..GlyphInfo::default()
264 };
265 match_fn(&mut info, value.get() as u32)
266 })
267 })
268 })
269 })
270 }
271}
272
273fn match_class<'a>(
275 class_def: &'a Option<ClassDef<'a>>,
276) -> impl Fn(&mut GlyphInfo, u32) -> bool + 'a {
277 |&mut info, value| {
278 class_def
279 .as_ref()
280 .is_some_and(|class_def| u32::from(class_def.get(info.as_glyph())) == value)
281 }
282}
283
284fn get_class_cached(class_def: &impl Fn(GlyphId) -> u16, info: &mut GlyphInfo) -> u16 {
285 let mut klass = info.syllable() as u16;
286 if klass < 255 {
287 return klass;
288 }
289 klass = class_def(info.as_glyph());
290 if klass < 255 {
291 info.set_syllable(klass as u8);
292 }
293
294 klass
295}
296
297fn match_class_cached<'a>(
298 class_def: impl Fn(GlyphId) -> u16 + 'a,
299) -> impl Fn(&mut GlyphInfo, u32) -> bool + 'a {
300 move |info: &mut GlyphInfo, value| u32::from(get_class_cached(&class_def, info)) == value
301}
302
303fn get_class_cached1(class_def: &impl Fn(GlyphId) -> u16, info: &mut GlyphInfo) -> u16 {
304 let mut klass = (info.syllable() & 0x0F) as u16;
305 if klass < 15 {
306 return klass;
307 }
308
309 klass = class_def(info.as_glyph());
310
311 if klass < 15 {
312 info.set_syllable((info.syllable() & 0xF0) | klass as u8);
313 }
314
315 klass
316}
317
318fn match_class_cached1<'a>(
319 class_def: impl Fn(GlyphId) -> u16 + 'a,
320) -> impl Fn(&mut GlyphInfo, u32) -> bool + 'a {
321 move |info: &mut GlyphInfo, value| u32::from(get_class_cached1(&class_def, info)) == value
322}
323
324fn get_class_cached2(class_def: &impl Fn(GlyphId) -> u16, info: &mut GlyphInfo) -> u16 {
325 let mut klass = (info.syllable() & 0xF0) as u16 >> 4;
326 if klass < 15 {
327 return klass;
328 }
329 klass = class_def(info.as_glyph());
330 if klass < 15 {
331 info.set_syllable((info.syllable() & 0x0F) | ((klass as u8) << 4));
332 }
333 klass
334}
335
336fn match_class_cached2<'a>(
337 class_def: impl Fn(GlyphId) -> u16 + 'a,
338) -> impl Fn(&mut GlyphInfo, u32) -> bool + 'a {
339 move |info: &mut GlyphInfo, value| u32::from(get_class_cached2(&class_def, info)) == value
340}
341
342impl Apply for ChainedSequenceContextFormat2<'_> {
343 fn apply_with_external_cache(
344 &self,
345 ctx: &mut hb_ot_apply_context_t,
346 external_cache: &SubtableExternalCache,
347 ) -> Option<()> {
348 let glyph = ctx.buffer.cur(0).as_glyph();
349 let SubtableExternalCache::ChainContextFormat2Cache(cache) = external_cache else {
350 return None;
351 };
352 let offset_data = self.offset_data();
353 coverage_binary_cached(
354 |gid| cache.coverage.index(&offset_data, gid),
355 glyph,
356 &cache.coverage_cache,
357 )?;
358 let index = cache.input.class(&offset_data, glyph) as usize;
359 let set = self.chained_class_seq_rule_sets().get(index)?.ok()?;
360 apply_chain_context_rules(
361 ctx,
362 &set.chained_class_seq_rules(),
363 (
364 |info, val| u32::from(cache.backtrack.class(&offset_data, info.as_glyph())) == val,
365 |info, val| u32::from(cache.input.class(&offset_data, info.as_glyph())) == val,
366 |info, val| u32::from(cache.lookahead.class(&offset_data, info.as_glyph())) == val,
367 ),
368 )
369 }
370 fn apply_cached(
371 &self,
372 ctx: &mut hb_ot_apply_context_t,
373 external_cache: &SubtableExternalCache,
374 ) -> Option<()> {
375 let glyph = ctx.buffer.cur(0).as_glyph();
376 let SubtableExternalCache::ChainContextFormat2Cache(cache) = external_cache else {
377 return None;
378 };
379 let offset_data = self.offset_data();
380 coverage_binary_cached(
381 |gid| cache.coverage.index(&offset_data, gid),
382 glyph,
383 &cache.coverage_cache,
384 )?;
385 let input_class = |gid| cache.input.class(&offset_data, gid);
386 let lookahead_class = |gid| cache.lookahead.class(&offset_data, gid);
387 let index = get_class_cached2(&input_class, &mut ctx.buffer.info[ctx.buffer.idx]) as usize;
388 let set = self.chained_class_seq_rule_sets().get(index)?.ok()?;
389 apply_chain_context_rules(
390 ctx,
391 &set.chained_class_seq_rules(),
392 (
393 |info, val| u32::from(cache.backtrack.class(&offset_data, info.as_glyph())) == val,
394 match_class_cached2(&input_class),
395 match_class_cached1(&lookahead_class),
396 ),
397 )
398 }
399 fn cache_cost(&self) -> u32 {
400 self.input_class_def()
401 .ok()
402 .map_or(0, |class_def| class_def.cost())
403 + self
404 .lookahead_class_def()
405 .ok()
406 .map_or(0, |class_def| class_def.cost())
407 }
408
409 fn external_cache_create(&self, _mode: SubtableExternalCacheMode) -> SubtableExternalCache {
410 let data = self.offset_data();
411 SubtableExternalCache::ChainContextFormat2Cache(ChainContextFormat2Cache {
412 coverage_cache: BinaryCache::new(),
413 coverage: CoverageInfo::new(&data, self.coverage_offset().to_u32() as u16)
414 .unwrap_or_default(),
415 backtrack: ClassDefInfo::new(&data, self.backtrack_class_def_offset().to_u32() as u16)
416 .unwrap_or_default(),
417 input: ClassDefInfo::new(&data, self.input_class_def_offset().to_u32() as u16)
418 .unwrap_or_default(),
419 lookahead: ClassDefInfo::new(&data, self.lookahead_class_def_offset().to_u32() as u16)
420 .unwrap_or_default(),
421 })
422 }
423}
424
425impl WouldApply for ChainedSequenceContextFormat3<'_> {
426 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
427 let input_coverages = self.input_coverages();
428 (!ctx.zero_context
429 || (self.backtrack_coverage_offsets().is_empty()
430 && self.lookahead_coverage_offsets().is_empty()))
431 && (ctx.glyphs.len() == input_coverages.len()
432 && input_coverages
433 .iter()
434 .skip(1)
435 .enumerate()
436 .all(|(i, coverage)| {
437 coverage.is_ok_and(|cov| cov.get(ctx.glyphs[i + 1]).is_some())
438 }))
439 }
440}
441
442impl Apply for ChainedSequenceContextFormat3<'_> {
443 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
444 let glyph = ctx.buffer.cur(0).as_glyph();
445
446 let input_coverages = self.input_coverages();
447 input_coverages.get(0).ok()?.get(glyph)?;
448
449 let backtrack_coverages = self.backtrack_coverages();
450 let lookahead_coverages = self.lookahead_coverages();
451
452 let back = |info: &mut GlyphInfo, index: u32| {
453 backtrack_coverages
454 .get(index as usize)
455 .is_ok_and(|cov| cov.get(info.glyph_id).is_some())
456 };
457
458 let ahead = |info: &mut GlyphInfo, index: u32| {
459 lookahead_coverages
460 .get(index as usize)
461 .is_ok_and(|cov| cov.get(info.glyph_id).is_some())
462 };
463
464 let input = |info: &mut GlyphInfo, index: u32| {
465 input_coverages
466 .get(index as usize + 1)
467 .is_ok_and(|cov| cov.get(info.glyph_id).is_some())
468 };
469
470 let mut end_index = ctx.buffer.idx;
471 let mut match_end = 0;
472
473 let input_matches = match_input(
474 ctx,
475 input_coverages.len() as u16 - 1,
476 input,
477 &mut match_end,
478 None,
479 );
480
481 if input_matches {
482 end_index = match_end;
483 }
484
485 if !(input_matches
486 && match_lookahead(
487 ctx,
488 lookahead_coverages.len() as u16,
489 ahead,
490 match_end,
491 &mut end_index,
492 ))
493 {
494 ctx.buffer
495 .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index));
496 return None;
497 }
498
499 let mut start_index = ctx.buffer.out_len;
500
501 if !match_backtrack(
502 ctx,
503 backtrack_coverages.len() as u16,
504 back,
505 &mut start_index,
506 ) {
507 ctx.buffer
508 .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index));
509 return None;
510 }
511
512 ctx.buffer
513 .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index));
514 apply_lookup(
515 ctx,
516 input_coverages.len() - 1,
517 match_end,
518 self.seq_lookup_records(),
519 );
520
521 Some(())
522 }
523}
524
525#[derive(Clone, Copy, Default)]
532struct ParsedRule<'a> {
533 backtrack: &'a [BigEndian<u16>],
534 input: &'a [BigEndian<u16>],
535 lookahead: &'a [BigEndian<u16>],
536 records: &'a [SequenceLookupRecord],
537}
538
539impl<'a> ParsedRule<'a> {
540 fn from_rule_data(data: FontData<'a>) -> Option<Self> {
542 let glyph_count: u16 = data.read_at(0).ok()?;
543 let record_count: u16 = data.read_at(2).ok()?;
544 let input_end = 4 + (glyph_count as usize).saturating_sub(1) * u16::RAW_BYTE_LEN;
545 let records_end = input_end + record_count as usize * SequenceLookupRecord::RAW_BYTE_LEN;
546 Some(ParsedRule {
547 input: data.read_array(4..input_end).ok()?,
548 records: data.read_array(input_end..records_end).ok()?,
549 ..ParsedRule::default()
550 })
551 }
552
553 fn from_chain_rule_data(data: FontData<'a>) -> Option<Self> {
555 let backtrack_count: u16 = data.read_at(0).ok()?;
556 let backtrack_end = 2 + backtrack_count as usize * u16::RAW_BYTE_LEN;
557 let input_count: u16 = data.read_at(backtrack_end).ok()?;
558 let input_end =
559 backtrack_end + 2 + (input_count as usize).saturating_sub(1) * u16::RAW_BYTE_LEN;
560 let lookahead_count: u16 = data.read_at(input_end).ok()?;
561 let lookahead_end = input_end + 2 + lookahead_count as usize * u16::RAW_BYTE_LEN;
562 let record_count: u16 = data.read_at(lookahead_end).ok()?;
563 let records_end =
564 lookahead_end + 2 + record_count as usize * SequenceLookupRecord::RAW_BYTE_LEN;
565 Some(ParsedRule {
566 backtrack: data.read_array(2..backtrack_end).ok()?,
567 input: data.read_array(backtrack_end + 2..input_end).ok()?,
568 lookahead: data.read_array(input_end + 2..lookahead_end).ok()?,
569 records: data.read_array(lookahead_end + 2..records_end).ok()?,
570 })
571 }
572
573 fn apply(
578 &self,
579 ctx: &mut hb_ot_apply_context_t,
580 match_func: &impl Fn(&mut GlyphInfo, u32) -> bool,
581 ) -> Option<()> {
582 let inputs = self.input;
583 let match_func = |info: &mut GlyphInfo, index| {
584 inputs
585 .get(index as usize)
586 .is_some_and(|value| match_func(info, value.get() as u32))
587 };
588
589 let mut match_end = 0;
590
591 if match_input(ctx, inputs.len() as _, match_func, &mut match_end, None) {
592 ctx.buffer
593 .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end));
594 apply_lookup(ctx, inputs.len(), match_end, self.records);
595 return Some(());
596 }
597 None
598 }
599}
600
601trait ContextRule<'a>: FontRead<'a> {
602 fn parse(&self) -> ParsedRule<'a>;
604
605 fn first_input(&self) -> Option<u16>;
611}
612
613fn plain_rule_first_input(data: &FontData) -> Option<u16> {
619 let glyph_count: u16 = data.read_at(0).ok()?;
620 if glyph_count <= 1 {
621 return None;
622 }
623 data.read_at(4).ok()
624}
625
626fn chain_rule_first_input(data: &FontData) -> Option<u16> {
631 let backtrack_count: u16 = data.read_at(0).ok()?;
632 let count_pos = 2 + backtrack_count as usize * u16::RAW_BYTE_LEN;
633 let input_count: u16 = data.read_at(count_pos).ok()?;
634 if input_count <= 1 {
635 return None;
636 }
637 data.read_at(count_pos + 2).ok()
638}
639
640impl<'a> ContextRule<'a> for SequenceRule<'a> {
641 fn parse(&self) -> ParsedRule<'a> {
642 ParsedRule::from_rule_data(self.offset_data()).unwrap_or_default()
643 }
644
645 fn first_input(&self) -> Option<u16> {
646 plain_rule_first_input(&self.offset_data())
647 }
648}
649
650impl<'a> ContextRule<'a> for ClassSequenceRule<'a> {
651 fn parse(&self) -> ParsedRule<'a> {
652 ParsedRule::from_rule_data(self.offset_data()).unwrap_or_default()
653 }
654
655 fn first_input(&self) -> Option<u16> {
656 plain_rule_first_input(&self.offset_data())
657 }
658}
659
660impl<'a> ContextRule<'a> for ChainedSequenceRule<'a> {
661 fn parse(&self) -> ParsedRule<'a> {
662 ParsedRule::from_chain_rule_data(self.offset_data()).unwrap_or_default()
663 }
664
665 fn first_input(&self) -> Option<u16> {
666 chain_rule_first_input(&self.offset_data())
667 }
668}
669
670impl<'a> ContextRule<'a> for ChainedClassSequenceRule<'a> {
671 fn parse(&self) -> ParsedRule<'a> {
672 ParsedRule::from_chain_rule_data(self.offset_data()).unwrap_or_default()
673 }
674
675 fn first_input(&self) -> Option<u16> {
676 chain_rule_first_input(&self.offset_data())
677 }
678}
679
680fn apply_context_rules<'a, 'b, R: ContextRule<'a>>(
681 ctx: &mut hb_ot_apply_context_t,
682 rules: &'b ArrayOfOffsets<'a, R, Offset16>,
683 match_func: impl Fn(&mut GlyphInfo, u32) -> bool,
684) -> Option<()> {
685 if false {
690 for rule in rules.iter().filter_map(|r| r.ok()) {
691 if rule.parse().apply(ctx, &match_func).is_some() {
692 return Some(());
693 }
694 }
695 return None;
696 }
697 let mut skippy_iter = skipping_iterator_t::with_match_fn(ctx, true, Some(match_always));
705 skippy_iter.reset(skippy_iter.buffer.idx);
706 skippy_iter.set_glyph_data(0);
707 let mut unsafe_to = None;
708 let unsafe_to1;
709 let mut unsafe_to2 = 0;
710 let mut second = None;
711 let first = if skippy_iter.next(None) {
712 let g1 = skippy_iter.index();
713 if skippy_iter.may_skip(&skippy_iter.buffer.info[g1]) != may_skip_t::SKIP_NO {
714 for rule in rules.iter().filter_map(|r| r.ok()) {
717 if rule.parse().apply(ctx, &match_func).is_some() {
718 return Some(());
719 }
720 }
721 return None;
722 }
723 unsafe_to1 = skippy_iter.index() + 1;
724 g1
725 } else {
726 for rule in rules
729 .iter()
730 .filter_map(|r| r.ok().map(|r| r.parse()))
731 .filter(|r| r.input.len() <= 1)
732 {
733 if rule.apply(ctx, &match_func).is_some() {
734 return Some(());
735 }
736 }
737 return None;
738 };
739 let matched = skippy_iter.next(None);
740 let g2 = skippy_iter.index();
741 if matched {
742 second = Some(g2);
743 unsafe_to2 = skippy_iter.index() + 1;
744 if skippy_iter.may_skip(&skippy_iter.buffer.info[g2]) != may_skip_t::SKIP_NO {
745 for rule in rules.iter().filter_map(|r| r.ok()) {
748 if rule.parse().apply(ctx, &match_func).is_some() {
749 return Some(());
750 }
751 }
752 return None;
753 }
754 }
755 let mut rules_iter = rules.iter().filter_map(|r| r.ok());
756 let mut rule_box = rules_iter.next().map(|r| r.parse());
757 while let Some(rule) = rule_box {
758 let inputs = rule.input;
759 let match_func2 = |info: &mut GlyphInfo, index| {
760 if let Some(value) = inputs.get(index as usize).map(|v| v.get()) {
761 match_func(info, u32::from(value))
762 } else {
763 false
764 }
765 };
766 if inputs.len() <= 1 || match_func2(&mut ctx.buffer.info[first], 0) {
767 if second.is_none()
768 || (inputs.len() <= 2 || match_func2(&mut ctx.buffer.info[second.unwrap()], 1))
769 {
770 if rule.apply(ctx, &match_func).is_some() {
771 if let Some(unsafe_to) = unsafe_to {
772 ctx.buffer
773 .unsafe_to_concat(Some(ctx.buffer.idx), Some(unsafe_to));
774 }
775 return Some(());
776 }
777 } else {
778 unsafe_to = Some(unsafe_to2);
779 }
780 rule_box = rules_iter.next().map(|r| r.parse());
781 } else {
782 if unsafe_to.is_none() {
783 unsafe_to = Some(unsafe_to1);
784 }
785
786 let first_glyph_value = inputs.first().unwrap().get();
788 loop {
789 let Some(next_rule) = rules_iter.next() else {
790 rule_box = None;
791 break;
792 };
793 if next_rule.first_input() != Some(first_glyph_value) {
794 rule_box = Some(next_rule.parse());
795 break;
796 }
797 }
798 }
799 }
800 if let Some(unsafe_to) = unsafe_to {
801 ctx.buffer
802 .unsafe_to_concat(Some(ctx.buffer.idx), Some(unsafe_to));
803 }
804 None
805}
806
807fn apply_chain_with_sequences<
808 F1: Fn(&mut GlyphInfo, u32) -> bool,
809 F2: Fn(&mut GlyphInfo, u32) -> bool,
810 F3: Fn(&mut GlyphInfo, u32) -> bool,
811>(
812 ctx: &mut hb_ot_apply_context_t,
813 rule: &ParsedRule<'_>,
814 match_funcs: &(F1, F2, F3),
815) -> Option<()> {
816 let input = rule.input;
817 let f3 = |info: &mut GlyphInfo, index| {
818 input
819 .get(index as usize)
820 .is_some_and(|value| match_funcs.1(info, value.get() as u32))
821 };
822
823 let mut end_index = ctx.buffer.idx;
824 let mut match_end = 0;
825
826 let input_matches = match_input(ctx, input.len() as u16, f3, &mut match_end, None);
827
828 if input_matches {
829 end_index = match_end;
830 } else {
831 ctx.buffer
832 .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index));
833 return None;
834 }
835
836 let lookahead = rule.lookahead;
837 let f2 = |info: &mut GlyphInfo, index| {
838 lookahead
839 .get(index as usize)
840 .is_some_and(|value| match_funcs.2(info, value.get() as u32))
841 };
842
843 if !match_lookahead(ctx, lookahead.len() as u16, f2, match_end, &mut end_index) {
844 ctx.buffer
845 .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index));
846 return None;
847 }
848
849 let mut start_index = ctx.buffer.out_len;
850
851 let backtrack = rule.backtrack;
852 let f1 = |info: &mut GlyphInfo, index| {
853 backtrack
854 .get(index as usize)
855 .is_some_and(|value| match_funcs.0(info, value.get() as u32))
856 };
857
858 if !match_backtrack(ctx, backtrack.len() as u16, f1, &mut start_index) {
859 ctx.buffer
860 .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index));
861 return None;
862 }
863
864 ctx.buffer
865 .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index));
866 apply_lookup(ctx, input.len(), match_end, rule.records);
867
868 Some(())
869}
870
871fn apply_chain_context_rules<
872 'a,
873 'b,
874 R: ContextRule<'a>,
875 F1: Fn(&mut GlyphInfo, u32) -> bool,
876 F2: Fn(&mut GlyphInfo, u32) -> bool,
877 F3: Fn(&mut GlyphInfo, u32) -> bool,
878>(
879 ctx: &mut hb_ot_apply_context_t,
880 rules: &'b ArrayOfOffsets<'a, R, Offset16>,
881 match_funcs: (F1, F2, F3),
882) -> Option<()> {
883 if rules.len() <= 4 {
884 for rule in rules.iter().filter_map(|r| r.ok()) {
885 if apply_chain_with_sequences(ctx, &rule.parse(), &match_funcs).is_some() {
886 return Some(());
887 }
888 }
889 return None;
890 }
891 let mut skippy_iter = skipping_iterator_t::with_match_fn(ctx, true, Some(match_always));
899 skippy_iter.reset(skippy_iter.buffer.idx);
900 skippy_iter.set_glyph_data(0);
901 let mut unsafe_to = None;
902 let unsafe_to1;
903 let mut unsafe_to2 = 0;
904 let mut second = None;
905 let first = if skippy_iter.next(None) {
906 let g1 = skippy_iter.index();
907 if skippy_iter.may_skip(&skippy_iter.buffer.info[g1]) != may_skip_t::SKIP_NO {
908 for rule in rules.iter().filter_map(|r| r.ok()) {
911 if apply_chain_with_sequences(ctx, &rule.parse(), &match_funcs).is_some() {
912 return Some(());
913 }
914 }
915 return None;
916 }
917 unsafe_to1 = skippy_iter.index() + 1;
918 g1
919 } else {
920 for rule in rules
923 .iter()
924 .filter_map(|r| r.ok().map(|r| r.parse()))
925 .filter(|r| r.input.len() <= 1 && r.lookahead.is_empty())
926 {
927 if apply_chain_with_sequences(ctx, &rule, &match_funcs).is_some() {
928 return Some(());
929 }
930 }
931 return None;
932 };
933 let matched = skippy_iter.next(None);
934 let g2 = skippy_iter.index();
935 if matched {
936 second = Some(g2);
937 unsafe_to2 = skippy_iter.index() + 1;
938 if skippy_iter.may_skip(&skippy_iter.buffer.info[g2]) != may_skip_t::SKIP_NO {
939 for rule in rules.iter().filter_map(|r| r.ok()) {
942 if apply_chain_with_sequences(ctx, &rule.parse(), &match_funcs).is_some() {
943 return Some(());
944 }
945 }
946 return None;
947 }
948 }
949 let mut rules_iter = rules.iter().filter_map(|r| r.ok());
950 let mut rule_box = rules_iter.next().map(|r| r.parse());
951 while let Some(rule) = rule_box {
952 let input = rule.input;
953 let lookahead = rule.lookahead;
954 let match_input = |info: &mut GlyphInfo, index: usize| {
955 input
956 .get(index)
957 .is_some_and(|v| match_funcs.1(info, u32::from(v.get())))
958 };
959 let match_lookahead = |info: &mut GlyphInfo, index: usize| {
960 lookahead
961 .get(index)
962 .is_some_and(|v| match_funcs.2(info, u32::from(v.get())))
963 };
964 let len_p1 = input.len() + 1;
965 let matched_first = if len_p1 > 1 {
966 match_input(&mut ctx.buffer.info[first], 0)
967 } else {
968 lookahead.is_empty() || match_lookahead(&mut ctx.buffer.info[first], 0)
969 };
970 if matched_first {
971 let matched_second = if let Some(second) = second {
972 if len_p1 > 2 {
973 match_input(&mut ctx.buffer.info[second], 1)
974 } else {
975 (lookahead.len() <= 2 - len_p1)
976 || match_lookahead(&mut ctx.buffer.info[second], 2 - len_p1)
977 }
978 } else {
979 true
980 };
981 if matched_second {
982 if apply_chain_with_sequences(ctx, &rule, &match_funcs).is_some() {
983 if let Some(unsafe_to) = unsafe_to {
984 ctx.buffer
985 .unsafe_to_concat(Some(ctx.buffer.idx), Some(unsafe_to));
986 }
987 return Some(());
988 }
989 } else {
990 unsafe_to = Some(unsafe_to2);
991 }
992
993 rule_box = rules_iter.next().map(|r| r.parse());
994 } else {
995 if unsafe_to.is_none() {
996 unsafe_to = Some(unsafe_to1);
997 }
998
999 if len_p1 > 1 {
1000 let first_glyph_value = input.first().unwrap().get();
1002 loop {
1003 let Some(next_rule) = rules_iter.next() else {
1004 rule_box = None;
1005 break;
1006 };
1007 if next_rule.first_input() != Some(first_glyph_value) {
1008 rule_box = Some(next_rule.parse());
1009 break;
1010 }
1011 }
1012 } else {
1013 rule_box = rules_iter.next().map(|r| r.parse());
1014 }
1015 }
1016 }
1017 if let Some(unsafe_to) = unsafe_to {
1018 ctx.buffer
1019 .unsafe_to_concat(Some(ctx.buffer.idx), Some(unsafe_to));
1020 }
1021 None
1022}