1use ttf_parser::opentype_layout::*;
4use ttf_parser::{GlyphId, LazyArray16};
5
6use super::buffer::hb_glyph_info_t;
7use super::buffer::{hb_buffer_t, GlyphPropsFlags};
8use super::hb_font_t;
9use super::hb_mask_t;
10use super::ot_layout::LayoutTable;
11use super::ot_layout::*;
12use super::ot_layout_common::*;
13use super::unicode::hb_unicode_general_category_t;
14
15pub fn match_glyph(glyph: GlyphId, value: u16) -> bool {
17 glyph == GlyphId(value)
18}
19
20pub fn match_input(
21 ctx: &mut hb_ot_apply_context_t,
22 input_len: u16,
23 match_func: &match_func_t,
24 end_position: &mut usize,
25 match_positions: &mut smallvec::SmallVec<[usize; 4]>,
26 p_total_component_count: Option<&mut u8>,
27) -> bool {
28 #[derive(PartialEq)]
51 enum Ligbase {
52 NotChecked,
53 MayNotSkip,
54 MaySkip,
55 }
56
57 let count = usize::from(input_len) + 1;
58 if count > MAX_CONTEXT_LENGTH {
59 return false;
60 }
61
62 if count > match_positions.len() {
63 match_positions.resize(count, 0);
64 }
65
66 let mut iter = skipping_iterator_t::new(ctx, ctx.buffer.idx, false);
67 iter.set_glyph_data(0);
68 iter.enable_matching(match_func);
69
70 let first = ctx.buffer.cur(0);
71 let first_lig_id = _hb_glyph_info_get_lig_id(first);
72 let first_lig_comp = _hb_glyph_info_get_lig_comp(first);
73 let mut total_component_count = 0;
74 let mut ligbase = Ligbase::NotChecked;
75
76 for position in &mut match_positions[1..count] {
77 let mut unsafe_to = 0;
78 if !iter.next(Some(&mut unsafe_to)) {
79 *end_position = unsafe_to;
80 return false;
81 }
82
83 *position = iter.index();
84
85 let this = ctx.buffer.info[iter.index()];
86 let this_lig_id = _hb_glyph_info_get_lig_id(&this);
87 let this_lig_comp = _hb_glyph_info_get_lig_comp(&this);
88
89 if first_lig_id != 0 && first_lig_comp != 0 {
90 if first_lig_id != this_lig_id || first_lig_comp != this_lig_comp {
94 if ligbase == Ligbase::NotChecked {
97 let out = ctx.buffer.out_info();
98 let mut j = ctx.buffer.out_len;
99 let mut found = false;
100 while j > 0 && _hb_glyph_info_get_lig_id(&out[j - 1]) == first_lig_id {
101 if _hb_glyph_info_get_lig_comp(&out[j - 1]) == 0 {
102 j -= 1;
103 found = true;
104 break;
105 }
106 j -= 1;
107 }
108
109 ligbase = if found && iter.may_skip(&out[j]) == may_skip_t::SKIP_YES {
110 Ligbase::MaySkip
111 } else {
112 Ligbase::MayNotSkip
113 };
114 }
115
116 if ligbase == Ligbase::MayNotSkip {
117 return false;
118 }
119 }
120 } else {
121 if this_lig_id != 0 && this_lig_comp != 0 && (this_lig_id != first_lig_id) {
125 return false;
126 }
127 }
128
129 total_component_count += _hb_glyph_info_get_lig_num_comps(&this);
130 }
131
132 *end_position = iter.index() + 1;
133
134 if let Some(p_total_component_count) = p_total_component_count {
135 total_component_count += _hb_glyph_info_get_lig_num_comps(first);
136 *p_total_component_count = total_component_count;
137 }
138
139 match_positions[0] = ctx.buffer.idx;
140
141 true
142}
143
144pub fn match_backtrack(
145 ctx: &mut hb_ot_apply_context_t,
146 backtrack_len: u16,
147 match_func: &match_func_t,
148 match_start: &mut usize,
149) -> bool {
150 let mut iter = skipping_iterator_t::new(ctx, ctx.buffer.backtrack_len(), true);
151 iter.set_glyph_data(0);
152 iter.enable_matching(match_func);
153
154 for _ in 0..backtrack_len {
155 let mut unsafe_from = 0;
156 if !iter.prev(Some(&mut unsafe_from)) {
157 *match_start = unsafe_from;
158 return false;
159 }
160 }
161
162 *match_start = iter.index();
163 true
164}
165
166pub fn match_lookahead(
167 ctx: &mut hb_ot_apply_context_t,
168 lookahead_len: u16,
169 match_func: &match_func_t,
170 start_index: usize,
171 end_index: &mut usize,
172) -> bool {
173 assert!(start_index >= 1);
176 let mut iter = skipping_iterator_t::new(ctx, start_index - 1, true);
177 iter.set_glyph_data(0);
178 iter.enable_matching(match_func);
179
180 for _ in 0..lookahead_len {
181 let mut unsafe_to = 0;
182 if !iter.next(Some(&mut unsafe_to)) {
183 *end_index = unsafe_to;
184 return false;
185 }
186 }
187
188 *end_index = iter.index() + 1;
189 true
190}
191
192pub type match_func_t<'a> = dyn Fn(GlyphId, u16) -> bool + 'a;
193
194pub struct skipping_iterator_t<'a, 'b> {
201 ctx: &'a hb_ot_apply_context_t<'a, 'b>,
202 lookup_props: u32,
203 ignore_zwnj: bool,
204 ignore_zwj: bool,
205 ignore_hidden: bool,
206 mask: hb_mask_t,
207 syllable: u8,
208 matching: Option<&'a match_func_t<'a>>,
209 buf_len: usize,
210 glyph_data: u16,
211 pub(crate) buf_idx: usize,
212}
213
214#[derive(PartialEq, Eq, Copy, Clone)]
215pub enum match_t {
216 MATCH,
217 NOT_MATCH,
218 SKIP,
219}
220
221#[derive(PartialEq, Eq, Copy, Clone)]
222enum may_match_t {
223 MATCH_NO,
224 MATCH_YES,
225 MATCH_MAYBE,
226}
227
228#[derive(PartialEq, Eq, Copy, Clone)]
229enum may_skip_t {
230 SKIP_NO,
231 SKIP_YES,
232 SKIP_MAYBE,
233}
234
235impl<'a, 'b> skipping_iterator_t<'a, 'b> {
236 pub fn new(
237 ctx: &'a hb_ot_apply_context_t<'a, 'b>,
238 start_buf_index: usize,
239 context_match: bool,
240 ) -> Self {
241 skipping_iterator_t {
242 ctx,
243 lookup_props: ctx.lookup_props,
244 ignore_zwnj: ctx.table_index == TableIndex::GPOS || (context_match && ctx.auto_zwnj),
246 ignore_zwj: context_match || ctx.auto_zwj,
248 ignore_hidden: ctx.table_index == TableIndex::GPOS,
250 mask: if context_match {
251 u32::MAX
252 } else {
253 ctx.lookup_mask()
254 },
255 syllable: if ctx.buffer.idx == start_buf_index && ctx.per_syllable {
256 ctx.buffer.cur(0).syllable()
257 } else {
258 0
259 },
260 glyph_data: 0,
261 matching: None,
262 buf_len: ctx.buffer.len,
263 buf_idx: start_buf_index,
264 }
265 }
266
267 pub fn set_glyph_data(&mut self, glyph_data: u16) {
268 self.glyph_data = glyph_data
269 }
270
271 fn advance_glyph_data(&mut self) {
272 self.glyph_data += 1;
273 }
274
275 pub fn set_lookup_props(&mut self, lookup_props: u32) {
276 self.lookup_props = lookup_props;
277 }
278
279 pub fn enable_matching(&mut self, func: &'a match_func_t<'a>) {
280 self.matching = Some(func);
281 }
282
283 pub fn index(&self) -> usize {
284 self.buf_idx
285 }
286
287 pub fn next(&mut self, unsafe_to: Option<&mut usize>) -> bool {
288 let stop = self.buf_len as i32 - 1;
289
290 while (self.buf_idx as i32) < stop {
291 self.buf_idx += 1;
292 let info = &self.ctx.buffer.info[self.buf_idx];
293
294 match self.match_(info) {
295 match_t::MATCH => {
296 self.advance_glyph_data();
297 return true;
298 }
299 match_t::NOT_MATCH => {
300 if let Some(unsafe_to) = unsafe_to {
301 *unsafe_to = self.buf_idx + 1;
302 }
303
304 return false;
305 }
306 match_t::SKIP => continue,
307 }
308 }
309
310 if let Some(unsafe_to) = unsafe_to {
311 *unsafe_to = self.buf_idx + 1;
312 }
313
314 false
315 }
316
317 pub fn prev(&mut self, unsafe_from: Option<&mut usize>) -> bool {
318 let stop: usize = 0;
319
320 while self.buf_idx > stop {
321 self.buf_idx -= 1;
322 let info = &self.ctx.buffer.out_info()[self.buf_idx];
323
324 match self.match_(info) {
325 match_t::MATCH => {
326 self.advance_glyph_data();
327 return true;
328 }
329 match_t::NOT_MATCH => {
330 if let Some(unsafe_from) = unsafe_from {
331 *unsafe_from = self.buf_idx.max(1) - 1;
332 }
333
334 return false;
335 }
336 match_t::SKIP => {
337 continue;
338 }
339 }
340 }
341
342 if let Some(unsafe_from) = unsafe_from {
343 *unsafe_from = 0;
344 }
345
346 false
347 }
348
349 pub fn match_(&self, info: &hb_glyph_info_t) -> match_t {
350 let skip = self.may_skip(info);
351
352 if skip == may_skip_t::SKIP_YES {
353 return match_t::SKIP;
354 }
355
356 let _match = self.may_match(info);
357
358 if _match == may_match_t::MATCH_YES
359 || (_match == may_match_t::MATCH_MAYBE && skip == may_skip_t::SKIP_NO)
360 {
361 return match_t::MATCH;
362 }
363
364 if skip == may_skip_t::SKIP_NO {
365 return match_t::NOT_MATCH;
366 }
367
368 match_t::SKIP
369 }
370
371 fn may_match(&self, info: &hb_glyph_info_t) -> may_match_t {
372 if (info.mask & self.mask) == 0 || (self.syllable != 0 && self.syllable != info.syllable())
373 {
374 return may_match_t::MATCH_NO;
375 }
376
377 if let Some(match_func) = self.matching {
378 return if match_func(info.as_glyph(), self.glyph_data) {
379 may_match_t::MATCH_YES
380 } else {
381 may_match_t::MATCH_NO
382 };
383 }
384
385 may_match_t::MATCH_MAYBE
386 }
387
388 fn may_skip(&self, info: &hb_glyph_info_t) -> may_skip_t {
389 if !self.ctx.check_glyph_property(info, self.lookup_props) {
390 return may_skip_t::SKIP_YES;
391 }
392
393 if _hb_glyph_info_is_default_ignorable(info)
394 && (self.ignore_zwnj || !_hb_glyph_info_is_zwnj(info))
395 && (self.ignore_zwj || !_hb_glyph_info_is_zwj(info))
396 && (self.ignore_hidden || !_hb_glyph_info_is_hidden(info))
397 {
398 return may_skip_t::SKIP_MAYBE;
399 }
400
401 may_skip_t::SKIP_NO
402 }
403}
404
405impl WouldApply for ContextLookup<'_> {
406 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
407 let glyph = ctx.glyphs[0];
408 match *self {
409 Self::Format1 { coverage, sets } => coverage
410 .get(glyph)
411 .and_then(|index| sets.get(index))
412 .map_or(false, |set| set.would_apply(ctx, &match_glyph)),
413 Self::Format2 { classes, sets, .. } => {
414 let class = classes.get(glyph);
415 sets.get(class)
416 .map_or(false, |set| set.would_apply(ctx, &match_class(classes)))
417 }
418 Self::Format3 { coverages, .. } => {
419 ctx.glyphs.len() == usize::from(coverages.len()) + 1
420 && coverages
421 .into_iter()
422 .enumerate()
423 .all(|(i, coverage)| coverage.get(ctx.glyphs[i + 1]).is_some())
424 }
425 }
426 }
427}
428
429impl Apply for ContextLookup<'_> {
430 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
431 let glyph = ctx.buffer.cur(0).as_glyph();
432 match *self {
433 Self::Format1 { coverage, sets } => {
434 coverage.get(glyph)?;
435 let set = coverage.get(glyph).and_then(|index| sets.get(index))?;
436 set.apply(ctx, &match_glyph)
437 }
438 Self::Format2 {
439 coverage,
440 classes,
441 sets,
442 } => {
443 coverage.get(glyph)?;
444 let class = classes.get(glyph);
445 let set = sets.get(class)?;
446 set.apply(ctx, &match_class(classes))
447 }
448 Self::Format3 {
449 coverage,
450 coverages,
451 lookups,
452 } => {
453 coverage.get(glyph)?;
454 let coverages_len = coverages.len();
455
456 let match_func = |glyph, index| {
457 let coverage = coverages.get(index).unwrap();
458 coverage.get(glyph).is_some()
459 };
460
461 let mut match_end = 0;
462 let mut match_positions = smallvec::SmallVec::from_elem(0, 4);
463
464 if match_input(
465 ctx,
466 coverages_len,
467 &match_func,
468 &mut match_end,
469 &mut match_positions,
470 None,
471 ) {
472 ctx.buffer
473 .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end));
474 apply_lookup(
475 ctx,
476 usize::from(coverages_len),
477 &mut match_positions,
478 match_end,
479 lookups,
480 );
481 Some(())
482 } else {
483 ctx.buffer
484 .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end));
485 None
486 }
487 }
488 }
489 }
490}
491
492trait SequenceRuleSetExt {
493 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool;
494 fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()>;
495}
496
497impl SequenceRuleSetExt for SequenceRuleSet<'_> {
498 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool {
499 self.into_iter()
500 .any(|rule| rule.would_apply(ctx, match_func))
501 }
502
503 fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()> {
504 if self
505 .into_iter()
506 .any(|rule| rule.apply(ctx, match_func).is_some())
507 {
508 Some(())
509 } else {
510 None
511 }
512 }
513}
514
515trait SequenceRuleExt {
516 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool;
517 fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()>;
518}
519
520impl SequenceRuleExt for SequenceRule<'_> {
521 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool {
522 ctx.glyphs.len() == usize::from(self.input.len()) + 1
523 && self
524 .input
525 .into_iter()
526 .enumerate()
527 .all(|(i, value)| match_func(ctx.glyphs[i + 1], value))
528 }
529
530 fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_func: &match_func_t) -> Option<()> {
531 apply_context(ctx, self.input, match_func, self.lookups)
532
533 }
535}
536
537impl WouldApply for ChainedContextLookup<'_> {
538 fn would_apply(&self, ctx: &WouldApplyContext) -> bool {
539 let glyph_id = ctx.glyphs[0];
540 match *self {
541 Self::Format1 { coverage, sets } => coverage
542 .get(glyph_id)
543 .and_then(|index| sets.get(index))
544 .map_or(false, |set| set.would_apply(ctx, &match_glyph)),
545 Self::Format2 {
546 input_classes,
547 sets,
548 ..
549 } => {
550 let class = input_classes.get(glyph_id);
551 sets.get(class).map_or(false, |set| {
552 set.would_apply(ctx, &match_class(input_classes))
553 })
554 }
555 Self::Format3 {
556 backtrack_coverages,
557 input_coverages,
558 lookahead_coverages,
559 ..
560 } => {
561 (!ctx.zero_context
562 || (backtrack_coverages.is_empty() && lookahead_coverages.is_empty()))
563 && (ctx.glyphs.len() == usize::from(input_coverages.len()) + 1
564 && input_coverages
565 .into_iter()
566 .enumerate()
567 .all(|(i, coverage)| coverage.contains(ctx.glyphs[i + 1])))
568 }
569 }
570 }
571}
572
573impl Apply for ChainedContextLookup<'_> {
574 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
575 let glyph = ctx.buffer.cur(0).as_glyph();
576 match *self {
577 Self::Format1 { coverage, sets } => {
578 let index = coverage.get(glyph)?;
579 let set = sets.get(index)?;
580 set.apply(ctx, [&match_glyph, &match_glyph, &match_glyph])
581 }
582 Self::Format2 {
583 coverage,
584 backtrack_classes,
585 input_classes,
586 lookahead_classes,
587 sets,
588 } => {
589 coverage.get(glyph)?;
590 let class = input_classes.get(glyph);
591 let set = sets.get(class)?;
592 set.apply(
593 ctx,
594 [
595 &match_class(backtrack_classes),
596 &match_class(input_classes),
597 &match_class(lookahead_classes),
598 ],
599 )
600 }
601 Self::Format3 {
602 coverage,
603 backtrack_coverages,
604 input_coverages,
605 lookahead_coverages,
606 lookups,
607 } => {
608 coverage.get(glyph)?;
609
610 let back = |glyph, index| {
611 let coverage = backtrack_coverages.get(index).unwrap();
612 coverage.contains(glyph)
613 };
614
615 let ahead = |glyph, index| {
616 let coverage = lookahead_coverages.get(index).unwrap();
617 coverage.contains(glyph)
618 };
619
620 let input = |glyph, index| {
621 let coverage = input_coverages.get(index).unwrap();
622 coverage.contains(glyph)
623 };
624
625 let mut end_index = ctx.buffer.idx;
626 let mut match_end = 0;
627 let mut match_positions = smallvec::SmallVec::from_elem(0, 4);
628
629 let input_matches = match_input(
630 ctx,
631 input_coverages.len(),
632 &input,
633 &mut match_end,
634 &mut match_positions,
635 None,
636 );
637
638 if input_matches {
639 end_index = match_end;
640 }
641
642 if !(input_matches
643 && match_lookahead(
644 ctx,
645 lookahead_coverages.len(),
646 &ahead,
647 match_end,
648 &mut end_index,
649 ))
650 {
651 ctx.buffer
652 .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index));
653 return None;
654 }
655
656 let mut start_index = ctx.buffer.out_len;
657
658 if !match_backtrack(ctx, backtrack_coverages.len(), &back, &mut start_index) {
659 ctx.buffer
660 .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index));
661 return None;
662 }
663
664 ctx.buffer
665 .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index));
666 apply_lookup(
667 ctx,
668 usize::from(input_coverages.len()),
669 &mut match_positions,
670 match_end,
671 lookups,
672 );
673
674 Some(())
675 }
676 }
677 }
678}
679
680trait ChainRuleSetExt {
681 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool;
682 fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_funcs: [&match_func_t; 3])
683 -> Option<()>;
684}
685
686impl ChainRuleSetExt for ChainedSequenceRuleSet<'_> {
687 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool {
688 self.into_iter()
689 .any(|rule| rule.would_apply(ctx, match_func))
690 }
691
692 fn apply(
693 &self,
694 ctx: &mut hb_ot_apply_context_t,
695 match_funcs: [&match_func_t; 3],
696 ) -> Option<()> {
697 if self
698 .into_iter()
699 .any(|rule| rule.apply(ctx, match_funcs).is_some())
700 {
701 Some(())
702 } else {
703 None
704 }
705
706 }
708}
709
710trait ChainRuleExt {
711 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool;
712 fn apply(&self, ctx: &mut hb_ot_apply_context_t, match_funcs: [&match_func_t; 3])
713 -> Option<()>;
714}
715
716impl ChainRuleExt for ChainedSequenceRule<'_> {
717 fn would_apply(&self, ctx: &WouldApplyContext, match_func: &match_func_t) -> bool {
718 (!ctx.zero_context || (self.backtrack.is_empty() && self.lookahead.is_empty()))
719 && (ctx.glyphs.len() == usize::from(self.input.len()) + 1
720 && self
721 .input
722 .into_iter()
723 .enumerate()
724 .all(|(i, value)| match_func(ctx.glyphs[i + 1], value)))
725 }
726
727 fn apply(
728 &self,
729 ctx: &mut hb_ot_apply_context_t,
730 match_funcs: [&match_func_t; 3],
731 ) -> Option<()> {
732 apply_chain_context(
733 ctx,
734 self.backtrack,
735 self.input,
736 self.lookahead,
737 match_funcs,
738 self.lookups,
739 )
740 }
741}
742
743fn apply_context(
744 ctx: &mut hb_ot_apply_context_t,
745 input: LazyArray16<u16>,
746 match_func: &match_func_t,
747 lookups: LazyArray16<SequenceLookupRecord>,
748) -> Option<()> {
749 let match_func = |glyph, index| {
750 let value = input.get(index).unwrap();
751 match_func(glyph, value)
752 };
753
754 let mut match_end = 0;
755 let mut match_positions = smallvec::SmallVec::from_elem(0, 4);
756
757 if match_input(
758 ctx,
759 input.len(),
760 &match_func,
761 &mut match_end,
762 &mut match_positions,
763 None,
764 ) {
765 ctx.buffer
766 .unsafe_to_break(Some(ctx.buffer.idx), Some(match_end));
767 apply_lookup(
768 ctx,
769 usize::from(input.len()),
770 &mut match_positions,
771 match_end,
772 lookups,
773 );
774 return Some(());
775 }
776
777 None
778}
779
780fn apply_chain_context(
781 ctx: &mut hb_ot_apply_context_t,
782 backtrack: LazyArray16<u16>,
783 input: LazyArray16<u16>,
784 lookahead: LazyArray16<u16>,
785 match_funcs: [&match_func_t; 3],
786 lookups: LazyArray16<SequenceLookupRecord>,
787) -> Option<()> {
788 let f1 = |glyph, index| {
791 let value = backtrack.get(index).unwrap();
792 match_funcs[0](glyph, value)
793 };
794
795 let f2 = |glyph, index| {
796 let value = lookahead.get(index).unwrap();
797 match_funcs[2](glyph, value)
798 };
799
800 let f3 = |glyph, index| {
801 let value = input.get(index).unwrap();
802 match_funcs[1](glyph, value)
803 };
804
805 let mut end_index = ctx.buffer.idx;
806 let mut match_end = 0;
807 let mut match_positions = smallvec::SmallVec::from_elem(0, 4);
808
809 let input_matches = match_input(
810 ctx,
811 input.len(),
812 &f3,
813 &mut match_end,
814 &mut match_positions,
815 None,
816 );
817
818 if input_matches {
819 end_index = match_end;
820 }
821
822 if !(input_matches && match_lookahead(ctx, lookahead.len(), &f2, match_end, &mut end_index)) {
823 ctx.buffer
824 .unsafe_to_concat(Some(ctx.buffer.idx), Some(end_index));
825 return None;
826 }
827
828 let mut start_index = ctx.buffer.out_len;
829
830 if !match_backtrack(ctx, backtrack.len(), &f1, &mut start_index) {
831 ctx.buffer
832 .unsafe_to_concat_from_outbuffer(Some(start_index), Some(end_index));
833 return None;
834 }
835
836 ctx.buffer
837 .unsafe_to_break_from_outbuffer(Some(start_index), Some(end_index));
838 apply_lookup(
839 ctx,
840 usize::from(input.len()),
841 &mut match_positions,
842 match_end,
843 lookups,
844 );
845
846 Some(())
847}
848
849fn apply_lookup(
850 ctx: &mut hb_ot_apply_context_t,
851 input_len: usize,
852 match_positions: &mut smallvec::SmallVec<[usize; 4]>,
853 match_end: usize,
854 lookups: LazyArray16<SequenceLookupRecord>,
855) {
856 let mut count = input_len + 1;
857
858 if count > match_positions.len() {
859 match_positions.resize(count, 0);
860 }
861
862 let mut end: isize = {
865 let backtrack_len = ctx.buffer.backtrack_len();
866 let delta = backtrack_len as isize - ctx.buffer.idx as isize;
867
868 for j in 0..count {
870 match_positions[j] = (match_positions[j] as isize + delta) as _;
871 }
872
873 backtrack_len as isize + match_end as isize - ctx.buffer.idx as isize
874 };
875
876 for record in lookups {
877 if !ctx.buffer.successful {
878 break;
879 }
880
881 let idx = usize::from(record.sequence_index);
882 if idx >= count {
883 continue;
884 }
885
886 let orig_len = ctx.buffer.backtrack_len() + ctx.buffer.lookahead_len();
887
888 if match_positions[idx] >= orig_len {
890 continue;
891 }
892
893 if !ctx.buffer.move_to(match_positions[idx]) {
894 break;
895 }
896
897 if ctx.buffer.max_ops <= 0 {
898 break;
899 }
900
901 if ctx.recurse(record.lookup_list_index).is_none() {
902 continue;
903 }
904
905 let new_len = ctx.buffer.backtrack_len() + ctx.buffer.lookahead_len();
906 let mut delta = new_len as isize - orig_len as isize;
907 if delta == 0 {
908 continue;
909 }
910
911 end += delta;
935 if end < match_positions[idx] as isize {
936 delta += match_positions[idx] as isize - end;
945 end = match_positions[idx] as isize;
946 }
947
948 let mut next = idx + 1;
950
951 if delta > 0 {
952 if delta as usize + count > MAX_CONTEXT_LENGTH {
953 break;
954 }
955
956 if delta as usize + count > match_positions.len() {
957 let inner_max = (core::cmp::max(4, match_positions.len()) as f32 * 1.5) as usize;
958 match_positions.resize(core::cmp::max(delta as usize + count, inner_max), 0);
959 }
960 } else {
961 delta = delta.max(next as isize - count as isize);
963 next = (next as isize - delta) as _;
964 }
965
966 match_positions.copy_within(next..count, (next as isize + delta) as _);
968 next = (next as isize + delta) as _;
969 count = (count as isize + delta) as _;
970
971 for j in idx + 1..next {
973 match_positions[j] = match_positions[j - 1] + 1;
974 }
975
976 while next < count {
978 match_positions[next] = (match_positions[next] as isize + delta) as _;
979 next += 1;
980 }
981 }
982
983 ctx.buffer.move_to(end.try_into().unwrap());
984}
985
986fn match_class(class_def: ClassDefinition<'_>) -> impl Fn(GlyphId, u16) -> bool + '_ {
988 move |glyph, value| class_def.get(glyph) == value
989}
990
991pub trait WouldApply {
993 fn would_apply(&self, ctx: &WouldApplyContext) -> bool;
995}
996
997pub trait Apply {
999 fn apply(&self, ctx: &mut OT::hb_ot_apply_context_t) -> Option<()>;
1001}
1002
1003pub struct WouldApplyContext<'a> {
1004 pub glyphs: &'a [GlyphId],
1005 pub zero_context: bool,
1006}
1007
1008pub mod OT {
1009 use super::*;
1010 use crate::hb::set_digest::{hb_set_digest_ext, hb_set_digest_t};
1011
1012 pub struct hb_ot_apply_context_t<'a, 'b> {
1013 pub table_index: TableIndex,
1014 pub face: &'a hb_font_t<'b>,
1015 pub buffer: &'a mut hb_buffer_t,
1016 lookup_mask: hb_mask_t,
1017 pub per_syllable: bool,
1018 pub lookup_index: LookupIndex,
1019 pub lookup_props: u32,
1020 pub nesting_level_left: usize,
1021 pub auto_zwnj: bool,
1022 pub auto_zwj: bool,
1023 pub random: bool,
1024 pub random_state: u32,
1025 pub last_base: i32,
1026 pub last_base_until: u32,
1027 pub digest: hb_set_digest_t,
1028 }
1029
1030 impl<'a, 'b> hb_ot_apply_context_t<'a, 'b> {
1031 pub fn new(
1032 table_index: TableIndex,
1033 face: &'a hb_font_t<'b>,
1034 buffer: &'a mut hb_buffer_t,
1035 ) -> Self {
1036 let buffer_digest = buffer.digest();
1037 Self {
1038 table_index,
1039 face,
1040 buffer,
1041 lookup_mask: 1,
1042 per_syllable: false,
1043 lookup_index: u16::MAX,
1044 lookup_props: 0,
1045 nesting_level_left: MAX_NESTING_LEVEL,
1046 auto_zwnj: true,
1047 auto_zwj: true,
1048 random: false,
1049 random_state: 1,
1050 last_base: -1,
1051 last_base_until: 0,
1052 digest: buffer_digest,
1053 }
1054 }
1055
1056 pub fn random_number(&mut self) -> u32 {
1057 self.random_state = self.random_state.wrapping_mul(48271) % 2147483647;
1059 self.random_state
1060 }
1061
1062 pub fn set_lookup_mask(&mut self, mask: hb_mask_t) {
1063 self.lookup_mask = mask;
1064 self.last_base = -1;
1065 self.last_base_until = 0;
1066 }
1067
1068 pub fn lookup_mask(&self) -> hb_mask_t {
1069 self.lookup_mask
1070 }
1071
1072 pub fn recurse(&mut self, sub_lookup_index: LookupIndex) -> Option<()> {
1073 if self.nesting_level_left == 0 {
1074 self.buffer.shaping_failed = true;
1075 return None;
1076 }
1077
1078 self.buffer.max_ops -= 1;
1079 if self.buffer.max_ops < 0 {
1080 self.buffer.shaping_failed = true;
1081 return None;
1082 }
1083
1084 self.nesting_level_left -= 1;
1085 let saved_props = self.lookup_props;
1086 let saved_index = self.lookup_index;
1087
1088 self.lookup_index = sub_lookup_index;
1089 let applied = match self.table_index {
1090 TableIndex::GSUB => self
1091 .face
1092 .gsub
1093 .as_ref()
1094 .and_then(|table| table.get_lookup(sub_lookup_index))
1095 .and_then(|lookup| {
1096 self.lookup_props = lookup.props();
1097 lookup.apply(self)
1098 }),
1099 TableIndex::GPOS => self
1100 .face
1101 .gpos
1102 .as_ref()
1103 .and_then(|table| table.get_lookup(sub_lookup_index))
1104 .and_then(|lookup| {
1105 self.lookup_props = lookup.props();
1106 lookup.apply(self)
1107 }),
1108 };
1109
1110 self.lookup_props = saved_props;
1111 self.lookup_index = saved_index;
1112 self.nesting_level_left += 1;
1113 applied
1114 }
1115
1116 pub fn check_glyph_property(&self, info: &hb_glyph_info_t, match_props: u32) -> bool {
1117 let glyph_props = info.glyph_props();
1118
1119 let lookup_flags = match_props as u16;
1121
1122 if glyph_props & lookup_flags & lookup_flags::IGNORE_FLAGS != 0 {
1125 return false;
1126 }
1127
1128 if glyph_props & GlyphPropsFlags::MARK.bits() != 0 {
1129 if lookup_flags & lookup_flags::USE_MARK_FILTERING_SET != 0 {
1132 let set_index = (match_props >> 16) as u16;
1133 if let Some(table) = self.face.tables().gdef {
1137 return table.is_mark_glyph(info.as_glyph(), Some(set_index));
1138 } else {
1139 return false;
1140 }
1141 }
1142
1143 if lookup_flags & lookup_flags::MARK_ATTACHMENT_TYPE_MASK != 0 {
1147 return (lookup_flags & lookup_flags::MARK_ATTACHMENT_TYPE_MASK)
1148 == (glyph_props & lookup_flags::MARK_ATTACHMENT_TYPE_MASK);
1149 }
1150 }
1151
1152 true
1153 }
1154
1155 fn set_glyph_class(
1156 &mut self,
1157 glyph_id: GlyphId,
1158 class_guess: GlyphPropsFlags,
1159 ligature: bool,
1160 component: bool,
1161 ) {
1162 self.digest.add(glyph_id);
1163
1164 let cur = self.buffer.cur_mut(0);
1165 let mut props = cur.glyph_props();
1166
1167 props |= GlyphPropsFlags::SUBSTITUTED.bits();
1168
1169 if ligature {
1170 props |= GlyphPropsFlags::LIGATED.bits();
1171 props &= !GlyphPropsFlags::MULTIPLIED.bits();
1177 }
1178
1179 if component {
1180 props |= GlyphPropsFlags::MULTIPLIED.bits();
1181 }
1182
1183 let has_glyph_classes = self
1184 .face
1185 .tables()
1186 .gdef
1187 .map_or(false, |table| table.has_glyph_classes());
1188
1189 if has_glyph_classes {
1190 props &= GlyphPropsFlags::PRESERVE.bits();
1191 cur.set_glyph_props(props | self.face.glyph_props(glyph_id));
1192 } else if !class_guess.is_empty() {
1193 props &= GlyphPropsFlags::PRESERVE.bits();
1194 cur.set_glyph_props(props | class_guess.bits());
1195 } else {
1196 cur.set_glyph_props(props);
1197 }
1198 }
1199
1200 pub fn replace_glyph(&mut self, glyph_id: GlyphId) {
1201 self.set_glyph_class(glyph_id, GlyphPropsFlags::empty(), false, false);
1202 self.buffer.replace_glyph(u32::from(glyph_id.0));
1203 }
1204
1205 pub fn replace_glyph_inplace(&mut self, glyph_id: GlyphId) {
1206 self.set_glyph_class(glyph_id, GlyphPropsFlags::empty(), false, false);
1207 self.buffer.cur_mut(0).glyph_id = u32::from(glyph_id.0);
1208 }
1209
1210 pub fn replace_glyph_with_ligature(
1211 &mut self,
1212 glyph_id: GlyphId,
1213 class_guess: GlyphPropsFlags,
1214 ) {
1215 self.set_glyph_class(glyph_id, class_guess, true, false);
1216 self.buffer.replace_glyph(u32::from(glyph_id.0));
1217 }
1218
1219 pub fn output_glyph_for_component(
1220 &mut self,
1221 glyph_id: GlyphId,
1222 class_guess: GlyphPropsFlags,
1223 ) {
1224 self.set_glyph_class(glyph_id, class_guess, false, true);
1225 self.buffer.output_glyph(u32::from(glyph_id.0));
1226 }
1227 }
1228}
1229
1230use OT::hb_ot_apply_context_t;
1231
1232pub fn ligate_input(
1233 ctx: &mut hb_ot_apply_context_t,
1234 count: usize,
1236 match_positions: &smallvec::SmallVec<[usize; 4]>,
1238 match_end: usize,
1239 total_component_count: u8,
1240 lig_glyph: GlyphId,
1241) {
1242 let mut buffer = &mut ctx.buffer;
1275 buffer.merge_clusters(buffer.idx, match_end);
1276
1277 let mut is_base_ligature = _hb_glyph_info_is_base_glyph(&buffer.info[match_positions[0]]);
1278 let mut is_mark_ligature = _hb_glyph_info_is_mark(&buffer.info[match_positions[0]]);
1279 for i in 1..count {
1280 if !_hb_glyph_info_is_mark(&buffer.info[match_positions[i]]) {
1281 is_base_ligature = false;
1282 is_mark_ligature = false;
1283 }
1284 }
1285
1286 let is_ligature = !is_base_ligature && !is_mark_ligature;
1287 let class = if is_ligature {
1288 GlyphPropsFlags::LIGATURE
1289 } else {
1290 GlyphPropsFlags::empty()
1291 };
1292 let lig_id = if is_ligature {
1293 buffer.allocate_lig_id()
1294 } else {
1295 0
1296 };
1297 let first = buffer.cur_mut(0);
1298 let mut last_lig_id = _hb_glyph_info_get_lig_id(first);
1299 let mut last_num_comps = _hb_glyph_info_get_lig_num_comps(first);
1300 let mut comps_so_far = last_num_comps;
1301
1302 if is_ligature {
1303 _hb_glyph_info_set_lig_props_for_ligature(first, lig_id, total_component_count);
1304 if _hb_glyph_info_get_general_category(first)
1305 == hb_unicode_general_category_t::NonspacingMark
1306 {
1307 _hb_glyph_info_set_general_category(first, hb_unicode_general_category_t::OtherLetter);
1308 }
1309 }
1310
1311 ctx.replace_glyph_with_ligature(lig_glyph, class);
1312 buffer = &mut ctx.buffer;
1313
1314 for i in 1..count {
1315 while buffer.idx < match_positions[i] && buffer.successful {
1316 if is_ligature {
1317 let cur = buffer.cur_mut(0);
1318 let mut this_comp = _hb_glyph_info_get_lig_comp(cur);
1319 if this_comp == 0 {
1320 this_comp = last_num_comps;
1321 }
1322 assert!(comps_so_far >= last_num_comps);
1325 let new_lig_comp = comps_so_far - last_num_comps + this_comp.min(last_num_comps);
1326 _hb_glyph_info_set_lig_props_for_mark(cur, lig_id, new_lig_comp);
1327 }
1328 buffer.next_glyph();
1329 }
1330
1331 let cur = buffer.cur(0);
1332 last_lig_id = _hb_glyph_info_get_lig_id(cur);
1333 last_num_comps = _hb_glyph_info_get_lig_num_comps(cur);
1334 comps_so_far += last_num_comps;
1335
1336 buffer.idx += 1;
1338 }
1339
1340 if !is_mark_ligature && last_lig_id != 0 {
1341 for i in buffer.idx..buffer.len {
1343 let info = &mut buffer.info[i];
1344 if last_lig_id != _hb_glyph_info_get_lig_id(info) {
1345 break;
1346 }
1347
1348 let this_comp = _hb_glyph_info_get_lig_comp(info);
1349 if this_comp == 0 {
1350 break;
1351 }
1352
1353 assert!(comps_so_far >= last_num_comps);
1356 let new_lig_comp = comps_so_far - last_num_comps + this_comp.min(last_num_comps);
1357 _hb_glyph_info_set_lig_props_for_mark(info, lig_id, new_lig_comp)
1358 }
1359 }
1360}