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_normalize::*;
15use super::ot_shape_plan::hb_ot_shape_plan_t;
16use super::ot_shaper::*;
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_shaper_t = hb_ot_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;
37
38#[allow(dead_code)]
42pub mod ot_category_t {
43 pub const OT_X: u8 = 0;
44 pub const OT_C: u8 = 1;
45 pub const OT_V: u8 = 2;
46 pub const OT_N: u8 = 3;
47 pub const OT_H: u8 = 4;
48 pub const OT_ZWNJ: u8 = 5;
49 pub const OT_ZWJ: u8 = 6;
50 pub const OT_M: u8 = 7;
51 pub const OT_SM: u8 = 8;
52 pub const OT_A: u8 = 9;
53 pub const OT_VD: u8 = OT_A;
54 pub const OT_PLACEHOLDER: u8 = 10;
55 pub const OT_GB: u8 = OT_PLACEHOLDER;
56 pub const OT_DOTTEDCIRCLE: u8 = 11;
57 pub const OT_RS: u8 = 12; pub const OT_MPst: u8 = 13;
59 pub const OT_Repha: u8 = 14; pub const OT_Ra: u8 = 15;
61 pub const OT_CM: u8 = 16; pub const OT_Symbol: u8 = 17; pub const OT_CS: u8 = 18;
64
65 pub const OT_VAbv: u8 = 20;
67 pub const OT_VBlw: u8 = 21;
68 pub const OT_VPre: u8 = 22;
69 pub const OT_VPst: u8 = 23;
70
71 pub const OT_Robatic: u8 = 25;
73 pub const OT_Xgroup: u8 = 26;
74 pub const OT_Ygroup: u8 = 27;
75
76 pub const OT_As: u8 = 32; pub const OT_MH: u8 = 35; pub const OT_MR: u8 = 36; pub const OT_MW: u8 = 37; pub const OT_MY: u8 = 38; pub const OT_PT: u8 = 39; pub const OT_VS: u8 = 40; pub const OT_ML: u8 = 41; pub const IV: u8 = 2;
91}
92
93pub type Position = u8;
94pub mod ot_position_t {
95 pub const POS_START: u8 = 0;
96
97 pub const POS_RA_TO_BECOME_REPH: u8 = 1;
98 pub const POS_PRE_M: u8 = 2;
99 pub const POS_PRE_C: u8 = 3;
100
101 pub const POS_BASE_C: u8 = 4;
102 pub const POS_AFTER_MAIN: u8 = 5;
103
104 pub const POS_ABOVE_C: u8 = 6;
105
106 pub const POS_BEFORE_SUB: u8 = 7;
107 pub const POS_BELOW_C: u8 = 8;
108 pub const POS_AFTER_SUB: u8 = 9;
109
110 pub const POS_BEFORE_POST: u8 = 10;
111 pub const POS_POST_C: u8 = 11;
112 pub const POS_AFTER_POST: u8 = 12;
113
114 pub const POS_SMVD: u8 = 13;
115
116 pub const POS_END: u8 = 14;
117}
118
119const INDIC_FEATURES: &[(hb_tag_t, hb_ot_map_feature_flags_t)] = &[
120 (
124 hb_tag_t::from_bytes(b"nukt"),
125 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
126 ),
127 (
128 hb_tag_t::from_bytes(b"akhn"),
129 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
130 ),
131 (
132 hb_tag_t::from_bytes(b"rphf"),
133 F_MANUAL_JOINERS | F_PER_SYLLABLE,
134 ),
135 (
136 hb_tag_t::from_bytes(b"rkrf"),
137 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
138 ),
139 (
140 hb_tag_t::from_bytes(b"pref"),
141 F_MANUAL_JOINERS | F_PER_SYLLABLE,
142 ),
143 (
144 hb_tag_t::from_bytes(b"blwf"),
145 F_MANUAL_JOINERS | F_PER_SYLLABLE,
146 ),
147 (
148 hb_tag_t::from_bytes(b"abvf"),
149 F_MANUAL_JOINERS | F_PER_SYLLABLE,
150 ),
151 (
152 hb_tag_t::from_bytes(b"half"),
153 F_MANUAL_JOINERS | F_PER_SYLLABLE,
154 ),
155 (
156 hb_tag_t::from_bytes(b"pstf"),
157 F_MANUAL_JOINERS | F_PER_SYLLABLE,
158 ),
159 (
160 hb_tag_t::from_bytes(b"vatu"),
161 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
162 ),
163 (
164 hb_tag_t::from_bytes(b"cjct"),
165 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
166 ),
167 (
173 hb_tag_t::from_bytes(b"init"),
174 F_MANUAL_JOINERS | F_PER_SYLLABLE,
175 ),
176 (
177 hb_tag_t::from_bytes(b"pres"),
178 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
179 ),
180 (
181 hb_tag_t::from_bytes(b"abvs"),
182 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
183 ),
184 (
185 hb_tag_t::from_bytes(b"blws"),
186 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
187 ),
188 (
189 hb_tag_t::from_bytes(b"psts"),
190 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
191 ),
192 (
193 hb_tag_t::from_bytes(b"haln"),
194 F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE,
195 ),
196];
197
198#[allow(dead_code)]
200mod indic_feature {
201 pub const NUKT: usize = 0;
202 pub const AKHN: usize = 1;
203 pub const RPHF: usize = 2;
204 pub const RKRF: usize = 3;
205 pub const PREF: usize = 4;
206 pub const BLWF: usize = 5;
207 pub const ABVF: usize = 6;
208 pub const HALF: usize = 7;
209 pub const PSTF: usize = 8;
210 pub const VATU: usize = 9;
211 pub const CJCT: usize = 10;
212 pub const INIT: usize = 11;
213 pub const PRES: usize = 12;
214 pub const ABVS: usize = 13;
215 pub const BLWS: usize = 14;
216 pub const PSTS: usize = 15;
217 pub const HALN: usize = 16;
218}
219
220pub(crate) const fn category_flag(c: Category) -> u32 {
221 rb_flag(c as u32)
222}
223
224const CONSONANT_FLAGS_INDIC: u32 = category_flag(ot_category_t::OT_C)
230 | category_flag(ot_category_t::OT_CS)
231 | category_flag(ot_category_t::OT_Ra)
232 | category_flag(ot_category_t::OT_CM)
233 | category_flag(ot_category_t::OT_V)
234 | category_flag(ot_category_t::OT_PLACEHOLDER)
235 | category_flag(ot_category_t::OT_DOTTEDCIRCLE);
236
237const CONSONANT_FLAGS_MYANMAR: u32 = category_flag(ot_category_t::OT_C)
238 | category_flag(ot_category_t::OT_CS)
239 | category_flag(ot_category_t::OT_Ra)
240 | category_flag(ot_category_t::IV)
242 | category_flag(ot_category_t::OT_GB)
243 | category_flag(ot_category_t::OT_DOTTEDCIRCLE);
244
245const JOINER_FLAGS: u32 =
246 category_flag(ot_category_t::OT_ZWJ) | category_flag(ot_category_t::OT_ZWNJ);
247
248#[derive(Clone, Copy, PartialEq)]
249enum RephPosition {
250 AfterMain = ot_position_t::POS_AFTER_MAIN as isize,
251 BeforeSub = ot_position_t::POS_BEFORE_SUB as isize,
252 AfterSub = ot_position_t::POS_AFTER_SUB as isize,
253 BeforePost = ot_position_t::POS_BEFORE_POST as isize,
254 AfterPost = ot_position_t::POS_AFTER_POST as isize,
255}
256
257#[derive(Clone, Copy, PartialEq)]
258enum RephMode {
259 Implicit,
261 Explicit,
263 LogRepha,
265}
266
267#[derive(Clone, Copy, PartialEq)]
268enum BlwfMode {
269 PreAndPost,
271 PostOnly,
273}
274
275#[derive(Clone, Copy)]
276struct IndicConfig {
277 script: Option<Script>,
278 has_old_spec: bool,
279 virama: u32,
280 reph_pos: RephPosition,
281 reph_mode: RephMode,
282 blwf_mode: BlwfMode,
283}
284
285impl IndicConfig {
286 const fn new(
287 script: Option<Script>,
288 has_old_spec: bool,
289 virama: u32,
290 reph_pos: RephPosition,
291 reph_mode: RephMode,
292 blwf_mode: BlwfMode,
293 ) -> Self {
294 IndicConfig {
295 script,
296 has_old_spec,
297 virama,
298 reph_pos,
299 reph_mode,
300 blwf_mode,
301 }
302 }
303}
304
305const INDIC_CONFIGS: &[IndicConfig] = &[
306 IndicConfig::new(
307 None,
308 false,
309 0,
310 RephPosition::BeforePost,
311 RephMode::Implicit,
312 BlwfMode::PreAndPost,
313 ),
314 IndicConfig::new(
315 Some(script::DEVANAGARI),
316 true,
317 0x094D,
318 RephPosition::BeforePost,
319 RephMode::Implicit,
320 BlwfMode::PreAndPost,
321 ),
322 IndicConfig::new(
323 Some(script::BENGALI),
324 true,
325 0x09CD,
326 RephPosition::AfterSub,
327 RephMode::Implicit,
328 BlwfMode::PreAndPost,
329 ),
330 IndicConfig::new(
331 Some(script::GURMUKHI),
332 true,
333 0x0A4D,
334 RephPosition::BeforeSub,
335 RephMode::Implicit,
336 BlwfMode::PreAndPost,
337 ),
338 IndicConfig::new(
339 Some(script::GUJARATI),
340 true,
341 0x0ACD,
342 RephPosition::BeforePost,
343 RephMode::Implicit,
344 BlwfMode::PreAndPost,
345 ),
346 IndicConfig::new(
347 Some(script::ORIYA),
348 true,
349 0x0B4D,
350 RephPosition::AfterMain,
351 RephMode::Implicit,
352 BlwfMode::PreAndPost,
353 ),
354 IndicConfig::new(
355 Some(script::TAMIL),
356 true,
357 0x0BCD,
358 RephPosition::AfterPost,
359 RephMode::Implicit,
360 BlwfMode::PreAndPost,
361 ),
362 IndicConfig::new(
363 Some(script::TELUGU),
364 true,
365 0x0C4D,
366 RephPosition::AfterPost,
367 RephMode::Explicit,
368 BlwfMode::PostOnly,
369 ),
370 IndicConfig::new(
371 Some(script::KANNADA),
372 true,
373 0x0CCD,
374 RephPosition::AfterPost,
375 RephMode::Implicit,
376 BlwfMode::PostOnly,
377 ),
378 IndicConfig::new(
379 Some(script::MALAYALAM),
380 true,
381 0x0D4D,
382 RephPosition::AfterMain,
383 RephMode::LogRepha,
384 BlwfMode::PreAndPost,
385 ),
386 IndicConfig::new(
387 Some(script::SINHALA),
388 false,
389 0x0DCA,
390 RephPosition::AfterPost,
391 RephMode::Explicit,
392 BlwfMode::PreAndPost,
393 ),
394];
395
396struct IndicWouldSubstituteFeature {
397 lookups: Range<usize>,
398 zero_context: bool,
399}
400
401impl IndicWouldSubstituteFeature {
402 pub fn new(map: &hb_ot_map_t, feature_tag: hb_tag_t, zero_context: bool) -> Self {
403 IndicWouldSubstituteFeature {
404 lookups: match map.get_feature_stage(TableIndex::GSUB, feature_tag) {
405 Some(stage) => map.stage_lookup_range(TableIndex::GSUB, stage),
406 None => 0..0,
407 },
408 zero_context,
409 }
410 }
411
412 pub fn would_substitute(
413 &self,
414 map: &hb_ot_map_t,
415 face: &hb_font_t,
416 glyphs: &[GlyphId],
417 ) -> bool {
418 for index in self.lookups.clone() {
419 let lookup = map.lookup(TableIndex::GSUB, index);
420 let ctx = WouldApplyContext {
421 glyphs,
422 zero_context: self.zero_context,
423 };
424 if face
425 .gsub
426 .as_ref()
427 .and_then(|table| table.get_lookup(lookup.index))
428 .map_or(false, |lookup| lookup.would_apply(&ctx))
429 {
430 return true;
431 }
432 }
433
434 false
435 }
436}
437
438struct IndicShapePlan {
439 config: IndicConfig,
440 is_old_spec: bool,
441 rphf: IndicWouldSubstituteFeature,
443 pref: IndicWouldSubstituteFeature,
444 blwf: IndicWouldSubstituteFeature,
445 pstf: IndicWouldSubstituteFeature,
446 vatu: IndicWouldSubstituteFeature,
447 mask_array: [hb_mask_t; INDIC_FEATURES.len()],
448}
449
450impl IndicShapePlan {
451 fn new(plan: &hb_ot_shape_plan_t) -> Self {
452 let script = plan.script;
453 let config = if let Some(c) = INDIC_CONFIGS.iter().skip(1).find(|c| c.script == script) {
454 *c
455 } else {
456 INDIC_CONFIGS[0]
457 };
458
459 let is_old_spec = config.has_old_spec
460 && plan
461 .ot_map
462 .chosen_script(TableIndex::GSUB)
463 .map_or(true, |tag| tag.to_bytes()[3] != b'2');
464
465 let zero_context = is_old_spec && script != Some(script::MALAYALAM);
474
475 let mut mask_array = [0; INDIC_FEATURES.len()];
476 for (i, feature) in INDIC_FEATURES.iter().enumerate() {
477 mask_array[i] = if feature.1 & F_GLOBAL != 0 {
478 0
479 } else {
480 plan.ot_map.get_1_mask(feature.0)
481 }
482 }
483
484 IndicShapePlan {
493 config,
494 is_old_spec,
495 rphf: IndicWouldSubstituteFeature::new(
497 &plan.ot_map,
498 hb_tag_t::from_bytes(b"rphf"),
499 zero_context,
500 ),
501 pref: IndicWouldSubstituteFeature::new(
502 &plan.ot_map,
503 hb_tag_t::from_bytes(b"pref"),
504 zero_context,
505 ),
506 blwf: IndicWouldSubstituteFeature::new(
507 &plan.ot_map,
508 hb_tag_t::from_bytes(b"blwf"),
509 zero_context,
510 ),
511 pstf: IndicWouldSubstituteFeature::new(
512 &plan.ot_map,
513 hb_tag_t::from_bytes(b"pstf"),
514 zero_context,
515 ),
516 vatu: IndicWouldSubstituteFeature::new(
517 &plan.ot_map,
518 hb_tag_t::from_bytes(b"vatu"),
519 zero_context,
520 ),
521 mask_array,
522 }
523 }
524}
525
526impl hb_glyph_info_t {
527 pub(crate) fn indic_category(&self) -> Category {
528 self.ot_shaper_var_u8_category()
529 }
530
531 pub(crate) fn myanmar_category(&self) -> Category {
532 self.ot_shaper_var_u8_category()
533 }
534
535 pub(crate) fn khmer_category(&self) -> Category {
536 self.ot_shaper_var_u8_category()
537 }
538
539 pub(crate) fn set_indic_category(&mut self, c: Category) {
540 self.set_ot_shaper_var_u8_category(c)
541 }
542
543 pub(crate) fn set_myanmar_category(&mut self, c: Category) {
544 self.set_ot_shaper_var_u8_category(c)
545 }
546
547 pub(crate) fn indic_position(&self) -> Position {
548 self.ot_shaper_var_u8_auxiliary()
549 }
550
551 pub(crate) fn myanmar_position(&self) -> Position {
552 self.ot_shaper_var_u8_auxiliary()
553 }
554
555 pub(crate) fn set_indic_position(&mut self, c: Position) {
556 self.set_ot_shaper_var_u8_auxiliary(c)
557 }
558
559 pub(crate) fn set_myanmar_position(&mut self, c: Position) {
560 self.set_ot_shaper_var_u8_auxiliary(c)
561 }
562
563 fn is_one_of(&self, flags: u32) -> bool {
564 if _hb_glyph_info_ligated(self) {
566 return false;
567 }
568
569 rb_flag_unsafe(self.indic_category() as u32) & flags != 0
570 }
571
572 fn is_joiner(&self) -> bool {
573 self.is_one_of(JOINER_FLAGS)
574 }
575
576 pub(crate) fn is_consonant(&self) -> bool {
577 self.is_one_of(CONSONANT_FLAGS_INDIC)
578 }
579
580 pub(crate) fn is_consonant_myanmar(&self) -> bool {
581 self.is_one_of(CONSONANT_FLAGS_MYANMAR)
582 }
583
584 fn is_halant(&self) -> bool {
585 self.is_one_of(rb_flag(ot_category_t::OT_H as u32))
586 }
587
588 fn set_indic_properties(&mut self) {
589 let u = self.glyph_id;
590 let (cat, pos) = crate::hb::ot_shaper_indic_table::get_categories(u);
591
592 self.set_indic_category(cat);
593 self.set_indic_position(pos);
594 }
595}
596
597fn collect_features(planner: &mut hb_ot_shape_planner_t) {
598 planner.ot_map.add_gsub_pause(Some(setup_syllables));
600
601 planner
602 .ot_map
603 .enable_feature(hb_tag_t::from_bytes(b"locl"), F_PER_SYLLABLE, 1);
604 planner
607 .ot_map
608 .enable_feature(hb_tag_t::from_bytes(b"ccmp"), F_PER_SYLLABLE, 1);
609
610 planner.ot_map.add_gsub_pause(Some(initial_reordering));
611
612 for feature in INDIC_FEATURES.iter().take(11) {
613 planner.ot_map.add_feature(feature.0, feature.1, 1);
614 planner.ot_map.add_gsub_pause(None);
615 }
616
617 planner.ot_map.add_gsub_pause(Some(final_reordering));
618
619 for feature in INDIC_FEATURES.iter().skip(11) {
620 planner.ot_map.add_feature(feature.0, feature.1, 1);
621 }
622}
623
624fn override_features(planner: &mut hb_ot_shape_planner_t) {
625 planner
626 .ot_map
627 .disable_feature(hb_tag_t::from_bytes(b"liga"));
628 planner.ot_map.add_gsub_pause(Some(syllabic_clear_var)); }
630
631fn preprocess_text(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) {
632 super::ot_shaper_vowel_constraints::preprocess_text_vowel_constraints(buffer);
633}
634
635fn decompose(_: &hb_ot_shape_normalize_context_t, ab: char) -> Option<(char, char)> {
636 match ab {
638 '\u{0931}' | '\u{09DC}' | '\u{09DD}' | '\u{0B94}' => return None, _ => {}
644 }
645
646 crate::hb::unicode::decompose(ab)
647}
648
649fn compose(_: &hb_ot_shape_normalize_context_t, a: char, b: char) -> Option<char> {
650 if a.general_category().is_mark() {
652 return None;
653 }
654
655 if a == '\u{09AF}' && b == '\u{09BC}' {
657 return Some('\u{09DF}');
658 }
659
660 crate::hb::unicode::compose(a, b)
661}
662
663fn setup_masks(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) {
664 for info in buffer.info_slice_mut() {
667 info.set_indic_properties();
668 }
669}
670
671fn setup_syllables(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) -> bool {
672 super::ot_shaper_indic_machine::find_syllables_indic(buffer);
673
674 let mut start = 0;
675 let mut end = buffer.next_syllable(0);
676 while start < buffer.len {
677 buffer.unsafe_to_break(Some(start), Some(end));
678 start = end;
679 end = buffer.next_syllable(start);
680 }
681
682 false
683}
684
685fn initial_reordering(
686 plan: &hb_ot_shape_plan_t,
687 face: &hb_font_t,
688 buffer: &mut hb_buffer_t,
689) -> bool {
690 use super::ot_shaper_indic_machine::SyllableType;
691
692 let mut ret = false;
693
694 let indic_plan = plan.data::<IndicShapePlan>();
695
696 update_consonant_positions(plan, indic_plan, face, buffer);
697 if super::ot_shaper_syllabic::insert_dotted_circles(
698 face,
699 buffer,
700 SyllableType::BrokenCluster as u8,
701 ot_category_t::OT_DOTTEDCIRCLE,
702 Some(ot_category_t::OT_Repha),
703 Some(ot_position_t::POS_END),
704 ) {
705 ret = true;
706 }
707
708 let mut start = 0;
709 let mut end = buffer.next_syllable(0);
710 while start < buffer.len {
711 initial_reordering_syllable(plan, indic_plan, face, start, end, buffer);
712 start = end;
713 end = buffer.next_syllable(start);
714 }
715
716 ret
717}
718
719fn update_consonant_positions(
720 plan: &hb_ot_shape_plan_t,
721 indic_plan: &IndicShapePlan,
722 face: &hb_font_t,
723 buffer: &mut hb_buffer_t,
724) {
725 let mut virama_glyph = None;
726 if indic_plan.config.virama != 0 {
727 virama_glyph = face.get_nominal_glyph(indic_plan.config.virama);
728 }
729
730 if let Some(virama) = virama_glyph {
731 for info in buffer.info_slice_mut() {
732 if info.indic_position() == ot_position_t::POS_BASE_C {
733 let consonant = info.as_glyph();
734 info.set_indic_position(consonant_position_from_face(
735 plan, indic_plan, face, consonant, virama,
736 ));
737 }
738 }
739 }
740}
741
742fn consonant_position_from_face(
743 plan: &hb_ot_shape_plan_t,
744 indic_plan: &IndicShapePlan,
745 face: &hb_font_t,
746 consonant: GlyphId,
747 virama: GlyphId,
748) -> u8 {
749 if indic_plan
764 .blwf
765 .would_substitute(&plan.ot_map, face, &[virama, consonant])
766 || indic_plan
767 .blwf
768 .would_substitute(&plan.ot_map, face, &[consonant, virama])
769 || indic_plan
770 .vatu
771 .would_substitute(&plan.ot_map, face, &[virama, consonant])
772 || indic_plan
773 .vatu
774 .would_substitute(&plan.ot_map, face, &[consonant, virama])
775 {
776 return ot_position_t::POS_BELOW_C;
777 }
778
779 if indic_plan
780 .pstf
781 .would_substitute(&plan.ot_map, face, &[virama, consonant])
782 || indic_plan
783 .pstf
784 .would_substitute(&plan.ot_map, face, &[consonant, virama])
785 {
786 return ot_position_t::POS_POST_C;
787 }
788
789 if indic_plan
790 .pref
791 .would_substitute(&plan.ot_map, face, &[virama, consonant])
792 || indic_plan
793 .pref
794 .would_substitute(&plan.ot_map, face, &[consonant, virama])
795 {
796 return ot_position_t::POS_POST_C;
797 }
798
799 ot_position_t::POS_BASE_C
800}
801
802fn initial_reordering_syllable(
803 plan: &hb_ot_shape_plan_t,
804 indic_plan: &IndicShapePlan,
805 face: &hb_font_t,
806 start: usize,
807 end: usize,
808 buffer: &mut hb_buffer_t,
809) {
810 use super::ot_shaper_indic_machine::SyllableType;
811
812 let syllable_type = match buffer.info[start].syllable() & 0x0F {
813 0 => SyllableType::ConsonantSyllable,
814 1 => SyllableType::VowelSyllable,
815 2 => SyllableType::StandaloneCluster,
816 3 => SyllableType::SymbolCluster,
817 4 => SyllableType::BrokenCluster,
818 5 => SyllableType::NonIndicCluster,
819 _ => unreachable!(),
820 };
821
822 match syllable_type {
823 SyllableType::VowelSyllable | SyllableType::ConsonantSyllable => {
825 initial_reordering_consonant_syllable(plan, indic_plan, face, start, end, buffer);
826 }
827 SyllableType::BrokenCluster | SyllableType::StandaloneCluster => {
829 initial_reordering_standalone_cluster(plan, indic_plan, face, start, end, buffer);
830 }
831 SyllableType::SymbolCluster | SyllableType::NonIndicCluster => {}
832 }
833}
834
835fn initial_reordering_consonant_syllable(
838 plan: &hb_ot_shape_plan_t,
839 indic_plan: &IndicShapePlan,
840 face: &hb_font_t,
841 start: usize,
842 end: usize,
843 buffer: &mut hb_buffer_t,
844) {
845 if buffer.script == Some(script::KANNADA)
849 && start + 3 <= end
850 && buffer.info[start].is_one_of(category_flag(ot_category_t::OT_Ra))
851 && buffer.info[start + 1].is_one_of(category_flag(ot_category_t::OT_H))
852 && buffer.info[start + 2].is_one_of(category_flag(ot_category_t::OT_ZWJ))
853 {
854 buffer.merge_clusters(start + 1, start + 3);
855 buffer.info.swap(start + 1, start + 2);
856 }
857
858 let mut base = end;
872 let mut has_reph = false;
873
874 {
875 let mut limit = start;
879 if indic_plan.mask_array[indic_feature::RPHF] != 0
880 && start + 3 <= end
881 && ((indic_plan.config.reph_mode == RephMode::Implicit
882 && !buffer.info[start + 2].is_joiner())
883 || (indic_plan.config.reph_mode == RephMode::Explicit
884 && buffer.info[start + 2].indic_category() == ot_category_t::OT_ZWJ))
885 {
886 let glyphs = &[
888 buffer.info[start].as_glyph(),
889 buffer.info[start + 1].as_glyph(),
890 if indic_plan.config.reph_mode == RephMode::Explicit {
891 buffer.info[start + 2].as_glyph()
892 } else {
893 GlyphId(0)
894 },
895 ];
896 if indic_plan
897 .rphf
898 .would_substitute(&plan.ot_map, face, &glyphs[0..2])
899 || (indic_plan.config.reph_mode == RephMode::Explicit
900 && indic_plan.rphf.would_substitute(&plan.ot_map, face, glyphs))
901 {
902 limit += 2;
903 while limit < end && buffer.info[limit].is_joiner() {
904 limit += 1;
905 }
906 base = start;
907 has_reph = true;
908 }
909 } else if indic_plan.config.reph_mode == RephMode::LogRepha
910 && buffer.info[start].indic_category() == ot_category_t::OT_Repha
911 {
912 limit += 1;
913 while limit < end && buffer.info[limit].is_joiner() {
914 limit += 1;
915 }
916 base = start;
917 has_reph = true;
918 }
919
920 {
921 let mut i = end;
923 let mut seen_below = false;
924 loop {
925 i -= 1;
926 if buffer.info[i].is_consonant_myanmar() {
928 if buffer.info[i].indic_position() != ot_position_t::POS_BELOW_C
931 && (buffer.info[i].indic_position() != ot_position_t::POS_POST_C
932 || seen_below)
933 {
934 base = i;
935 break;
936 }
937 if buffer.info[i].indic_position() == ot_position_t::POS_BELOW_C {
938 seen_below = true;
939 }
940
941 base = i;
951 } else {
952 if start < i
958 && buffer.info[i].indic_category() == ot_category_t::OT_ZWJ
959 && buffer.info[i - 1].indic_category() == ot_category_t::OT_H
960 {
961 break;
962 }
963 }
964
965 if i <= limit {
966 break;
967 }
968 }
969 }
970
971 if has_reph && base == start && limit - base <= 2 {
977 has_reph = false;
979 }
980 }
981
982 for i in start..base {
1013 let pos = buffer.info[i].indic_position();
1014 buffer.info[i].set_indic_position(cmp::min(ot_position_t::POS_PRE_C, pos));
1015 }
1016
1017 if base < end {
1018 buffer.info[base].set_indic_position(ot_position_t::POS_BASE_C);
1019 }
1020
1021 if has_reph {
1023 buffer.info[start].set_indic_position(ot_position_t::POS_RA_TO_BECOME_REPH);
1024 }
1025
1026 if indic_plan.is_old_spec {
1054 let disallow_double_halants = buffer.script == Some(script::KANNADA);
1055 for i in base + 1..end {
1056 if buffer.info[i].indic_category() == ot_category_t::OT_H {
1057 let mut j = end - 1;
1058 while j > i {
1059 if buffer.info[j].is_consonant()
1060 || (disallow_double_halants
1061 && buffer.info[j].indic_category() == ot_category_t::OT_H)
1062 {
1063 break;
1064 }
1065
1066 j -= 1;
1067 }
1068
1069 if buffer.info[j].indic_category() != ot_category_t::OT_H && j > i {
1070 let t = buffer.info[i];
1072 for k in 0..j - i {
1073 buffer.info[k + i] = buffer.info[k + i + 1];
1074 }
1075 buffer.info[j] = t;
1076 }
1077
1078 break;
1079 }
1080 }
1081 }
1082
1083 {
1085 let mut last_pos = ot_position_t::POS_START;
1086 for i in start..end {
1087 let ok = rb_flag_unsafe(buffer.info[i].indic_category() as u32)
1088 & (category_flag(ot_category_t::OT_ZWJ)
1089 | category_flag(ot_category_t::OT_ZWNJ)
1090 | category_flag(ot_category_t::OT_N)
1091 | category_flag(ot_category_t::OT_RS)
1092 | category_flag(ot_category_t::OT_CM)
1093 | category_flag(ot_category_t::OT_H))
1094 != 0;
1095 if ok {
1096 buffer.info[i].set_indic_position(last_pos);
1097
1098 if buffer.info[i].indic_category() == ot_category_t::OT_H
1099 && buffer.info[i].indic_position() == ot_position_t::POS_PRE_M
1100 {
1101 for j in (start + 1..=i).rev() {
1105 if buffer.info[j - 1].indic_position() != ot_position_t::POS_PRE_M {
1106 let pos = buffer.info[j - 1].indic_position();
1107 buffer.info[i].set_indic_position(pos);
1108 break;
1109 }
1110 }
1111 }
1112 } else if buffer.info[i].indic_position() != ot_position_t::POS_SMVD {
1113 if buffer.info[i].indic_category() == ot_category_t::OT_MPst
1114 && i > start
1115 && buffer.info[i - 1].indic_category() == ot_category_t::OT_SM
1116 {
1117 let val = buffer.info[i].indic_position();
1118 buffer.info[i - 1].set_indic_position(val);
1119 }
1120
1121 last_pos = buffer.info[i].indic_position();
1122 }
1123 }
1124 }
1125 {
1128 let mut last = base;
1129 for i in base + 1..end {
1130 if buffer.info[i].is_consonant() {
1131 for j in last + 1..i {
1132 if buffer.info[j].indic_position() < ot_position_t::POS_SMVD {
1133 let pos = buffer.info[i].indic_position();
1134 buffer.info[j].set_indic_position(pos);
1135 }
1136 }
1137
1138 last = i;
1139 } else if (rb_flag_unsafe(buffer.info[i].indic_category() as u32)
1140 & (rb_flag(ot_category_t::OT_M as u32) | rb_flag(ot_category_t::OT_MPst as u32)))
1141 != 0
1142 {
1143 last = i;
1144 }
1145 }
1146 }
1147
1148 {
1149 let syllable = buffer.info[start].syllable();
1151 for i in start..end {
1152 buffer.info[i].set_syllable(u8::try_from(i - start).unwrap());
1153 }
1154
1155 buffer.info[start..end].sort_by_key(|a| a.indic_position());
1156
1157 let mut first_left_mantra = end;
1159 let mut last_left_mantra = end;
1160 base = end;
1161
1162 for i in start..end {
1163 if buffer.info[i].indic_position() == ot_position_t::POS_BASE_C {
1164 base = i;
1165 break;
1166 } else if buffer.info[i].indic_position() == ot_position_t::POS_PRE_M {
1167 if first_left_mantra == end {
1168 first_left_mantra = i;
1169 }
1170
1171 last_left_mantra = i;
1172 }
1173 }
1174
1175 if first_left_mantra < last_left_mantra {
1177 buffer.reverse_range(first_left_mantra, last_left_mantra + 1);
1179 let mut i = first_left_mantra;
1181
1182 for j in i..=last_left_mantra {
1183 if (rb_flag_unsafe(buffer.info[j].indic_category() as u32)
1184 & (rb_flag(ot_category_t::OT_M as u32)
1185 | rb_flag(ot_category_t::OT_MPst as u32)))
1186 != 0
1187 {
1188 buffer.reverse_range(i, j + 1);
1189 i = j + 1;
1190 }
1191 }
1192 }
1193
1194 if indic_plan.is_old_spec || end - start > 127 {
1226 buffer.merge_clusters(base, end);
1227 } else {
1228 for i in base..end {
1230 if buffer.info[i].syllable() != 255 {
1231 let mut min = i;
1232 let mut max = i;
1233 let mut j = start + buffer.info[i].syllable() as usize;
1234 while j != i {
1235 min = cmp::min(min, j);
1236 max = cmp::max(max, j);
1237 let next = start + buffer.info[j].syllable() as usize;
1238 buffer.info[j].set_syllable(255); j = next;
1240 }
1241
1242 buffer.merge_clusters(cmp::max(base, min), max + 1);
1243 }
1244 }
1245 }
1246
1247 for info in &mut buffer.info[start..end] {
1249 info.set_syllable(syllable);
1250 }
1251 }
1252
1253 {
1256 for info in &mut buffer.info[start..end] {
1258 if info.indic_position() != ot_position_t::POS_RA_TO_BECOME_REPH {
1259 break;
1260 }
1261
1262 info.mask |= indic_plan.mask_array[indic_feature::RPHF];
1263 }
1264
1265 let mut mask = indic_plan.mask_array[indic_feature::HALF];
1267 if !indic_plan.is_old_spec && indic_plan.config.blwf_mode == BlwfMode::PreAndPost {
1268 mask |= indic_plan.mask_array[indic_feature::BLWF];
1269 }
1270
1271 for info in &mut buffer.info[start..base] {
1272 info.mask |= mask;
1273 }
1274
1275 mask = 0;
1277 if base < end {
1278 buffer.info[base].mask |= mask;
1279 }
1280
1281 mask = indic_plan.mask_array[indic_feature::BLWF]
1283 | indic_plan.mask_array[indic_feature::ABVF]
1284 | indic_plan.mask_array[indic_feature::PSTF];
1285 for i in base + 1..end {
1286 buffer.info[i].mask |= mask;
1287 }
1288 }
1289
1290 if indic_plan.is_old_spec && buffer.script == Some(script::DEVANAGARI) {
1291 for i in start..base.saturating_sub(1) {
1309 if buffer.info[i].indic_category() == ot_category_t::OT_Ra
1310 && buffer.info[i + 1].indic_category() == ot_category_t::OT_H
1311 && (i + 2 == base || buffer.info[i + 2].indic_category() != ot_category_t::OT_ZWJ)
1312 {
1313 buffer.info[i].mask |= indic_plan.mask_array[indic_feature::BLWF];
1314 buffer.info[i + 1].mask |= indic_plan.mask_array[indic_feature::BLWF];
1315 }
1316 }
1317 }
1318
1319 let pref_len = 2;
1320 if indic_plan.mask_array[indic_feature::PREF] != 0 && base + pref_len < end {
1321 for i in base + 1..end - pref_len + 1 {
1323 let glyphs = &[buffer.info[i + 0].as_glyph(), buffer.info[i + 1].as_glyph()];
1324 if indic_plan.pref.would_substitute(&plan.ot_map, face, glyphs) {
1325 buffer.info[i + 0].mask |= indic_plan.mask_array[indic_feature::PREF];
1326 buffer.info[i + 1].mask |= indic_plan.mask_array[indic_feature::PREF];
1327 break;
1328 }
1329 }
1330 }
1331
1332 for i in start + 1..end {
1334 if buffer.info[i].is_joiner() {
1335 let non_joiner = buffer.info[i].indic_category() == ot_category_t::OT_ZWNJ;
1336 let mut j = i;
1337
1338 loop {
1339 j -= 1;
1340
1341 if non_joiner {
1347 buffer.info[j].mask &= !indic_plan.mask_array[indic_feature::HALF];
1348 }
1349
1350 if j <= start || buffer.info[j].is_consonant() {
1351 break;
1352 }
1353 }
1354 }
1355 }
1356}
1357
1358fn initial_reordering_standalone_cluster(
1359 plan: &hb_ot_shape_plan_t,
1360 indic_plan: &IndicShapePlan,
1361 face: &hb_font_t,
1362 start: usize,
1363 end: usize,
1364 buffer: &mut hb_buffer_t,
1365) {
1366 initial_reordering_consonant_syllable(plan, indic_plan, face, start, end, buffer);
1369}
1370
1371fn final_reordering(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) -> bool {
1372 if buffer.is_empty() {
1373 return false;
1374 }
1375
1376 foreach_syllable!(buffer, start, end, {
1377 final_reordering_impl(plan, face, start, end, buffer);
1378 });
1379
1380 false
1381}
1382
1383fn final_reordering_impl(
1384 plan: &hb_ot_shape_plan_t,
1385 face: &hb_font_t,
1386 start: usize,
1387 end: usize,
1388 buffer: &mut hb_buffer_t,
1389) {
1390 let indic_plan = plan.data::<IndicShapePlan>();
1391
1392 let mut virama_glyph = None;
1400 if indic_plan.config.virama != 0 {
1401 if let Some(g) = face.get_nominal_glyph(indic_plan.config.virama) {
1402 virama_glyph = Some(g.0 as u32);
1403 }
1404 }
1405
1406 if let Some(virama_glyph) = virama_glyph {
1407 for info in &mut buffer.info[start..end] {
1408 if info.glyph_id == virama_glyph
1409 && _hb_glyph_info_ligated(info)
1410 && _hb_glyph_info_multiplied(info)
1411 {
1412 info.set_indic_category(ot_category_t::OT_H);
1414 _hb_glyph_info_clear_ligated_and_multiplied(info);
1415 }
1416 }
1417 }
1418
1419 let mut try_pref = indic_plan.mask_array[indic_feature::PREF] != 0;
1427
1428 let mut base = start;
1429 while base < end {
1430 if buffer.info[base].indic_position() as u32 >= ot_position_t::POS_BASE_C as u32 {
1431 if try_pref && base + 1 < end {
1432 for i in base + 1..end {
1433 if (buffer.info[i].mask & indic_plan.mask_array[indic_feature::PREF]) != 0 {
1434 if !(_hb_glyph_info_substituted(&buffer.info[i])
1435 && _hb_glyph_info_ligated_and_didnt_multiply(&buffer.info[i]))
1436 {
1437 base = i;
1440 while base < end && buffer.info[base].is_halant() {
1441 base += 1;
1442 }
1443
1444 if base < end {
1445 buffer.info[base].set_indic_position(ot_position_t::POS_BASE_C);
1446 }
1447
1448 try_pref = false;
1449 }
1450
1451 break;
1452 }
1453
1454 if base == end {
1455 break;
1456 }
1457 }
1458 }
1459
1460 if buffer.script == Some(script::MALAYALAM) {
1462 let mut i = base + 1;
1463 while i < end {
1464 while i < end && buffer.info[i].is_joiner() {
1465 i += 1;
1466 }
1467
1468 if i == end || !buffer.info[i].is_halant() {
1469 break;
1470 }
1471
1472 i += 1; while i < end && buffer.info[i].is_joiner() {
1475 i += 1;
1476 }
1477
1478 if i < end
1479 && buffer.info[i].is_consonant()
1480 && buffer.info[i].indic_position() == ot_position_t::POS_BELOW_C
1481 {
1482 base = i;
1483 buffer.info[base].set_indic_position(ot_position_t::POS_BASE_C);
1484 }
1485
1486 i += 1;
1487 }
1488 }
1489
1490 if start < base
1491 && buffer.info[base].indic_position() as u32 > ot_position_t::POS_BASE_C as u32
1492 {
1493 base -= 1;
1494 }
1495
1496 break;
1497 }
1498
1499 base += 1;
1500 }
1501
1502 if base == end
1503 && start < base
1504 && buffer.info[base - 1].is_one_of(rb_flag(ot_category_t::OT_ZWJ as u32))
1505 {
1506 base -= 1;
1507 }
1508
1509 if base < end {
1510 while start < base
1511 && buffer.info[base].is_one_of(
1512 rb_flag(ot_category_t::OT_N as u32) | rb_flag(ot_category_t::OT_H as u32),
1513 )
1514 {
1515 base -= 1;
1516 }
1517 }
1518
1519 if start + 1 < end && start < base {
1548 let mut new_pos = if base == end { base - 2 } else { base - 1 };
1550
1551 if buffer.script != Some(script::MALAYALAM) && buffer.script != Some(script::TAMIL) {
1555 loop {
1556 while new_pos > start
1557 && !buffer.info[new_pos].is_one_of(
1558 rb_flag(ot_category_t::OT_M as u32)
1559 | rb_flag(ot_category_t::OT_MPst as u32)
1560 | rb_flag(ot_category_t::OT_H as u32),
1561 )
1562 {
1563 new_pos -= 1;
1564 }
1565
1566 if buffer.info[new_pos].is_halant()
1570 && buffer.info[new_pos].indic_position() != ot_position_t::POS_PRE_M
1571 {
1572 if new_pos + 1 < end {
1573 if buffer.info[new_pos + 1].indic_category() == ot_category_t::OT_ZWJ {
1575 if new_pos > start {
1577 new_pos -= 1;
1578 continue;
1579 }
1580 }
1581
1582 }
1590 } else {
1591 new_pos = start; }
1593
1594 break;
1595 }
1596 }
1597
1598 if start < new_pos && buffer.info[new_pos].indic_position() != ot_position_t::POS_PRE_M {
1599 for i in (start + 1..=new_pos).rev() {
1601 if buffer.info[i - 1].indic_position() == ot_position_t::POS_PRE_M {
1602 let old_pos = i - 1;
1603 if old_pos < base && base <= new_pos {
1605 base -= 1;
1606 }
1607
1608 let tmp = buffer.info[old_pos];
1609 for i in 0..new_pos - old_pos {
1610 buffer.info[i + old_pos] = buffer.info[i + old_pos + 1];
1611 }
1612 buffer.info[new_pos] = tmp;
1613
1614 buffer.merge_clusters(new_pos, cmp::min(end, base + 1));
1617
1618 new_pos -= 1;
1619 }
1620 }
1621 } else {
1622 for i in start..base {
1623 if buffer.info[i].indic_position() == ot_position_t::POS_PRE_M {
1624 buffer.merge_clusters(i, cmp::min(end, base + 1));
1625 break;
1626 }
1627 }
1628 }
1629 }
1630
1631 if start + 1 < end
1649 && buffer.info[start].indic_position() == ot_position_t::POS_RA_TO_BECOME_REPH
1650 && (buffer.info[start].indic_category() == ot_category_t::OT_Repha)
1651 ^ _hb_glyph_info_ligated_and_didnt_multiply(&buffer.info[start])
1652 {
1653 let mut new_reph_pos;
1654 'reph: {
1655 let reph_pos = indic_plan.config.reph_pos;
1656
1657 if reph_pos != RephPosition::AfterPost {
1660 {
1671 new_reph_pos = start + 1;
1672 while new_reph_pos < base && !buffer.info[new_reph_pos].is_halant() {
1673 new_reph_pos += 1;
1674 }
1675
1676 if new_reph_pos < base && buffer.info[new_reph_pos].is_halant() {
1677 if new_reph_pos + 1 < base && buffer.info[new_reph_pos + 1].is_joiner() {
1679 new_reph_pos += 1;
1680 }
1681
1682 break 'reph;
1683 }
1684 }
1685
1686 if reph_pos == RephPosition::AfterMain {
1690 new_reph_pos = base;
1691 while new_reph_pos + 1 < end
1692 && buffer.info[new_reph_pos + 1].indic_position()
1693 <= ot_position_t::POS_AFTER_MAIN
1694 {
1695 new_reph_pos += 1;
1696 }
1697
1698 if new_reph_pos < end {
1699 break 'reph;
1700 }
1701 }
1702
1703 if reph_pos == RephPosition::AfterSub {
1710 new_reph_pos = base;
1711 while new_reph_pos + 1 < end
1712 && (rb_flag_unsafe(buffer.info[new_reph_pos + 1].indic_position() as u32)
1713 & (rb_flag(ot_position_t::POS_POST_C as u32)
1714 | rb_flag(ot_position_t::POS_AFTER_POST as u32)
1715 | rb_flag(ot_position_t::POS_SMVD as u32)))
1716 == 0
1717 {
1718 new_reph_pos += 1;
1719 }
1720
1721 if new_reph_pos < end {
1722 break 'reph;
1723 }
1724 }
1725 }
1726
1727 new_reph_pos = start + 1;
1736 while new_reph_pos < base && !buffer.info[new_reph_pos].is_halant() {
1737 new_reph_pos += 1;
1738 }
1739
1740 if new_reph_pos < base && buffer.info[new_reph_pos].is_halant() {
1741 if new_reph_pos + 1 < base && buffer.info[new_reph_pos + 1].is_joiner() {
1743 new_reph_pos += 1;
1744 }
1745
1746 break 'reph;
1747 }
1748 {
1752 new_reph_pos = end - 1;
1753 while new_reph_pos > start
1754 && buffer.info[new_reph_pos].indic_position() == ot_position_t::POS_SMVD
1755 {
1756 new_reph_pos -= 1;
1757 }
1758
1759 if buffer.info[new_reph_pos].is_halant() {
1765 for info in &buffer.info[base + 1..new_reph_pos] {
1766 if (rb_flag_unsafe(info.indic_category() as u32)
1767 & (rb_flag(ot_category_t::OT_M as u32)
1768 | rb_flag(ot_category_t::OT_MPst as u32)))
1769 != 0
1770 {
1771 new_reph_pos -= 1;
1773 }
1774 }
1775 }
1776 }
1777
1778 break 'reph;
1779 }
1780
1781 buffer.merge_clusters(start, new_reph_pos + 1);
1783
1784 let reph = buffer.info[start];
1785 for i in 0..new_reph_pos - start {
1786 buffer.info[i + start] = buffer.info[i + start + 1];
1787 }
1788 buffer.info[new_reph_pos] = reph;
1789
1790 if start < base && base <= new_reph_pos {
1791 base -= 1;
1792 }
1793 }
1794
1795 if try_pref && base + 1 < end {
1802 for i in base + 1..end {
1803 if (buffer.info[i].mask & indic_plan.mask_array[indic_feature::PREF]) != 0 {
1804 if _hb_glyph_info_ligated_and_didnt_multiply(&buffer.info[i]) {
1813 let mut new_pos = base;
1819 if buffer.script != Some(script::MALAYALAM)
1823 && buffer.script != Some(script::TAMIL)
1824 {
1825 while new_pos > start
1826 && !buffer.info[new_pos - 1].is_one_of(
1827 rb_flag(ot_category_t::OT_M as u32)
1828 | rb_flag(ot_category_t::OT_MPst as u32)
1829 | rb_flag(ot_category_t::OT_H as u32),
1830 )
1831 {
1832 new_pos -= 1;
1833 }
1834 }
1835
1836 if new_pos > start && buffer.info[new_pos - 1].is_halant() {
1837 if new_pos < end && buffer.info[new_pos].is_joiner() {
1839 new_pos += 1;
1840 }
1841 }
1842
1843 {
1844 let old_pos = i;
1845
1846 buffer.merge_clusters(new_pos, old_pos + 1);
1847 let tmp = buffer.info[old_pos];
1848 for i in (0..old_pos - new_pos).rev() {
1849 buffer.info[i + new_pos + 1] = buffer.info[i + new_pos];
1850 }
1851 buffer.info[new_pos] = tmp;
1852
1853 if new_pos <= base && base < old_pos {
1854 #[allow(unused_assignments)]
1856 {
1857 base += 1;
1858 }
1859 }
1860 }
1861 }
1862
1863 break;
1864 }
1865 }
1866 }
1867
1868 if buffer.info[start].indic_position() == ot_position_t::POS_PRE_M {
1870 if start == 0
1871 || (rb_flag_unsafe(
1872 _hb_glyph_info_get_general_category(&buffer.info[start - 1]).to_rb(),
1873 ) & rb_flag_range(
1874 hb_gc::RB_UNICODE_GENERAL_CATEGORY_FORMAT,
1875 hb_gc::RB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK,
1876 )) == 0
1877 {
1878 buffer.info[start].mask |= indic_plan.mask_array[indic_feature::INIT];
1879 } else {
1880 buffer.unsafe_to_break(Some(start - 1), Some(start + 1));
1881 }
1882 }
1883}