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