1use super::buffer::GlyphInfo;
4use super::buffer::{hb_buffer_t, GlyphPropsFlags};
5use super::cache::hb_cache_t;
6use super::face::Scale;
7use super::hb_font_t;
8use super::hb_mask_t;
9use super::ot_layout::*;
10use super::ot_layout_common::*;
11use super::set_digest::hb_set_digest_t;
12use crate::hb::ot::{ClassDefInfo, CoverageInfo};
13use crate::hb::ot_layout_gsubgpos::OT::check_glyph_property;
14use crate::hb::unicode::GeneralCategory;
15use alloc::boxed::Box;
16use read_fonts::tables::layout::SequenceLookupRecord;
17use read_fonts::types::GlyphId;
18
19pub(crate) type MatchPositions = smallvec::SmallVec<[u32; 8]>;
20
21pub fn match_glyph(info: &mut GlyphInfo, value: u32) -> bool {
23 info.glyph_id == value
24}
25
26pub fn match_always(_info: &mut GlyphInfo, _value: u32) -> bool {
27 true
28}
29
30pub fn match_input(
31 ctx: &mut hb_ot_apply_context_t,
32 input_len: u16,
33 match_func: impl Fn(&mut GlyphInfo, u32) -> bool,
34 end_position: &mut usize,
35 p_total_component_count: Option<&mut u8>,
36) -> bool {
37 #[derive(PartialEq)]
60 enum Ligbase {
61 NotChecked,
62 MayNotSkip,
63 MaySkip,
64 }
65
66 let count = usize::from(input_len) + 1;
67
68 if count == 1 {
69 *end_position = ctx.buffer.idx + 1;
70 ctx.match_positions_len = 1;
71 ctx.match_positions[0] = ctx.buffer.idx as u32;
72 if let Some(p_total_component_count) = p_total_component_count {
73 *p_total_component_count = ctx.buffer.cur(0).lig_num_comps();
74 }
75 return true;
76 }
77
78 if count > MAX_CONTEXT_LENGTH {
79 return false;
80 }
81 ctx.match_positions_len = count;
82
83 let mut iter = skipping_iterator_t::with_match_fn(ctx, false, Some(match_func));
84 iter.reset(iter.buffer.idx);
85 iter.set_glyph_data(0);
86
87 let first = *iter.buffer.cur(0);
88 let first_lig_id = first.lig_id();
89 let first_lig_comp = first.lig_comp();
90 let mut total_component_count = 0;
91 let mut ligbase = Ligbase::NotChecked;
92
93 for i in 1..count {
94 let mut unsafe_to = 0;
95 if !iter.next(Some(&mut unsafe_to)) {
96 *end_position = unsafe_to;
97 return false;
98 }
99
100 iter.set_match_position(i, iter.index());
101
102 let this = iter.buffer.info[iter.index()];
103 let this_lig_id = this.lig_id();
104 let this_lig_comp = this.lig_comp();
105
106 if first_lig_id != 0 && first_lig_comp != 0 {
107 if first_lig_id != this_lig_id || first_lig_comp != this_lig_comp {
111 if ligbase == Ligbase::NotChecked {
114 let out = iter.buffer.out_info();
115 let mut j = iter.buffer.out_len;
116 let mut found = false;
117 while j > 0 && out[j - 1].lig_id() == first_lig_id {
118 if out[j - 1].lig_comp() == 0 {
119 j -= 1;
120 found = true;
121 break;
122 }
123 j -= 1;
124 }
125
126 ligbase = if found && iter.may_skip(&out[j]) == may_skip_t::SKIP_YES {
127 Ligbase::MaySkip
128 } else {
129 Ligbase::MayNotSkip
130 };
131 }
132
133 if ligbase == Ligbase::MayNotSkip {
134 return false;
135 }
136 }
137 } else {
138 if this_lig_id != 0 && this_lig_comp != 0 && (this_lig_id != first_lig_id) {
142 return false;
143 }
144 }
145
146 total_component_count += this.lig_num_comps();
147 }
148
149 *end_position = iter.index() + 1;
150
151 if let Some(p_total_component_count) = p_total_component_count {
152 total_component_count += first.lig_num_comps();
153 *p_total_component_count = total_component_count;
154 }
155
156 ctx.match_positions[0] = iter.buffer.idx as u32;
157
158 true
159}
160
161pub fn match_backtrack(
162 ctx: &mut hb_ot_apply_context_t,
163 backtrack_len: u16,
164 match_func: impl Fn(&mut GlyphInfo, u32) -> bool,
165 match_start: &mut usize,
166) -> bool {
167 if backtrack_len == 0 {
168 *match_start = ctx.buffer.backtrack_len();
169 return true;
170 }
171
172 let mut iter = skipping_iterator_t::with_match_fn(ctx, true, Some(match_func));
173 iter.reset_back(iter.buffer.backtrack_len());
174 iter.set_glyph_data(0);
175
176 for _ in 0..backtrack_len {
177 let mut unsafe_from = 0;
178 if !iter.prev(Some(&mut unsafe_from)) {
179 *match_start = unsafe_from;
180 return false;
181 }
182 }
183
184 *match_start = iter.index();
185 true
186}
187
188pub fn match_lookahead(
189 ctx: &mut hb_ot_apply_context_t,
190 lookahead_len: u16,
191 match_func: impl Fn(&mut GlyphInfo, u32) -> bool,
192 start_index: usize,
193 end_index: &mut usize,
194) -> bool {
195 if lookahead_len == 0 {
196 *end_index = start_index;
197 return true;
198 }
199
200 debug_assert!(start_index >= 1);
203 let mut iter = skipping_iterator_t::with_match_fn(ctx, true, Some(match_func));
204 iter.reset(start_index - 1);
205 iter.set_glyph_data(0);
206
207 for _ in 0..lookahead_len {
208 let mut unsafe_to = 0;
209 if !iter.next(Some(&mut unsafe_to)) {
210 *end_index = unsafe_to;
211 return false;
212 }
213 }
214
215 *end_index = iter.index() + 1;
216 true
217}
218
219#[derive(PartialEq, Eq, Copy, Clone)]
220pub enum match_t {
221 MATCH,
222 NOT_MATCH,
223 SKIP,
224}
225
226#[derive(PartialEq, Eq, Copy, Clone)]
227enum may_match_t {
228 MATCH_NO,
229 MATCH_YES,
230 MATCH_MAYBE,
231}
232
233#[derive(PartialEq, Eq, Copy, Clone)]
234pub enum may_skip_t {
235 SKIP_NO,
236 SKIP_YES,
237 SKIP_MAYBE,
238}
239
240#[derive(Default)]
241pub struct matcher_t {
242 lookup_props: u32,
243 mask: hb_mask_t,
244 ignore_zwnj: bool,
245 ignore_zwj: bool,
246 ignore_hidden: bool,
247 per_syllable: bool,
248}
249
250impl matcher_t {
251 fn new(ctx: &hb_ot_apply_context_t, context_match: bool) -> Self {
252 matcher_t {
253 lookup_props: ctx.lookup_props,
254 ignore_zwnj: ctx.table_index == TableIndex::GPOS || (context_match && ctx.auto_zwnj),
256 ignore_zwj: context_match || ctx.auto_zwj,
258 ignore_hidden: ctx.table_index == TableIndex::GPOS,
260 mask: if context_match {
261 u32::MAX
262 } else {
263 ctx.lookup_mask()
264 },
265 per_syllable: ctx.table_index == TableIndex::GSUB && ctx.per_syllable,
267 }
268 }
269
270 fn may_match(
271 &self,
272 info: &mut GlyphInfo,
273 glyph_data: u32,
274 match_func: Option<&impl Fn(&mut GlyphInfo, u32) -> bool>,
275 syllable: u8,
276 ) -> may_match_t {
277 if (info.mask & self.mask) == 0
278 || (self.per_syllable && syllable != 0 && syllable != info.syllable())
279 {
280 return may_match_t::MATCH_NO;
281 }
282
283 if let Some(match_func) = match_func {
284 return if match_func(info, glyph_data) {
285 may_match_t::MATCH_YES
286 } else {
287 may_match_t::MATCH_NO
288 };
289 }
290
291 may_match_t::MATCH_MAYBE
292 }
293
294 #[inline(always)]
295 fn may_skip(&self, info: &GlyphInfo, face: &hb_font_t, lookup_props: u32) -> may_skip_t {
296 if !check_glyph_property(face, info, lookup_props) {
297 return may_skip_t::SKIP_YES;
298 }
299
300 if info.is_default_ignorable()
301 && (self.ignore_zwnj || !info.is_zwnj())
302 && (self.ignore_zwj || !info.is_zwj())
303 && (self.ignore_hidden || !info.is_hidden())
304 {
305 return may_skip_t::SKIP_MAYBE;
306 }
307
308 may_skip_t::SKIP_NO
309 }
310}
311
312pub struct skipping_iterator_t<'f, 'c, F> {
319 pub(crate) buffer: &'c mut hb_buffer_t,
320 face: &'c hb_font_t<'f>,
321 matcher: &'c matcher_t,
322 match_positions: &'c mut MatchPositions,
323 buf_len: usize,
324 glyph_data: u32,
325 pub(crate) buf_idx: usize,
326 match_func: Option<F>,
327 lookup_props: u32,
328 syllable: u8,
329}
330
331impl<'f, 'c> skipping_iterator_t<'f, 'c, fn(&mut GlyphInfo, u32) -> bool> {
332 pub fn new(ctx: &'c mut hb_ot_apply_context_t<'f>, context_match: bool) -> Self {
333 Self::with_match_fn(ctx, context_match, None)
334 }
335}
336
337pub(crate) enum MatchSource {
338 Info,
339 OutInfo,
340}
341
342impl<'f, 'c, F> skipping_iterator_t<'f, 'c, F>
343where
344 F: Fn(&mut GlyphInfo, u32) -> bool,
345{
346 pub fn with_match_fn(
347 ctx: &'c mut hb_ot_apply_context_t<'f>,
348 context_match: bool,
349 match_fn: Option<F>,
350 ) -> Self {
351 let matcher = if context_match {
352 &ctx.context_matcher
353 } else {
354 &ctx.matcher
355 };
356 let buf_len = ctx.buffer.len;
357 skipping_iterator_t {
358 buffer: ctx.buffer,
359 face: ctx.face,
360 glyph_data: 0,
361 buf_len,
362 buf_idx: 0,
363 matcher,
364 match_func: match_fn,
365 match_positions: &mut ctx.match_positions,
366 lookup_props: matcher.lookup_props,
367 syllable: 0,
368 }
369 }
370
371 pub fn set_match_position(&mut self, idx: usize, position: usize) {
372 let count = idx + 1;
373 if count > self.match_positions.len() {
374 self.match_positions.resize(count, 0);
375 }
376
377 self.match_positions[idx] = position as u32;
378 }
379
380 pub fn set_glyph_data(&mut self, glyph_data: u32) {
381 self.glyph_data = glyph_data;
382 }
383
384 fn advance_glyph_data(&mut self) {
385 self.glyph_data += 1;
386 }
387
388 pub fn set_lookup_props(&mut self, lookup_props: u32) {
389 self.lookup_props = lookup_props;
390 }
391
392 pub fn index(&self) -> usize {
393 self.buf_idx
394 }
395
396 #[inline]
397 pub fn next(&mut self, unsafe_to: Option<&mut usize>) -> bool {
398 let stop = self.buf_len.saturating_sub(1);
399
400 while self.buf_idx < stop {
401 self.buf_idx += 1;
402
403 match self.match_at(self.buf_idx, MatchSource::Info) {
404 match_t::MATCH => {
405 self.advance_glyph_data();
406 return true;
407 }
408 match_t::NOT_MATCH => {
409 if let Some(unsafe_to) = unsafe_to {
410 *unsafe_to = self.buf_idx + 1;
411 }
412
413 return false;
414 }
415 match_t::SKIP => continue,
416 }
417 }
418
419 if let Some(unsafe_to) = unsafe_to {
420 *unsafe_to = self.buf_idx + 1;
421 }
422
423 false
424 }
425
426 #[inline]
427 pub fn prev(&mut self, unsafe_from: Option<&mut usize>) -> bool {
428 let stop: usize = 0;
429
430 while self.buf_idx > stop {
431 self.buf_idx -= 1;
432
433 match self.match_at(self.buf_idx, MatchSource::OutInfo) {
434 match_t::MATCH => {
435 self.advance_glyph_data();
436 return true;
437 }
438 match_t::NOT_MATCH => {
439 if let Some(unsafe_from) = unsafe_from {
440 *unsafe_from = self.buf_idx.max(1) - 1;
441 }
442
443 return false;
444 }
445 match_t::SKIP => {
446 continue;
447 }
448 }
449 }
450
451 if let Some(unsafe_from) = unsafe_from {
452 *unsafe_from = 0;
453 }
454
455 false
456 }
457
458 pub fn reset(&mut self, start_index: usize) {
459 self.buf_idx = start_index;
461 self.buf_len = self.buffer.len;
462 self.syllable = self.buffer.cur(0).syllable();
463 }
464
465 pub fn reset_back(&mut self, start_index: usize) {
466 self.buf_idx = start_index;
468 self.syllable = self.buffer.cur(0).syllable();
469 }
470
471 pub fn reset_fast(&mut self, start_index: usize) {
472 self.buf_idx = start_index;
474 }
475
476 pub fn may_skip(&self, info: &GlyphInfo) -> may_skip_t {
477 self.matcher.may_skip(info, self.face, self.lookup_props)
478 }
479
480 #[inline]
481 pub fn match_at(&mut self, idx: usize, source: MatchSource) -> match_t {
482 let info = match source {
483 MatchSource::Info => &mut self.buffer.info[idx],
484 MatchSource::OutInfo => &mut self.buffer.out_info_mut()[idx],
485 };
486 let skip = self.matcher.may_skip(info, self.face, self.lookup_props);
487
488 if skip == may_skip_t::SKIP_YES {
489 return match_t::SKIP;
490 }
491
492 let _match = self.matcher.may_match(
493 info,
494 self.glyph_data,
495 self.match_func.as_ref(),
496 self.syllable,
497 );
498
499 if _match == may_match_t::MATCH_YES
500 || (_match == may_match_t::MATCH_MAYBE && skip == may_skip_t::SKIP_NO)
501 {
502 return match_t::MATCH;
503 }
504
505 if skip == may_skip_t::SKIP_NO {
506 return match_t::NOT_MATCH;
507 }
508
509 match_t::SKIP
510 }
511}
512
513pub(crate) fn apply_lookup(
514 ctx: &mut hb_ot_apply_context_t,
515 input_len: usize,
516 match_end: usize,
517 lookups: &[SequenceLookupRecord],
518) {
519 let mut count = input_len + 1;
520
521 debug_assert!(count <= ctx.match_positions.len(), "");
522
523 let mut end: isize = {
526 let backtrack_len = ctx.buffer.backtrack_len();
527 let delta = backtrack_len as isize - ctx.buffer.idx as isize;
528
529 for j in 0..count {
531 ctx.match_positions[j] = (ctx.match_positions[j] as isize + delta) as _;
532 }
533
534 backtrack_len as isize + match_end as isize - ctx.buffer.idx as isize
535 };
536
537 for record in lookups {
538 if !ctx.buffer.successful {
539 break;
540 }
541
542 let idx = usize::from(record.sequence_index.get());
543 if idx >= count {
544 continue;
545 }
546
547 let orig_len = ctx.buffer.backtrack_len() + ctx.buffer.lookahead_len();
548
549 if ctx.match_positions[idx] as usize >= orig_len {
551 continue;
552 }
553
554 if !ctx.buffer.move_to(ctx.match_positions[idx] as usize) {
555 break;
556 }
557
558 if ctx.buffer.max_ops <= 0 {
559 break;
560 }
561
562 if ctx.recurse(record.lookup_list_index.get()).is_none() {
563 continue;
564 }
565
566 let new_len = ctx.buffer.backtrack_len() + ctx.buffer.lookahead_len();
567 let mut delta = new_len as isize - orig_len as isize;
568 if delta == 0 {
569 continue;
570 }
571
572 end += delta;
596 if end < ctx.match_positions[idx] as isize {
597 delta += ctx.match_positions[idx] as isize - end;
606 end = ctx.match_positions[idx] as isize;
607 }
608
609 let mut next = idx + 1;
611
612 if delta > 0 {
613 if delta as usize + count > MAX_CONTEXT_LENGTH {
614 break;
615 }
616
617 if delta as usize + count > ctx.match_positions.len() {
618 ctx.match_positions.resize(delta as usize + count, 0);
619 }
620 } else {
621 delta = delta.max(next as isize - count as isize);
623 next = (next as isize - delta) as _;
624 }
625
626 ctx.match_positions
628 .copy_within(next..count, (next as isize + delta) as _);
629 next = (next as isize + delta) as _;
630 count = (count as isize + delta) as _;
631 ctx.match_positions_len = count;
632
633 for j in idx + 1..next {
635 ctx.match_positions[j] = ctx.match_positions[j - 1] + 1;
636 }
637
638 while next < count {
640 ctx.match_positions[next] = (ctx.match_positions[next] as isize + delta) as _;
641 next += 1;
642 }
643 }
644
645 ctx.buffer.move_to(end.try_into().unwrap());
646}
647
648pub trait WouldApply {
650 fn would_apply(&self, ctx: &WouldApplyContext) -> bool;
652}
653
654pub(crate) type MappingCache = hb_cache_t<
657 16, 8, 256, 16, >;
662
663pub(crate) type BinaryCache = hb_cache_t<
664 15, 1, 256, 8, >;
669
670#[derive(Copy, Clone, PartialEq, Eq, Debug)]
671pub(crate) enum SubtableExternalCacheMode {
672 #[allow(unused)]
673 None,
674 Small,
675 Full,
676}
677
678pub(crate) struct LigatureSubstFormat1Cache {
679 pub seconds: hb_set_digest_t,
680 pub coverage: MappingCache,
681}
682
683impl LigatureSubstFormat1Cache {
684 pub fn new(seconds: hb_set_digest_t) -> Self {
685 LigatureSubstFormat1Cache {
686 coverage: MappingCache::new(),
687 seconds,
688 }
689 }
690}
691
692pub(crate) struct LigatureSubstFormat1SmallCache {
693 pub coverage: CoverageInfo,
694 pub seconds: hb_set_digest_t,
695}
696
697pub(crate) struct PairPosFormat1Cache {
698 pub coverage: MappingCache,
699}
700
701impl PairPosFormat1Cache {
702 pub fn new() -> Self {
703 PairPosFormat1Cache {
704 coverage: MappingCache::new(),
705 }
706 }
707}
708
709pub(crate) struct PairPosFormat1SmallCache {
710 pub coverage: CoverageInfo,
711}
712
713pub(crate) struct PairPosFormat2Cache {
714 pub coverage: MappingCache,
715 pub first: MappingCache,
716 pub second: MappingCache,
717}
718
719impl PairPosFormat2Cache {
720 pub fn new() -> Self {
721 PairPosFormat2Cache {
722 coverage: MappingCache::new(),
723 first: MappingCache::new(),
724 second: MappingCache::new(),
725 }
726 }
727}
728
729pub(crate) struct PairPosFormat2SmallCache {
730 pub coverage: CoverageInfo,
731 pub first: ClassDefInfo,
732 pub second: ClassDefInfo,
733}
734
735pub(crate) struct ContextFormat2Cache {
736 pub coverage: CoverageInfo,
737 pub input: ClassDefInfo,
738 pub coverage_cache: BinaryCache,
739}
740
741pub(crate) struct ChainContextFormat2Cache {
742 pub coverage: CoverageInfo,
743 pub backtrack: ClassDefInfo,
744 pub input: ClassDefInfo,
745 pub lookahead: ClassDefInfo,
746 pub coverage_cache: BinaryCache,
747}
748
749pub(crate) enum SubtableExternalCache {
750 None,
751 LigatureSubstFormat1Cache(Box<LigatureSubstFormat1Cache>),
752 LigatureSubstFormat1SmallCache(LigatureSubstFormat1SmallCache),
753 PairPosFormat1Cache(Box<PairPosFormat1Cache>),
754 PairPosFormat1SmallCache(PairPosFormat1SmallCache),
755 PairPosFormat2Cache(Box<PairPosFormat2Cache>),
756 PairPosFormat2SmallCache(PairPosFormat2SmallCache),
757 ContextFormat2Cache(ContextFormat2Cache),
758 ChainContextFormat2Cache(ChainContextFormat2Cache),
759}
760
761pub trait Apply {
763 fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> {
764 self.apply_with_external_cache(ctx, &SubtableExternalCache::None)
766 }
767
768 fn apply_with_external_cache(
771 &self,
772 ctx: &mut hb_ot_apply_context_t,
773 _external_cache: &SubtableExternalCache,
774 ) -> Option<()> {
775 self.apply(ctx)
777 }
778
779 fn apply_cached(
780 &self,
781 ctx: &mut hb_ot_apply_context_t,
782 external_cache: &SubtableExternalCache,
783 ) -> Option<()> {
784 self.apply_with_external_cache(ctx, external_cache)
787 }
788
789 fn cache_cost(&self) -> u32 {
790 0
793 }
794
795 fn external_cache_create(&self, mode: SubtableExternalCacheMode) -> SubtableExternalCache {
796 let _ = mode;
799 SubtableExternalCache::None
800 }
801}
802
803pub struct WouldApplyContext<'a> {
804 pub glyphs: &'a [GlyphId],
805 pub zero_context: bool,
806}
807
808pub mod OT {
809 use super::*;
810
811 fn match_properties_mark(
812 face: &hb_font_t,
813 info: &GlyphInfo,
814 glyph_props: u16,
815 match_props: u32,
816 ) -> bool {
817 if match_props as u16 & lookup_flags::USE_MARK_FILTERING_SET != 0 {
820 let set_index = (match_props >> 16) as u16;
821 return face
822 .ot_tables
823 .is_mark_glyph(info.as_glyph().to_u32(), set_index);
824 }
825
826 if match_props as u16 & lookup_flags::MARK_ATTACHMENT_TYPE_MASK != 0 {
830 return (match_props as u16 & lookup_flags::MARK_ATTACHMENT_TYPE_MASK)
831 == (glyph_props & lookup_flags::MARK_ATTACHMENT_TYPE_MASK);
832 }
833
834 true
835 }
836
837 #[inline(always)]
838 pub fn check_glyph_property(face: &hb_font_t, info: &GlyphInfo, match_props: u32) -> bool {
839 let glyph_props = info.glyph_props();
840
841 if glyph_props & match_props as u16 & lookup_flags::IGNORE_FLAGS != 0 {
844 return false;
845 }
846
847 if glyph_props & GlyphPropsFlags::MARK.bits() != 0 {
848 return match_properties_mark(face, info, glyph_props, match_props);
849 }
850
851 true
852 }
853
854 pub struct hb_ot_apply_context_t<'a> {
855 pub table_index: TableIndex,
856 pub face: &'a hb_font_t<'a>,
857 pub scale: Scale,
858 pub buffer: &'a mut hb_buffer_t,
859 lookup_mask: hb_mask_t,
860 pub per_syllable: bool,
861 pub lookup_index: u16,
862 pub lookup_props: u32,
863 pub nesting_level_left: usize,
864 pub auto_zwnj: bool,
865 pub auto_zwj: bool,
866 pub random: bool,
867 pub random_state: u32,
868 pub new_syllables: Option<u8>,
869 pub last_base: i32,
870 pub last_base_until: u32,
871 pub(crate) matcher: matcher_t,
872 pub(crate) context_matcher: matcher_t,
873 pub(crate) match_positions_len: usize,
874 pub(crate) match_positions: MatchPositions,
875 }
876
877 impl<'a> hb_ot_apply_context_t<'a> {
878 pub fn new(
879 table_index: TableIndex,
880 face: &'a hb_font_t<'a>,
881 scale: Scale,
882 buffer: &'a mut hb_buffer_t,
883 ) -> Self {
884 Self {
885 table_index,
886 face,
887 scale,
888 buffer,
889 lookup_mask: 1,
890 per_syllable: false,
891 lookup_index: u16::MAX,
892 lookup_props: u32::MAX,
893 nesting_level_left: MAX_NESTING_LEVEL,
894 auto_zwnj: true,
895 auto_zwj: true,
896 random: false,
897 random_state: 1,
898 new_syllables: None,
899 last_base: -1,
900 last_base_until: 0,
901 matcher: matcher_t::default(),
902 context_matcher: matcher_t::default(),
903 match_positions_len: 0,
904 match_positions: MatchPositions::from_elem(0, 1),
905 }
906 }
907
908 #[inline(always)]
909 pub fn scale_x(&self, value: f32) -> i32 {
910 self.scale.scale_x_f(value)
911 }
912
913 #[inline(always)]
914 pub fn scale_y(&self, value: f32) -> i32 {
915 self.scale.scale_y_f(value)
916 }
917
918 pub fn random_number(&mut self) -> u32 {
919 self.random_state = self.random_state.wrapping_mul(48271) % (i32::MAX as u32);
921 self.random_state
922 }
923
924 pub fn set_lookup_mask(&mut self, mask: hb_mask_t) {
925 self.lookup_mask = mask;
926 self.last_base = -1;
927 self.last_base_until = 0;
928 }
929
930 pub fn lookup_mask(&self) -> hb_mask_t {
931 self.lookup_mask
932 }
933
934 pub fn update_matchers(&mut self) {
935 self.matcher = matcher_t::new(self, false);
936 self.context_matcher = matcher_t::new(self, true);
937 }
938
939 pub fn recurse(&mut self, sub_lookup_index: u16) -> Option<()> {
940 if self.nesting_level_left == 0 {
941 self.buffer.successful = false;
942 return None;
943 }
944
945 self.buffer.max_ops -= 1;
946 if self.buffer.max_ops < 0 {
947 self.buffer.successful = false;
948 return None;
949 }
950
951 self.nesting_level_left -= 1;
952 let saved_props = self.lookup_props;
953 let saved_index = self.lookup_index;
954
955 self.match_positions.resize(self.match_positions_len, 0);
956 let saved_match_positions = self.match_positions.clone();
957 let saved_match_positions_len = self.match_positions_len;
958
959 self.lookup_index = sub_lookup_index;
960 let applied = self
961 .face
962 .ot_tables
963 .table_data_and_lookup(self.table_index, sub_lookup_index)
964 .and_then(|(table_data, lookup)| {
965 self.lookup_props = lookup.props();
966 self.update_matchers();
967 lookup.apply(self, table_data, false)
968 });
969 self.lookup_props = saved_props;
970 self.lookup_index = saved_index;
971 self.update_matchers();
972 self.match_positions = saved_match_positions;
973 self.match_positions_len = saved_match_positions_len;
974 self.nesting_level_left += 1;
975 applied
976 }
977
978 fn set_glyph_class(
979 &mut self,
980 glyph_id: GlyphId,
981 class_guess: GlyphPropsFlags,
982 ligature: bool,
983 component: bool,
984 ) {
985 self.buffer.digest.add(glyph_id.into());
986
987 if let Some(syllable) = self.new_syllables {
988 self.buffer.cur_mut(0).set_syllable(syllable);
989 }
990
991 let cur = self.buffer.cur_mut(0);
992 let mut props = cur.glyph_props();
993
994 props |= GlyphPropsFlags::SUBSTITUTED.bits();
995
996 if ligature {
997 props |= GlyphPropsFlags::LIGATED.bits();
998 props &= !GlyphPropsFlags::MULTIPLIED.bits();
1004 }
1005
1006 if component {
1007 props |= GlyphPropsFlags::MULTIPLIED.bits();
1008 }
1009
1010 let has_glyph_classes = self.face.ot_tables.has_glyph_classes();
1011
1012 if has_glyph_classes {
1013 props &= GlyphPropsFlags::PRESERVE.bits();
1014 cur.set_glyph_props(props | self.face.ot_tables.glyph_props(glyph_id));
1015 } else if !class_guess.is_empty() {
1016 props &= GlyphPropsFlags::PRESERVE.bits();
1017 cur.set_glyph_props(props | class_guess.bits());
1018 } else {
1019 cur.set_glyph_props(props);
1020 }
1021 }
1022
1023 pub fn replace_glyph(&mut self, glyph_id: GlyphId) {
1024 self.set_glyph_class(glyph_id, GlyphPropsFlags::empty(), false, false);
1025 self.buffer.replace_glyph(u32::from(glyph_id));
1026 }
1027
1028 pub fn replace_glyph_inplace(&mut self, glyph_id: GlyphId) {
1029 self.set_glyph_class(glyph_id, GlyphPropsFlags::empty(), false, false);
1030 self.buffer.cur_mut(0).glyph_id = u32::from(glyph_id);
1031 }
1032
1033 pub fn replace_glyph_with_ligature(
1034 &mut self,
1035 glyph_id: GlyphId,
1036 class_guess: GlyphPropsFlags,
1037 ) {
1038 self.set_glyph_class(glyph_id, class_guess, true, false);
1039 self.buffer.replace_glyph(u32::from(glyph_id));
1040 }
1041
1042 pub fn output_glyph_for_component(
1043 &mut self,
1044 glyph_id: GlyphId,
1045 class_guess: GlyphPropsFlags,
1046 ) {
1047 self.set_glyph_class(glyph_id, class_guess, false, true);
1048 self.buffer.output_glyph(u32::from(glyph_id));
1049 }
1050 }
1051}
1052
1053use OT::hb_ot_apply_context_t;
1054
1055pub fn ligate_input(
1056 ctx: &mut hb_ot_apply_context_t,
1057 count: usize,
1059 match_end: usize,
1061 total_component_count: u8,
1062 lig_glyph: GlyphId,
1063) {
1064 ctx.buffer.merge_clusters(ctx.buffer.idx, match_end);
1097
1098 let mut is_base_ligature = ctx.buffer.info[ctx.match_positions[0] as usize].is_base_glyph();
1099 let mut is_mark_ligature = ctx.buffer.info[ctx.match_positions[0] as usize].is_mark();
1100 for i in 1..count {
1101 if !ctx.buffer.info[ctx.match_positions[i] as usize].is_mark() {
1102 is_base_ligature = false;
1103 is_mark_ligature = false;
1104 }
1105 }
1106
1107 let is_ligature = !is_base_ligature && !is_mark_ligature;
1108 let class = if is_ligature {
1109 GlyphPropsFlags::LIGATURE
1110 } else {
1111 GlyphPropsFlags::empty()
1112 };
1113 let lig_id = if is_ligature {
1114 ctx.buffer.allocate_lig_id()
1115 } else {
1116 0
1117 };
1118 let first = ctx.buffer.cur_mut(0);
1119 let mut last_lig_id = first.lig_id();
1120 let mut last_num_comps = first.lig_num_comps();
1121 let mut comps_so_far = last_num_comps;
1122
1123 if is_ligature {
1124 first.set_lig_props_for_ligature(lig_id, total_component_count);
1125 if first.general_category() == GeneralCategory::NON_SPACING_MARK {
1126 first.set_general_category(GeneralCategory::OTHER_LETTER);
1127 }
1128 }
1129
1130 ctx.replace_glyph_with_ligature(lig_glyph, class);
1131
1132 for i in 1..count {
1133 while ctx.buffer.idx < ctx.match_positions[i] as usize && ctx.buffer.successful {
1134 if is_ligature {
1135 let cur = ctx.buffer.cur_mut(0);
1136 let mut this_comp = cur.lig_comp();
1137 if this_comp == 0 {
1138 this_comp = last_num_comps;
1139 }
1140 debug_assert!(comps_so_far >= last_num_comps);
1143 let new_lig_comp = comps_so_far - last_num_comps + this_comp.min(last_num_comps);
1144 cur.set_lig_props_for_mark(lig_id, new_lig_comp);
1145 }
1146 ctx.buffer.next_glyph();
1147 }
1148
1149 let cur = ctx.buffer.cur(0);
1150 last_lig_id = cur.lig_id();
1151 last_num_comps = cur.lig_num_comps();
1152 comps_so_far += last_num_comps;
1153
1154 ctx.buffer.idx += 1;
1156 }
1157
1158 if !is_mark_ligature && last_lig_id != 0 {
1159 for i in ctx.buffer.idx..ctx.buffer.len {
1161 let info = &mut ctx.buffer.info[i];
1162 if last_lig_id != info.lig_id() {
1163 break;
1164 }
1165
1166 let this_comp = info.lig_comp();
1167 if this_comp == 0 {
1168 break;
1169 }
1170
1171 debug_assert!(comps_so_far >= last_num_comps);
1174 let new_lig_comp = comps_so_far - last_num_comps + this_comp.min(last_num_comps);
1175 info.set_lig_props_for_mark(lig_id, new_lig_comp);
1176 }
1177 }
1178}