1use alloc::boxed::Box;
2use core::cmp;
3use core::convert::TryFrom;
4use core::ops::Range;
5
6use ttf_parser::GlyphId;
7
8use super::algs::*;
9use super::buffer::hb_buffer_t;
10use super::ot_layout::*;
11use super::ot_layout_gsubgpos::{WouldApply, WouldApplyContext};
12use super::ot_map::*;
13use super::ot_shape::*;
14use super::ot_shape_complex::*;
15use super::ot_shape_normalize::*;
16use super::ot_shape_plan::hb_ot_shape_plan_t;
17use super::unicode::{hb_gc, CharExt, GeneralCategoryExt};
18use super::{hb_font_t, hb_glyph_info_t, hb_mask_t, hb_tag_t, script, Script};
19
20pub const INDIC_SHAPER: hb_ot_complex_shaper_t = hb_ot_complex_shaper_t {
21 collect_features: Some(collect_features),
22 override_features: Some(override_features),
23 create_data: Some(|plan| Box::new(IndicShapePlan::new(plan))),
24 preprocess_text: Some(preprocess_text),
25 postprocess_glyphs: None,
26 normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
27 decompose: Some(decompose),
28 compose: Some(compose),
29 setup_masks: Some(setup_masks),
30 gpos_tag: None,
31 reorder_marks: None,
32 zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
33 fallback_position: false,
34};
35
36pub type Category = u8;
37pub mod category {
38 pub const X: u8 = 0;
39 pub const C: u8 = 1;
40 pub const V: u8 = 2;
41 pub const N: u8 = 3;
42 pub const H: u8 = 4;
43 pub const ZWNJ: u8 = 5;
44 pub const ZWJ: u8 = 6;
45 pub const M: u8 = 7;
46 pub const SM: u8 = 8;
47 pub const A: u8 = 10;
49 pub const PLACEHOLDER: u8 = 11;
50 pub const DOTTED_CIRCLE: u8 = 12;
51 pub const RS: u8 = 13; pub const COENG: u8 = 14; pub const REPHA: u8 = 15; pub const RA: u8 = 16;
55 pub const CM: u8 = 17; pub const SYMBOL: u8 = 18; pub const CS: u8 = 19;
58 pub const ROBATIC: u8 = 20;
59 pub const X_GROUP: u8 = 21;
60 pub const Y_GROUP: u8 = 22;
61 pub const MW: u8 = 23;
62 pub const MY: u8 = 24;
63 pub const PT: u8 = 25;
64 pub const V_AVB: u8 = 26;
66 pub const V_BLW: u8 = 27;
67 pub const V_PRE: u8 = 28;
68 pub const V_PST: u8 = 29;
69 pub const VS: u8 = 30; pub const P: u8 = 31; pub const D: u8 = 32; pub const ML: u8 = 33; }
74
75pub type Position = u8;
76pub mod position {
77 pub const START: u8 = 0;
78 pub const RA_TO_BECOME_REPH: u8 = 1;
79 pub const PRE_M: u8 = 2;
80 pub const PRE_C: u8 = 3;
81 pub const BASE_C: u8 = 4;
82 pub const AFTER_MAIN: u8 = 5;
83 pub const ABOVE_C: u8 = 6;
84 pub const BEFORE_SUB: u8 = 7;
85 pub const BELOW_C: u8 = 8;
86 pub const AFTER_SUB: u8 = 9;
87 pub const BEFORE_POST: u8 = 10;
88 pub const POST_C: u8 = 11;
89 pub const AFTER_POST: u8 = 12;
90 pub const FINAL_C: u8 = 13;
91 pub const SMVD: u8 = 14;
92 pub const END: u8 = 15;
93}
94
95#[allow(dead_code)]
96#[derive(Clone, Copy, PartialEq)]
97pub enum SyllabicCategory {
98 Other,
99 Avagraha,
100 Bindu,
101 BrahmiJoiningNumber,
102 CantillationMark,
103 Consonant,
104 ConsonantDead,
105 ConsonantFinal,
106 ConsonantHeadLetter,
107 ConsonantInitialPostfixed,
108 ConsonantKiller,
109 ConsonantMedial,
110 ConsonantPlaceholder,
111 ConsonantPrecedingRepha,
112 ConsonantPrefixed,
113 ConsonantSubjoined,
114 ConsonantSucceedingRepha,
115 ConsonantWithStacker,
116 GeminationMark,
117 InvisibleStacker,
118 Joiner,
119 ModifyingLetter,
120 NonJoiner,
121 Nukta,
122 Number,
123 NumberJoiner,
124 PureKiller,
125 RegisterShifter,
126 SyllableModifier,
127 ToneLetter,
128 ToneMark,
129 Virama,
130 Visarga,
131 Vowel,
132 VowelDependent,
133 VowelIndependent,
134}
135
136#[allow(dead_code)]
137#[derive(Clone, Copy)]
138pub enum MatraCategory {
139 NotApplicable,
140 Left,
141 Top,
142 Bottom,
143 Right,
144 BottomAndLeft,
145 BottomAndRight,
146 LeftAndRight,
147 TopAndBottom,
148 TopAndBottomAndRight,
149 TopAndBottomAndLeft,
150 TopAndLeft,
151 TopAndLeftAndRight,
152 TopAndRight,
153 Overstruck,
154 VisualOrderLeft,
155}
156
157const INDIC_FEATURES: &[(hb_tag_t, hb_ot_map_feature_flags_t)] = &[
158 (
162 hb_tag_t::from_bytes(b"nukt"),
163 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
164 ),
165 (
166 hb_tag_t::from_bytes(b"akhn"),
167 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
168 ),
169 (
170 hb_tag_t::from_bytes(b"rphf"),
171 F_MANUAL_JOINERS | F_PER_SYLLABLE,
172 ),
173 (
174 hb_tag_t::from_bytes(b"rkrf"),
175 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
176 ),
177 (
178 hb_tag_t::from_bytes(b"pref"),
179 F_MANUAL_JOINERS | F_PER_SYLLABLE,
180 ),
181 (
182 hb_tag_t::from_bytes(b"blwf"),
183 F_MANUAL_JOINERS | F_PER_SYLLABLE,
184 ),
185 (
186 hb_tag_t::from_bytes(b"abvf"),
187 F_MANUAL_JOINERS | F_PER_SYLLABLE,
188 ),
189 (
190 hb_tag_t::from_bytes(b"half"),
191 F_MANUAL_JOINERS | F_PER_SYLLABLE,
192 ),
193 (
194 hb_tag_t::from_bytes(b"pstf"),
195 F_MANUAL_JOINERS | F_PER_SYLLABLE,
196 ),
197 (
198 hb_tag_t::from_bytes(b"vatu"),
199 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
200 ),
201 (
202 hb_tag_t::from_bytes(b"cjct"),
203 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
204 ),
205 (
211 hb_tag_t::from_bytes(b"init"),
212 F_MANUAL_JOINERS | F_PER_SYLLABLE,
213 ),
214 (
215 hb_tag_t::from_bytes(b"pres"),
216 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
217 ),
218 (
219 hb_tag_t::from_bytes(b"abvs"),
220 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
221 ),
222 (
223 hb_tag_t::from_bytes(b"blws"),
224 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
225 ),
226 (
227 hb_tag_t::from_bytes(b"psts"),
228 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
229 ),
230 (
231 hb_tag_t::from_bytes(b"haln"),
232 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
233 ),
234];
235
236#[allow(dead_code)]
238mod indic_feature {
239 pub const NUKT: usize = 0;
240 pub const AKHN: usize = 1;
241 pub const RPHF: usize = 2;
242 pub const RKRF: usize = 3;
243 pub const PREF: usize = 4;
244 pub const BLWF: usize = 5;
245 pub const ABVF: usize = 6;
246 pub const HALF: usize = 7;
247 pub const PSTF: usize = 8;
248 pub const VATU: usize = 9;
249 pub const CJCT: usize = 10;
250 pub const INIT: usize = 11;
251 pub const PRES: usize = 12;
252 pub const ABVS: usize = 13;
253 pub const BLWS: usize = 14;
254 pub const PSTS: usize = 15;
255 pub const HALN: usize = 16;
256}
257
258const fn category_flag(c: Category) -> u32 {
259 rb_flag(c as u32)
260}
261
262const MEDIAL_FLAGS: u32 = category_flag(category::CM);
263const CONSONANT_FLAGS: u32 = category_flag(category::C)
269 | category_flag(category::CS)
270 | category_flag(category::RA)
271 | MEDIAL_FLAGS
272 | category_flag(category::V)
273 | category_flag(category::PLACEHOLDER)
274 | category_flag(category::DOTTED_CIRCLE);
275const JOINER_FLAGS: u32 = category_flag(category::ZWJ) | category_flag(category::ZWNJ);
276
277const RA_CHARS: &[u32] = &[
280 0x0930, 0x09B0, 0x09F0, 0x0A30, 0x0AB0, 0x0B30, 0x0BB0, 0x0C30, 0x0CB0, 0x0D30, 0x0DBB, ];
292
293#[derive(Clone, Copy, PartialEq)]
294enum BasePosition {
295 LastSinhala,
296 Last,
297}
298
299#[derive(Clone, Copy, PartialEq)]
300enum RephPosition {
301 AfterMain = position::AFTER_MAIN as isize,
302 BeforeSub = position::BEFORE_SUB as isize,
303 AfterSub = position::AFTER_SUB as isize,
304 BeforePost = position::BEFORE_POST as isize,
305 AfterPost = position::AFTER_POST as isize,
306}
307
308#[derive(Clone, Copy, PartialEq)]
309enum RephMode {
310 Implicit,
312 Explicit,
314 LogRepha,
316}
317
318#[derive(Clone, Copy, PartialEq)]
319enum BlwfMode {
320 PreAndPost,
322 PostOnly,
324}
325
326#[derive(Clone, Copy)]
327struct IndicConfig {
328 script: Option<Script>,
329 has_old_spec: bool,
330 virama: u32,
331 base_pos: BasePosition,
332 reph_pos: RephPosition,
333 reph_mode: RephMode,
334 blwf_mode: BlwfMode,
335}
336
337impl IndicConfig {
338 const fn new(
339 script: Option<Script>,
340 has_old_spec: bool,
341 virama: u32,
342 base_pos: BasePosition,
343 reph_pos: RephPosition,
344 reph_mode: RephMode,
345 blwf_mode: BlwfMode,
346 ) -> Self {
347 IndicConfig {
348 script,
349 has_old_spec,
350 virama,
351 base_pos,
352 reph_pos,
353 reph_mode,
354 blwf_mode,
355 }
356 }
357}
358
359const INDIC_CONFIGS: &[IndicConfig] = &[
360 IndicConfig::new(
361 None,
362 false,
363 0,
364 BasePosition::Last,
365 RephPosition::BeforePost,
366 RephMode::Implicit,
367 BlwfMode::PreAndPost,
368 ),
369 IndicConfig::new(
370 Some(script::DEVANAGARI),
371 true,
372 0x094D,
373 BasePosition::Last,
374 RephPosition::BeforePost,
375 RephMode::Implicit,
376 BlwfMode::PreAndPost,
377 ),
378 IndicConfig::new(
379 Some(script::BENGALI),
380 true,
381 0x09CD,
382 BasePosition::Last,
383 RephPosition::AfterSub,
384 RephMode::Implicit,
385 BlwfMode::PreAndPost,
386 ),
387 IndicConfig::new(
388 Some(script::GURMUKHI),
389 true,
390 0x0A4D,
391 BasePosition::Last,
392 RephPosition::BeforeSub,
393 RephMode::Implicit,
394 BlwfMode::PreAndPost,
395 ),
396 IndicConfig::new(
397 Some(script::GUJARATI),
398 true,
399 0x0ACD,
400 BasePosition::Last,
401 RephPosition::BeforePost,
402 RephMode::Implicit,
403 BlwfMode::PreAndPost,
404 ),
405 IndicConfig::new(
406 Some(script::ORIYA),
407 true,
408 0x0B4D,
409 BasePosition::Last,
410 RephPosition::AfterMain,
411 RephMode::Implicit,
412 BlwfMode::PreAndPost,
413 ),
414 IndicConfig::new(
415 Some(script::TAMIL),
416 true,
417 0x0BCD,
418 BasePosition::Last,
419 RephPosition::AfterPost,
420 RephMode::Implicit,
421 BlwfMode::PreAndPost,
422 ),
423 IndicConfig::new(
424 Some(script::TELUGU),
425 true,
426 0x0C4D,
427 BasePosition::Last,
428 RephPosition::AfterPost,
429 RephMode::Explicit,
430 BlwfMode::PostOnly,
431 ),
432 IndicConfig::new(
433 Some(script::KANNADA),
434 true,
435 0x0CCD,
436 BasePosition::Last,
437 RephPosition::AfterPost,
438 RephMode::Implicit,
439 BlwfMode::PostOnly,
440 ),
441 IndicConfig::new(
442 Some(script::MALAYALAM),
443 true,
444 0x0D4D,
445 BasePosition::Last,
446 RephPosition::AfterMain,
447 RephMode::LogRepha,
448 BlwfMode::PreAndPost,
449 ),
450 IndicConfig::new(
451 Some(script::SINHALA),
452 false,
453 0x0DCA,
454 BasePosition::LastSinhala,
455 RephPosition::AfterPost,
456 RephMode::Explicit,
457 BlwfMode::PreAndPost,
458 ),
459];
460
461struct IndicWouldSubstituteFeature {
462 lookups: Range<usize>,
463 zero_context: bool,
464}
465
466impl IndicWouldSubstituteFeature {
467 pub fn new(map: &hb_ot_map_t, feature_tag: hb_tag_t, zero_context: bool) -> Self {
468 IndicWouldSubstituteFeature {
469 lookups: match map.get_feature_stage(TableIndex::GSUB, feature_tag) {
470 Some(stage) => map.stage_lookup_range(TableIndex::GSUB, stage),
471 None => 0..0,
472 },
473 zero_context,
474 }
475 }
476
477 pub fn would_substitute(
478 &self,
479 map: &hb_ot_map_t,
480 face: &hb_font_t,
481 glyphs: &[GlyphId],
482 ) -> bool {
483 for index in self.lookups.clone() {
484 let lookup = map.lookup(TableIndex::GSUB, index);
485 let ctx = WouldApplyContext {
486 glyphs,
487 zero_context: self.zero_context,
488 };
489 if face
490 .gsub
491 .as_ref()
492 .and_then(|table| table.get_lookup(lookup.index))
493 .map_or(false, |lookup| lookup.would_apply(&ctx))
494 {
495 return true;
496 }
497 }
498
499 false
500 }
501}
502
503struct IndicShapePlan {
504 config: IndicConfig,
505 is_old_spec: bool,
506 rphf: IndicWouldSubstituteFeature,
508 pref: IndicWouldSubstituteFeature,
509 blwf: IndicWouldSubstituteFeature,
510 pstf: IndicWouldSubstituteFeature,
511 vatu: IndicWouldSubstituteFeature,
512 mask_array: [hb_mask_t; INDIC_FEATURES.len()],
513}
514
515impl IndicShapePlan {
516 fn new(plan: &hb_ot_shape_plan_t) -> Self {
517 let script = plan.script;
518 let config = if let Some(c) = INDIC_CONFIGS.iter().skip(1).find(|c| c.script == script) {
519 *c
520 } else {
521 INDIC_CONFIGS[0]
522 };
523
524 let is_old_spec = config.has_old_spec
525 && plan
526 .ot_map
527 .chosen_script(TableIndex::GSUB)
528 .map_or(true, |tag| tag.to_bytes()[3] != b'2');
529
530 let zero_context = is_old_spec && script != Some(script::MALAYALAM);
539
540 let mut mask_array = [0; INDIC_FEATURES.len()];
541 for (i, feature) in INDIC_FEATURES.iter().enumerate() {
542 mask_array[i] = if feature.1 & F_GLOBAL != 0 {
543 0
544 } else {
545 plan.ot_map.get_1_mask(feature.0)
546 }
547 }
548
549 IndicShapePlan {
558 config,
559 is_old_spec,
560 rphf: IndicWouldSubstituteFeature::new(
562 &plan.ot_map,
563 hb_tag_t::from_bytes(b"rphf"),
564 zero_context,
565 ),
566 pref: IndicWouldSubstituteFeature::new(
567 &plan.ot_map,
568 hb_tag_t::from_bytes(b"pref"),
569 zero_context,
570 ),
571 blwf: IndicWouldSubstituteFeature::new(
572 &plan.ot_map,
573 hb_tag_t::from_bytes(b"blwf"),
574 zero_context,
575 ),
576 pstf: IndicWouldSubstituteFeature::new(
577 &plan.ot_map,
578 hb_tag_t::from_bytes(b"pstf"),
579 zero_context,
580 ),
581 vatu: IndicWouldSubstituteFeature::new(
582 &plan.ot_map,
583 hb_tag_t::from_bytes(b"vatu"),
584 zero_context,
585 ),
586 mask_array,
587 }
588 }
589}
590
591impl hb_glyph_info_t {
592 pub(crate) fn indic_category(&self) -> Category {
593 self.complex_var_u8_category()
594 }
595
596 pub(crate) fn set_indic_category(&mut self, c: Category) {
597 self.set_complex_var_u8_category(c)
598 }
599
600 pub(crate) fn indic_position(&self) -> Position {
601 self.complex_var_u8_auxiliary()
602 }
603
604 pub(crate) fn set_indic_position(&mut self, c: Position) {
605 self.set_complex_var_u8_auxiliary(c)
606 }
607
608 fn is_one_of(&self, flags: u32) -> bool {
609 if _hb_glyph_info_ligated(self) {
611 return false;
612 }
613
614 rb_flag_unsafe(self.indic_category() as u32) & flags != 0
615 }
616
617 fn is_joiner(&self) -> bool {
618 self.is_one_of(JOINER_FLAGS)
619 }
620
621 pub(crate) fn is_consonant(&self) -> bool {
622 self.is_one_of(CONSONANT_FLAGS)
623 }
624
625 fn is_halant(&self) -> bool {
626 self.is_one_of(rb_flag(category::H as u32))
627 }
628
629 fn set_indic_properties(&mut self) {
630 let u = self.glyph_id;
631 let (mut cat, mut pos) = get_category_and_position(u);
632
633 match u {
637 0x0953..=0x0954 => cat = category::SM,
638 0x0A72..=0x0A73 | 0x1CF5..=0x1CF6 => cat = category::C,
640 0x1CE2..=0x1CE8 => cat = category::A,
643 0x1CED => cat = category::A,
647 0xA8F2..=0xA8F7 | 0x1CE9..=0x1CEC | 0x1CEE..=0x1CF1 => cat = category::SYMBOL,
649 0x0A51 => {
651 cat = category::M;
652 pos = position::BELOW_C;
653 }
654 0x11301 | 0x11303 => cat = category::SM,
657 0x1133B | 0x1133C => cat = category::N,
658 0x0AFB => cat = category::N,
660 0x0B55 => cat = category::N,
662 0x0980 => cat = category::PLACEHOLDER,
664 0x09FC => cat = category::PLACEHOLDER,
666 0x0C80 => cat = category::PLACEHOLDER,
668 0x0D04 => cat = category::PLACEHOLDER,
669 0x2010 | 0x2011 => cat = category::PLACEHOLDER,
670 0x25CC => cat = category::DOTTED_CIRCLE,
671 _ => {}
672 }
673
674 if (rb_flag_unsafe(cat as u32) & CONSONANT_FLAGS) != 0 {
677 pos = position::BASE_C;
678 if RA_CHARS.contains(&u) {
679 cat = category::RA;
680 }
681 } else if cat == category::M {
682 pos = matra_position_indic(u, pos);
683 } else if (rb_flag_unsafe(cat as u32)
684 & (category_flag(category::SM)
685 | category_flag(category::A)
686 | category_flag(category::SYMBOL)))
687 != 0
688 {
689 pos = position::SMVD;
690 }
691
692 if u == 0x0B01 {
694 pos = position::BEFORE_SUB;
695 }
696
697 self.set_indic_category(cat);
698 self.set_indic_position(pos);
699 }
700}
701
702fn collect_features(planner: &mut hb_ot_shape_planner_t) {
703 planner.ot_map.add_gsub_pause(Some(setup_syllables));
705
706 planner
707 .ot_map
708 .enable_feature(hb_tag_t::from_bytes(b"locl"), F_PER_SYLLABLE, 1);
709 planner
712 .ot_map
713 .enable_feature(hb_tag_t::from_bytes(b"ccmp"), F_PER_SYLLABLE, 1);
714
715 planner.ot_map.add_gsub_pause(Some(initial_reordering));
716
717 for feature in INDIC_FEATURES.iter().take(10) {
718 planner.ot_map.add_feature(feature.0, feature.1, 1);
719 planner.ot_map.add_gsub_pause(None);
720 }
721
722 planner.ot_map.add_gsub_pause(Some(final_reordering));
723
724 for feature in INDIC_FEATURES.iter().skip(10) {
725 planner.ot_map.add_feature(feature.0, feature.1, 1);
726 }
727}
728
729fn override_features(planner: &mut hb_ot_shape_planner_t) {
730 planner
731 .ot_map
732 .disable_feature(hb_tag_t::from_bytes(b"liga"));
733}
734
735fn preprocess_text(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) {
736 super::ot_shape_complex_vowel_constraints::preprocess_text_vowel_constraints(buffer);
737}
738
739fn decompose(ctx: &hb_ot_shape_normalize_context_t, ab: char) -> Option<(char, char)> {
740 match ab {
742 '\u{0931}' | '\u{09DC}' | '\u{09DD}' | '\u{0B94}' => return None, _ => {}
748 }
749
750 if ab == '\u{0DDA}' || ('\u{0DDC}'..='\u{0DDE}').contains(&ab) {
751 let mut ok = false;
776 if let Some(g) = ctx.face.get_nominal_glyph(u32::from(ab)) {
777 let indic_plan = ctx.plan.data::<IndicShapePlan>();
778 ok = indic_plan
779 .pstf
780 .would_substitute(&ctx.plan.ot_map, ctx.face, &[g]);
781 }
782
783 if ok {
784 return Some(('\u{0DD9}', ab));
786 }
787 }
788
789 crate::hb::unicode::decompose(ab)
790}
791
792fn compose(_: &hb_ot_shape_normalize_context_t, a: char, b: char) -> Option<char> {
793 if a.general_category().is_mark() {
795 return None;
796 }
797
798 if a == '\u{09AF}' && b == '\u{09BC}' {
800 return Some('\u{09DF}');
801 }
802
803 crate::hb::unicode::compose(a, b)
804}
805
806fn setup_masks(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) {
807 for info in buffer.info_slice_mut() {
810 info.set_indic_properties();
811 }
812}
813
814fn setup_syllables(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) {
815 super::ot_shape_complex_indic_machine::find_syllables_indic(buffer);
816
817 let mut start = 0;
818 let mut end = buffer.next_syllable(0);
819 while start < buffer.len {
820 buffer.unsafe_to_break(Some(start), Some(end));
821 start = end;
822 end = buffer.next_syllable(start);
823 }
824}
825
826fn initial_reordering(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) {
827 use super::ot_shape_complex_indic_machine::SyllableType;
828
829 let indic_plan = plan.data::<IndicShapePlan>();
830
831 update_consonant_positions(plan, indic_plan, face, buffer);
832 super::ot_shape_complex_syllabic::insert_dotted_circles(
833 face,
834 buffer,
835 SyllableType::BrokenCluster as u8,
836 category::DOTTED_CIRCLE,
837 Some(category::REPHA),
838 Some(position::END),
839 );
840
841 let mut start = 0;
842 let mut end = buffer.next_syllable(0);
843 while start < buffer.len {
844 initial_reordering_syllable(plan, indic_plan, face, start, end, buffer);
845 start = end;
846 end = buffer.next_syllable(start);
847 }
848}
849
850fn update_consonant_positions(
851 plan: &hb_ot_shape_plan_t,
852 indic_plan: &IndicShapePlan,
853 face: &hb_font_t,
854 buffer: &mut hb_buffer_t,
855) {
856 if indic_plan.config.base_pos != BasePosition::Last {
857 return;
858 }
859
860 let mut virama_glyph = None;
861 if indic_plan.config.virama != 0 {
862 virama_glyph = face.get_nominal_glyph(indic_plan.config.virama);
863 }
864
865 if let Some(virama) = virama_glyph {
866 for info in buffer.info_slice_mut() {
867 if info.indic_position() == position::BASE_C {
868 let consonant = info.as_glyph();
869 info.set_indic_position(consonant_position_from_face(
870 plan, indic_plan, face, consonant, virama,
871 ));
872 }
873 }
874 }
875}
876
877fn consonant_position_from_face(
878 plan: &hb_ot_shape_plan_t,
879 indic_plan: &IndicShapePlan,
880 face: &hb_font_t,
881 consonant: GlyphId,
882 virama: GlyphId,
883) -> u8 {
884 if indic_plan
899 .blwf
900 .would_substitute(&plan.ot_map, face, &[virama, consonant])
901 || indic_plan
902 .blwf
903 .would_substitute(&plan.ot_map, face, &[consonant, virama])
904 || indic_plan
905 .vatu
906 .would_substitute(&plan.ot_map, face, &[virama, consonant])
907 || indic_plan
908 .vatu
909 .would_substitute(&plan.ot_map, face, &[consonant, virama])
910 {
911 return position::BELOW_C;
912 }
913
914 if indic_plan
915 .pstf
916 .would_substitute(&plan.ot_map, face, &[virama, consonant])
917 || indic_plan
918 .pstf
919 .would_substitute(&plan.ot_map, face, &[consonant, virama])
920 {
921 return position::POST_C;
922 }
923
924 if indic_plan
925 .pref
926 .would_substitute(&plan.ot_map, face, &[virama, consonant])
927 || indic_plan
928 .pref
929 .would_substitute(&plan.ot_map, face, &[consonant, virama])
930 {
931 return position::POST_C;
932 }
933
934 position::BASE_C
935}
936
937fn initial_reordering_syllable(
938 plan: &hb_ot_shape_plan_t,
939 indic_plan: &IndicShapePlan,
940 face: &hb_font_t,
941 start: usize,
942 end: usize,
943 buffer: &mut hb_buffer_t,
944) {
945 use super::ot_shape_complex_indic_machine::SyllableType;
946
947 let syllable_type = match buffer.info[start].syllable() & 0x0F {
948 0 => SyllableType::ConsonantSyllable,
949 1 => SyllableType::VowelSyllable,
950 2 => SyllableType::StandaloneCluster,
951 3 => SyllableType::SymbolCluster,
952 4 => SyllableType::BrokenCluster,
953 5 => SyllableType::NonIndicCluster,
954 _ => unreachable!(),
955 };
956
957 match syllable_type {
958 SyllableType::VowelSyllable | SyllableType::ConsonantSyllable => {
960 initial_reordering_consonant_syllable(plan, indic_plan, face, start, end, buffer);
961 }
962 SyllableType::BrokenCluster | SyllableType::StandaloneCluster => {
964 initial_reordering_standalone_cluster(plan, indic_plan, face, start, end, buffer);
965 }
966 SyllableType::SymbolCluster | SyllableType::NonIndicCluster => {}
967 }
968}
969
970fn initial_reordering_consonant_syllable(
973 plan: &hb_ot_shape_plan_t,
974 indic_plan: &IndicShapePlan,
975 face: &hb_font_t,
976 start: usize,
977 end: usize,
978 buffer: &mut hb_buffer_t,
979) {
980 if buffer.script == Some(script::KANNADA)
984 && start + 3 <= end
985 && buffer.info[start].is_one_of(category_flag(category::RA))
986 && buffer.info[start + 1].is_one_of(category_flag(category::H))
987 && buffer.info[start + 2].is_one_of(category_flag(category::ZWJ))
988 {
989 buffer.merge_clusters(start + 1, start + 3);
990 buffer.info.swap(start + 1, start + 2);
991 }
992
993 let mut base = end;
1007 let mut has_reph = false;
1008
1009 {
1010 let mut limit = start;
1014 if indic_plan.mask_array[indic_feature::RPHF] != 0
1015 && start + 3 <= end
1016 && ((indic_plan.config.reph_mode == RephMode::Implicit
1017 && !buffer.info[start + 2].is_joiner())
1018 || (indic_plan.config.reph_mode == RephMode::Explicit
1019 && buffer.info[start + 2].indic_category() == category::ZWJ))
1020 {
1021 let glyphs = &[
1023 buffer.info[start].as_glyph(),
1024 buffer.info[start + 1].as_glyph(),
1025 if indic_plan.config.reph_mode == RephMode::Explicit {
1026 buffer.info[start + 2].as_glyph()
1027 } else {
1028 GlyphId(0)
1029 },
1030 ];
1031 if indic_plan
1032 .rphf
1033 .would_substitute(&plan.ot_map, face, &glyphs[0..2])
1034 || (indic_plan.config.reph_mode == RephMode::Explicit
1035 && indic_plan.rphf.would_substitute(&plan.ot_map, face, glyphs))
1036 {
1037 limit += 2;
1038 while limit < end && buffer.info[limit].is_joiner() {
1039 limit += 1;
1040 }
1041 base = start;
1042 has_reph = true;
1043 }
1044 } else if indic_plan.config.reph_mode == RephMode::LogRepha
1045 && buffer.info[start].indic_category() == category::REPHA
1046 {
1047 limit += 1;
1048 while limit < end && buffer.info[limit].is_joiner() {
1049 limit += 1;
1050 }
1051 base = start;
1052 has_reph = true;
1053 }
1054
1055 match indic_plan.config.base_pos {
1056 BasePosition::Last => {
1057 let mut i = end;
1059 let mut seen_below = false;
1060 loop {
1061 i -= 1;
1062 if buffer.info[i].is_consonant() {
1064 if buffer.info[i].indic_position() != position::BELOW_C
1067 && (buffer.info[i].indic_position() != position::POST_C || seen_below)
1068 {
1069 base = i;
1070 break;
1071 }
1072 if buffer.info[i].indic_position() == position::BELOW_C {
1073 seen_below = true;
1074 }
1075
1076 base = i;
1086 } else {
1087 if start < i
1093 && buffer.info[i].indic_category() == category::ZWJ
1094 && buffer.info[i - 1].indic_category() == category::H
1095 {
1096 break;
1097 }
1098 }
1099
1100 if i <= limit {
1101 break;
1102 }
1103 }
1104 }
1105 BasePosition::LastSinhala => {
1106 if !has_reph {
1111 base = limit;
1112 }
1113
1114 for i in limit..end {
1117 if buffer.info[i].is_consonant() {
1118 if limit < i && buffer.info[i - 1].indic_category() == category::ZWJ {
1119 break;
1120 } else {
1121 base = i;
1122 }
1123 }
1124 }
1125
1126 for i in base + 1..end {
1128 if buffer.info[i].is_consonant() {
1129 buffer.info[i].set_indic_position(position::BELOW_C);
1130 }
1131 }
1132 }
1133 }
1134
1135 if has_reph && base == start && limit - base <= 2 {
1141 has_reph = false;
1143 }
1144 }
1145
1146 for i in start..base {
1177 let pos = buffer.info[i].indic_position();
1178 buffer.info[i].set_indic_position(cmp::min(position::PRE_C, pos));
1179 }
1180
1181 if base < end {
1182 buffer.info[base].set_indic_position(position::BASE_C);
1183 }
1184
1185 for i in base + 1..end {
1188 if buffer.info[i].indic_category() == category::M {
1189 for j in i + 1..end {
1190 if buffer.info[j].is_consonant() {
1191 buffer.info[j].set_indic_position(position::FINAL_C);
1192 break;
1193 }
1194 }
1195
1196 break;
1197 }
1198 }
1199
1200 if has_reph {
1202 buffer.info[start].set_indic_position(position::RA_TO_BECOME_REPH);
1203 }
1204
1205 if indic_plan.is_old_spec {
1233 let disallow_double_halants = buffer.script == Some(script::KANNADA);
1234 for i in base + 1..end {
1235 if buffer.info[i].indic_category() == category::H {
1236 let mut j = end - 1;
1237 while j > i {
1238 if buffer.info[j].is_consonant()
1239 || (disallow_double_halants
1240 && buffer.info[j].indic_category() == category::H)
1241 {
1242 break;
1243 }
1244
1245 j -= 1;
1246 }
1247
1248 if buffer.info[j].indic_category() != category::H && j > i {
1249 let t = buffer.info[i];
1251 for k in 0..j - i {
1252 buffer.info[k + i] = buffer.info[k + i + 1];
1253 }
1254 buffer.info[j] = t;
1255 }
1256
1257 break;
1258 }
1259 }
1260 }
1261
1262 {
1264 let mut last_pos = position::START;
1265 for i in start..end {
1266 let ok = rb_flag_unsafe(buffer.info[i].indic_category() as u32)
1267 & (category_flag(category::ZWJ)
1268 | category_flag(category::ZWNJ)
1269 | category_flag(category::N)
1270 | category_flag(category::RS)
1271 | category_flag(category::CM)
1272 | category_flag(category::H))
1273 != 0;
1274 if ok {
1275 buffer.info[i].set_indic_position(last_pos);
1276
1277 if buffer.info[i].indic_category() == category::H
1278 && buffer.info[i].indic_position() == position::PRE_M
1279 {
1280 for j in (start + 1..=i).rev() {
1288 if buffer.info[j - 1].indic_position() != position::PRE_M {
1289 let pos = buffer.info[j - 1].indic_position();
1290 buffer.info[i].set_indic_position(pos);
1291 break;
1292 }
1293 }
1294 }
1295 } else if buffer.info[i].indic_position() != position::SMVD {
1296 last_pos = buffer.info[i].indic_position();
1297 }
1298 }
1299 }
1300 {
1303 let mut last = base;
1304 for i in base + 1..end {
1305 if buffer.info[i].is_consonant() {
1306 for j in last + 1..i {
1307 if (buffer.info[j].indic_position() as u8) < (position::SMVD as u8) {
1308 let pos = buffer.info[i].indic_position();
1309 buffer.info[j].set_indic_position(pos);
1310 }
1311 }
1312
1313 last = i;
1314 } else if buffer.info[i].indic_category() == category::M {
1315 last = i;
1316 }
1317 }
1318 }
1319
1320 {
1321 let syllable = buffer.info[start].syllable();
1323 for i in start..end {
1324 buffer.info[i].set_syllable(u8::try_from(i - start).unwrap());
1325 }
1326
1327 buffer.info[start..end].sort_by(|a, b| a.indic_position().cmp(&b.indic_position()));
1328
1329 base = end;
1331 for i in start..end {
1332 if buffer.info[i].indic_position() == position::BASE_C {
1333 base = i;
1334 break;
1335 }
1336 }
1337 if indic_plan.is_old_spec || end - start > 127 {
1369 buffer.merge_clusters(base, end);
1370 } else {
1371 for i in base..end {
1373 if buffer.info[i].syllable() != 255 {
1374 let mut min = i;
1375 let mut max = i;
1376 let mut j = start + buffer.info[i].syllable() as usize;
1377 while j != i {
1378 min = cmp::min(min, j);
1379 max = cmp::max(max, j);
1380 let next = start + buffer.info[j].syllable() as usize;
1381 buffer.info[j].set_syllable(255); j = next;
1383 }
1384
1385 buffer.merge_clusters(cmp::max(base, min), max + 1);
1386 }
1387 }
1388 }
1389
1390 for info in &mut buffer.info[start..end] {
1392 info.set_syllable(syllable);
1393 }
1394 }
1395
1396 {
1399 for info in &mut buffer.info[start..end] {
1401 if info.indic_position() != position::RA_TO_BECOME_REPH {
1402 break;
1403 }
1404
1405 info.mask |= indic_plan.mask_array[indic_feature::RPHF];
1406 }
1407
1408 let mut mask = indic_plan.mask_array[indic_feature::HALF];
1410 if !indic_plan.is_old_spec && indic_plan.config.blwf_mode == BlwfMode::PreAndPost {
1411 mask |= indic_plan.mask_array[indic_feature::BLWF];
1412 }
1413
1414 for info in &mut buffer.info[start..base] {
1415 info.mask |= mask;
1416 }
1417
1418 mask = 0;
1420 if base < end {
1421 buffer.info[base].mask |= mask;
1422 }
1423
1424 mask = indic_plan.mask_array[indic_feature::BLWF]
1426 | indic_plan.mask_array[indic_feature::ABVF]
1427 | indic_plan.mask_array[indic_feature::PSTF];
1428 for i in base + 1..end {
1429 buffer.info[i].mask |= mask;
1430 }
1431 }
1432
1433 if indic_plan.is_old_spec && buffer.script == Some(script::DEVANAGARI) {
1434 for i in start..base.saturating_sub(1) {
1452 if buffer.info[i].indic_category() == category::RA
1453 && buffer.info[i + 1].indic_category() == category::H
1454 && (i + 2 == base || buffer.info[i + 2].indic_category() != category::ZWJ)
1455 {
1456 buffer.info[i].mask |= indic_plan.mask_array[indic_feature::BLWF];
1457 buffer.info[i + 1].mask |= indic_plan.mask_array[indic_feature::BLWF];
1458 }
1459 }
1460 }
1461
1462 let pref_len = 2;
1463 if indic_plan.mask_array[indic_feature::PREF] != 0 && base + pref_len < end {
1464 for i in base + 1..end - pref_len + 1 {
1466 let glyphs = &[buffer.info[i + 0].as_glyph(), buffer.info[i + 1].as_glyph()];
1467 if indic_plan.pref.would_substitute(&plan.ot_map, face, glyphs) {
1468 buffer.info[i + 0].mask = indic_plan.mask_array[indic_feature::PREF];
1469 buffer.info[i + 1].mask = indic_plan.mask_array[indic_feature::PREF];
1470 break;
1471 }
1472 }
1473 }
1474
1475 for i in start + 1..end {
1477 if buffer.info[i].is_joiner() {
1478 let non_joiner = buffer.info[i].indic_category() == category::ZWNJ;
1479 let mut j = i;
1480
1481 loop {
1482 j -= 1;
1483
1484 if non_joiner {
1490 buffer.info[j].mask &= !indic_plan.mask_array[indic_feature::HALF];
1491 }
1492
1493 if j <= start || buffer.info[j].is_consonant() {
1494 break;
1495 }
1496 }
1497 }
1498 }
1499}
1500
1501fn initial_reordering_standalone_cluster(
1502 plan: &hb_ot_shape_plan_t,
1503 indic_plan: &IndicShapePlan,
1504 face: &hb_font_t,
1505 start: usize,
1506 end: usize,
1507 buffer: &mut hb_buffer_t,
1508) {
1509 initial_reordering_consonant_syllable(plan, indic_plan, face, start, end, buffer);
1512}
1513
1514fn final_reordering(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) {
1515 if buffer.is_empty() {
1516 return;
1517 }
1518
1519 let indic_plan = plan.data::<IndicShapePlan>();
1520
1521 let mut virama_glyph = None;
1522 if indic_plan.config.virama != 0 {
1523 if let Some(g) = face.get_nominal_glyph(indic_plan.config.virama) {
1524 virama_glyph = Some(g.0 as u32);
1525 }
1526 }
1527
1528 let mut start = 0;
1529 let mut end = buffer.next_syllable(0);
1530 while start < buffer.len {
1531 final_reordering_impl(indic_plan, virama_glyph, start, end, buffer);
1532 start = end;
1533 end = buffer.next_syllable(start);
1534 }
1535}
1536
1537fn final_reordering_impl(
1538 plan: &IndicShapePlan,
1539 virama_glyph: Option<u32>,
1540 start: usize,
1541 end: usize,
1542 buffer: &mut hb_buffer_t,
1543) {
1544 if let Some(virama_glyph) = virama_glyph {
1552 for info in &mut buffer.info[start..end] {
1553 if info.glyph_id == virama_glyph
1554 && _hb_glyph_info_ligated(info)
1555 && _hb_glyph_info_multiplied(info)
1556 {
1557 info.set_indic_category(category::H);
1559 _hb_glyph_info_clear_ligated_and_multiplied(info);
1560 }
1561 }
1562 }
1563
1564 let mut try_pref = plan.mask_array[indic_feature::PREF] != 0;
1572
1573 let mut base = start;
1574 while base < end {
1575 if buffer.info[base].indic_position() as u32 >= position::BASE_C as u32 {
1576 if try_pref && base + 1 < end {
1577 for i in base + 1..end {
1578 if (buffer.info[i].mask & plan.mask_array[indic_feature::PREF]) != 0 {
1579 if !(_hb_glyph_info_substituted(&buffer.info[i])
1580 && _hb_glyph_info_ligated_and_didnt_multiply(&buffer.info[i]))
1581 {
1582 base = i;
1585 while base < end && buffer.info[base].is_halant() {
1586 base += 1;
1587 }
1588
1589 buffer.info[base].set_indic_position(position::BASE_C);
1590 try_pref = false;
1591 }
1592
1593 break;
1594 }
1595 }
1596 }
1597
1598 if buffer.script == Some(script::MALAYALAM) {
1600 let mut i = base + 1;
1601 while i < end {
1602 while i < end && buffer.info[i].is_joiner() {
1603 i += 1;
1604 }
1605
1606 if i == end || !buffer.info[i].is_halant() {
1607 break;
1608 }
1609
1610 i += 1; while i < end && buffer.info[i].is_joiner() {
1613 i += 1;
1614 }
1615
1616 if i < end
1617 && buffer.info[i].is_consonant()
1618 && buffer.info[i].indic_position() == position::BELOW_C
1619 {
1620 base = i;
1621 buffer.info[base].set_indic_position(position::BASE_C);
1622 }
1623
1624 i += 1;
1625 }
1626 }
1627
1628 if start < base && buffer.info[base].indic_position() as u32 > position::BASE_C as u32 {
1629 base -= 1;
1630 }
1631
1632 break;
1633 }
1634
1635 base += 1;
1636 }
1637
1638 if base == end && start < base && buffer.info[base - 1].is_one_of(rb_flag(category::ZWJ as u32))
1639 {
1640 base -= 1;
1641 }
1642
1643 if base < end {
1644 while start < base
1645 && buffer.info[base]
1646 .is_one_of(rb_flag(category::N as u32) | rb_flag(category::H as u32))
1647 {
1648 base -= 1;
1649 }
1650 }
1651
1652 if start + 1 < end && start < base {
1681 let mut new_pos = if base == end { base - 2 } else { base - 1 };
1683
1684 if buffer.script != Some(script::MALAYALAM) && buffer.script != Some(script::TAMIL) {
1688 loop {
1689 while new_pos > start
1690 && !buffer.info[new_pos]
1691 .is_one_of(rb_flag(category::M as u32) | rb_flag(category::H as u32))
1692 {
1693 new_pos -= 1;
1694 }
1695
1696 if buffer.info[new_pos].is_halant()
1700 && buffer.info[new_pos].indic_position() != position::PRE_M
1701 {
1702 if new_pos + 1 < end {
1703 if buffer.info[new_pos + 1].indic_category() == category::ZWJ {
1705 if new_pos > start {
1707 new_pos -= 1;
1708 continue;
1709 }
1710 }
1711
1712 }
1720 } else {
1721 new_pos = start; }
1723
1724 break;
1725 }
1726 }
1727
1728 if start < new_pos && buffer.info[new_pos].indic_position() != position::PRE_M {
1729 for i in (start + 1..=new_pos).rev() {
1731 if buffer.info[i - 1].indic_position() == position::PRE_M {
1732 let old_pos = i - 1;
1733 if old_pos < base && base <= new_pos {
1735 base -= 1;
1736 }
1737
1738 let tmp = buffer.info[old_pos];
1739 for i in 0..new_pos - old_pos {
1740 buffer.info[i + old_pos] = buffer.info[i + old_pos + 1];
1741 }
1742 buffer.info[new_pos] = tmp;
1743
1744 buffer.merge_clusters(new_pos, cmp::min(end, base + 1));
1747
1748 new_pos -= 1;
1749 }
1750 }
1751 } else {
1752 for i in start..base {
1753 if buffer.info[i].indic_position() == position::PRE_M {
1754 buffer.merge_clusters(i, cmp::min(end, base + 1));
1755 break;
1756 }
1757 }
1758 }
1759 }
1760
1761 if start + 1 < end
1779 && buffer.info[start].indic_position() == position::RA_TO_BECOME_REPH
1780 && (buffer.info[start].indic_category() == category::REPHA)
1781 ^ _hb_glyph_info_ligated_and_didnt_multiply(&buffer.info[start])
1782 {
1783 let mut new_reph_pos;
1784 loop {
1785 let reph_pos = plan.config.reph_pos;
1786
1787 if reph_pos != RephPosition::AfterPost {
1790 {
1801 new_reph_pos = start + 1;
1802 while new_reph_pos < base && !buffer.info[new_reph_pos].is_halant() {
1803 new_reph_pos += 1;
1804 }
1805
1806 if new_reph_pos < base && buffer.info[new_reph_pos].is_halant() {
1807 if new_reph_pos + 1 < base && buffer.info[new_reph_pos + 1].is_joiner() {
1809 new_reph_pos += 1;
1810 }
1811
1812 break;
1813 }
1814 }
1815
1816 if reph_pos == RephPosition::AfterMain {
1820 new_reph_pos = base;
1821 while new_reph_pos + 1 < end
1822 && buffer.info[new_reph_pos + 1].indic_position() as u8
1823 <= position::AFTER_MAIN as u8
1824 {
1825 new_reph_pos += 1;
1826 }
1827
1828 if new_reph_pos < end {
1829 break;
1830 }
1831 }
1832
1833 if reph_pos == RephPosition::AfterSub {
1840 new_reph_pos = base;
1841 while new_reph_pos + 1 < end
1842 && (rb_flag_unsafe(buffer.info[new_reph_pos + 1].indic_position() as u32)
1843 & (rb_flag(position::POST_C as u32)
1844 | rb_flag(position::AFTER_POST as u32)
1845 | rb_flag(position::SMVD as u32)))
1846 == 0
1847 {
1848 new_reph_pos += 1;
1849 }
1850
1851 if new_reph_pos < end {
1852 break;
1853 }
1854 }
1855 }
1856
1857 new_reph_pos = start + 1;
1866 while new_reph_pos < base && !buffer.info[new_reph_pos].is_halant() {
1867 new_reph_pos += 1;
1868 }
1869
1870 if new_reph_pos < base && buffer.info[new_reph_pos].is_halant() {
1871 if new_reph_pos + 1 < base && buffer.info[new_reph_pos + 1].is_joiner() {
1873 new_reph_pos += 1;
1874 }
1875
1876 break;
1877 }
1878 {
1882 new_reph_pos = end - 1;
1883 while new_reph_pos > start
1884 && buffer.info[new_reph_pos].indic_position() == position::SMVD
1885 {
1886 new_reph_pos -= 1;
1887 }
1888
1889 if buffer.info[new_reph_pos].is_halant() {
1895 for info in &buffer.info[base + 1..new_reph_pos] {
1896 if info.indic_category() == category::M {
1897 new_reph_pos -= 1;
1899 }
1900 }
1901 }
1902 }
1903
1904 break;
1905 }
1906
1907 buffer.merge_clusters(start, new_reph_pos + 1);
1909
1910 let reph = buffer.info[start];
1911 for i in 0..new_reph_pos - start {
1912 buffer.info[i + start] = buffer.info[i + start + 1];
1913 }
1914 buffer.info[new_reph_pos] = reph;
1915
1916 if start < base && base <= new_reph_pos {
1917 base -= 1;
1918 }
1919 }
1920
1921 if try_pref && base + 1 < end {
1928 for i in base + 1..end {
1929 if (buffer.info[i].mask & plan.mask_array[indic_feature::PREF]) != 0 {
1930 if _hb_glyph_info_ligated_and_didnt_multiply(&buffer.info[i]) {
1939 let mut new_pos = base;
1945 if buffer.script != Some(script::MALAYALAM)
1949 && buffer.script != Some(script::TAMIL)
1950 {
1951 while new_pos > start
1952 && !buffer.info[new_pos - 1].is_one_of(
1953 rb_flag(category::M as u32) | rb_flag(category::H as u32),
1954 )
1955 {
1956 new_pos -= 1;
1957 }
1958 }
1959
1960 if new_pos > start && buffer.info[new_pos - 1].is_halant() {
1961 if new_pos < end && buffer.info[new_pos].is_joiner() {
1963 new_pos += 1;
1964 }
1965 }
1966
1967 {
1968 let old_pos = i;
1969
1970 buffer.merge_clusters(new_pos, old_pos + 1);
1971 let tmp = buffer.info[old_pos];
1972 for i in (0..=old_pos - new_pos).rev() {
1973 buffer.info[i + new_pos + 1] = buffer.info[i + new_pos];
1974 }
1975 buffer.info[new_pos] = tmp;
1976
1977 if new_pos <= base && base < old_pos {
1978 #[allow(unused_assignments)]
1980 {
1981 base += 1;
1982 }
1983 }
1984 }
1985 }
1986
1987 break;
1988 }
1989 }
1990 }
1991
1992 if buffer.info[start].indic_position() == position::PRE_M {
1994 if start == 0
1995 || (rb_flag_unsafe(
1996 _hb_glyph_info_get_general_category(&buffer.info[start - 1]).to_rb(),
1997 ) & rb_flag_range(
1998 hb_gc::RB_UNICODE_GENERAL_CATEGORY_FORMAT,
1999 hb_gc::RB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK,
2000 )) == 0
2001 {
2002 buffer.info[start].mask |= plan.mask_array[indic_feature::INIT];
2003 } else {
2004 buffer.unsafe_to_break(Some(start - 1), Some(start + 1));
2005 }
2006 }
2007}
2008
2009pub fn get_category_and_position(u: u32) -> (Category, Position) {
2010 let (c1, c2) = super::ot_shape_complex_indic_table::get_categories(u);
2011 let c2 = if c1 == SyllabicCategory::ConsonantMedial
2012 || c1 == SyllabicCategory::GeminationMark
2013 || c1 == SyllabicCategory::RegisterShifter
2014 || c1 == SyllabicCategory::ConsonantSucceedingRepha
2015 || c1 == SyllabicCategory::Virama
2016 || c1 == SyllabicCategory::VowelDependent
2017 || false
2018 {
2019 c2
2020 } else {
2021 MatraCategory::NotApplicable
2022 };
2023
2024 let c1 = match c1 {
2025 SyllabicCategory::Other => category::X,
2026 SyllabicCategory::Avagraha => category::SYMBOL,
2027 SyllabicCategory::Bindu => category::SM,
2028 SyllabicCategory::BrahmiJoiningNumber => category::PLACEHOLDER, SyllabicCategory::CantillationMark => category::A,
2030 SyllabicCategory::Consonant => category::C,
2031 SyllabicCategory::ConsonantDead => category::C,
2032 SyllabicCategory::ConsonantFinal => category::CM,
2033 SyllabicCategory::ConsonantHeadLetter => category::C,
2034 SyllabicCategory::ConsonantInitialPostfixed => category::PLACEHOLDER,
2035 SyllabicCategory::ConsonantKiller => category::M, SyllabicCategory::ConsonantMedial => category::CM,
2037 SyllabicCategory::ConsonantPlaceholder => category::PLACEHOLDER,
2038 SyllabicCategory::ConsonantPrecedingRepha => category::REPHA,
2039 SyllabicCategory::ConsonantPrefixed => category::X,
2040 SyllabicCategory::ConsonantSubjoined => category::CM,
2041 SyllabicCategory::ConsonantSucceedingRepha => category::CM,
2042 SyllabicCategory::ConsonantWithStacker => category::CS,
2043 SyllabicCategory::GeminationMark => category::SM, SyllabicCategory::InvisibleStacker => category::COENG,
2045 SyllabicCategory::Joiner => category::ZWJ,
2046 SyllabicCategory::ModifyingLetter => category::X,
2047 SyllabicCategory::NonJoiner => category::ZWNJ,
2048 SyllabicCategory::Nukta => category::N,
2049 SyllabicCategory::Number => category::PLACEHOLDER,
2050 SyllabicCategory::NumberJoiner => category::PLACEHOLDER, SyllabicCategory::PureKiller => category::M,
2052 SyllabicCategory::RegisterShifter => category::RS,
2053 SyllabicCategory::SyllableModifier => category::SM,
2054 SyllabicCategory::ToneLetter => category::X,
2055 SyllabicCategory::ToneMark => category::N,
2056 SyllabicCategory::Virama => category::H,
2057 SyllabicCategory::Visarga => category::SM,
2058 SyllabicCategory::Vowel => category::V,
2059 SyllabicCategory::VowelDependent => category::M,
2060 SyllabicCategory::VowelIndependent => category::V,
2061 };
2062
2063 let c2 = match c2 {
2064 MatraCategory::NotApplicable => position::END,
2065 MatraCategory::Left => position::PRE_C,
2066 MatraCategory::Top => position::ABOVE_C,
2067 MatraCategory::Bottom => position::BELOW_C,
2068 MatraCategory::Right => position::POST_C,
2069 MatraCategory::BottomAndLeft => position::POST_C,
2070 MatraCategory::BottomAndRight => position::POST_C,
2071 MatraCategory::LeftAndRight => position::POST_C,
2072 MatraCategory::TopAndBottom => position::BELOW_C,
2073 MatraCategory::TopAndBottomAndRight => position::POST_C,
2074 MatraCategory::TopAndBottomAndLeft => position::BELOW_C,
2075 MatraCategory::TopAndLeft => position::ABOVE_C,
2076 MatraCategory::TopAndLeftAndRight => position::POST_C,
2077 MatraCategory::TopAndRight => position::POST_C,
2078 MatraCategory::Overstruck => position::AFTER_MAIN,
2079 MatraCategory::VisualOrderLeft => position::PRE_M,
2080 };
2081
2082 (c1, c2)
2083}
2084
2085#[rustfmt::skip]
2086fn matra_position_indic(u: u32, side: u8) -> u8 {
2087 #[inline] fn in_half_block(u: u32, base: u32) -> bool { u & !0x7F == base }
2088 #[inline] fn is_deva(u: u32) -> bool { in_half_block(u, 0x0900) }
2089 #[inline] fn is_beng(u: u32) -> bool { in_half_block(u, 0x0980) }
2090 #[inline] fn is_guru(u: u32) -> bool { in_half_block(u, 0x0A00) }
2091 #[inline] fn is_gujr(u: u32) -> bool { in_half_block(u, 0x0A80) }
2092 #[inline] fn is_orya(u: u32) -> bool { in_half_block(u, 0x0B00) }
2093 #[inline] fn is_taml(u: u32) -> bool { in_half_block(u, 0x0B80) }
2094 #[inline] fn is_telu(u: u32) -> bool { in_half_block(u, 0x0C00) }
2095 #[inline] fn is_knda(u: u32) -> bool { in_half_block(u, 0x0C80) }
2096 #[inline] fn is_mlym(u: u32) -> bool { in_half_block(u, 0x0D00) }
2097 #[inline] fn is_sinh(u: u32) -> bool { in_half_block(u, 0x0D80) }
2098
2099 #[inline]
2100 fn matra_pos_right(u: u32) -> Position {
2101 if is_deva(u) {
2102 position::AFTER_SUB
2103 } else if is_beng(u) {
2104 position::AFTER_POST
2105 } else if is_guru(u) {
2106 position::AFTER_POST
2107 } else if is_gujr(u) {
2108 position::AFTER_POST
2109 } else if is_orya(u) {
2110 position::AFTER_POST
2111 } else if is_taml(u) {
2112 position::AFTER_POST
2113 } else if is_telu(u) {
2114 if u <= 0x0C42 {
2115 position::BEFORE_SUB
2116 } else {
2117 position::AFTER_SUB
2118 }
2119 } else if is_knda(u) {
2120 if u < 0x0CC3 || u > 0xCD6 {
2121 position::BEFORE_SUB
2122 } else {
2123 position::AFTER_SUB
2124 }
2125 } else if is_mlym(u) {
2126 position::AFTER_POST
2127 } else if is_sinh(u) {
2128 position::AFTER_SUB
2129 } else {
2130 position::AFTER_SUB
2131 }
2132 }
2133
2134 #[inline]
2136 fn matra_pos_top(u: u32) -> Position {
2137 if is_deva(u) {
2138 position::AFTER_SUB
2139 } else if is_guru(u) {
2140 position::AFTER_POST
2142 } else if is_gujr(u) {
2143 position::AFTER_SUB
2144 } else if is_orya(u) {
2145 position::AFTER_MAIN
2146 } else if is_taml(u) {
2147 position::AFTER_SUB
2148 } else if is_telu(u) {
2149 position::BEFORE_SUB
2150 } else if is_knda(u) {
2151 position::BEFORE_SUB
2152 } else if is_sinh(u) {
2153 position::AFTER_SUB
2154 } else {
2155 position::AFTER_SUB
2156 }
2157 }
2158
2159 #[inline]
2160 fn matra_pos_bottom(u: u32) -> Position {
2161 if is_deva(u) {
2162 position::AFTER_SUB
2163 } else if is_beng(u) {
2164 position::AFTER_SUB
2165 } else if is_guru(u) {
2166 position::AFTER_POST
2167 } else if is_gujr(u) {
2168 position::AFTER_POST
2169 } else if is_orya(u) {
2170 position::AFTER_SUB
2171 } else if is_taml(u) {
2172 position::AFTER_POST
2173 } else if is_telu(u) {
2174 position::BEFORE_SUB
2175 } else if is_knda(u) {
2176 position::BEFORE_SUB
2177 } else if is_mlym(u) {
2178 position::AFTER_POST
2179 } else if is_sinh(u) {
2180 position::AFTER_SUB
2181 } else {
2182 position::AFTER_SUB
2183 }
2184 }
2185
2186 match side {
2187 position::PRE_C => position::PRE_M,
2188 position::POST_C => matra_pos_right(u),
2189 position::ABOVE_C => matra_pos_top(u),
2190 position::BELOW_C => matra_pos_bottom(u),
2191 _ => side,
2192 }
2193}