1use super::aat::map::*;
2use super::buffer::*;
3use super::font_funcs::{AdvanceWidthBatch, FontFuncsDispatch};
4use super::ot_layout::*;
5use super::ot_layout_gpos_table::GPOS;
6use super::ot_map::*;
7use super::ot_shape_plan::hb_ot_shape_plan_t;
8use super::ot_shaper::*;
9use super::unicode::CharExt;
10use super::*;
11use super::{hb_font_t, hb_tag_t};
12use crate::hb::aat;
13use crate::hb::algs::{rb_flag, rb_flag_unsafe};
14use crate::hb::buffer::GlyphFlags;
15use crate::hb::unicode::hb_gc::{
16 HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER,
17 HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR, HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER,
18 HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER,
19};
20use crate::hb::unicode::GeneralCategory;
21use crate::BufferFlags;
22use crate::{Direction, Feature, Language, Script};
23use core::ptr;
24
25pub struct hb_ot_shape_planner_t<'a> {
26 pub face: &'a hb_font_t<'a>,
27 pub direction: Direction,
28 pub script: Option<Script>,
29 pub language: Option<Language>,
30 pub ot_map: hb_ot_map_builder_t<'a>,
31 pub aat_map: AatMapBuilder,
32 pub apply_morx: bool,
33 pub script_zero_marks: bool,
34 pub script_fallback_position: bool,
35 pub shaper: &'static hb_ot_shaper_t,
36}
37
38impl<'a> hb_ot_shape_planner_t<'a> {
39 pub fn new(
40 face: &'a hb_font_t<'a>,
41 direction: Direction,
42 script: Option<Script>,
43 language: Option<&Language>,
44 ) -> Self {
45 let ot_map = hb_ot_map_builder_t::new(face, script, language);
46 let aat_map = AatMapBuilder::default();
47
48 let mut shaper = match script {
49 Some(script) => hb_ot_shape_complex_categorize(
50 script,
51 direction,
52 ot_map.chosen_script(TableIndex::GSUB),
53 ),
54 None => &DEFAULT_SHAPER,
55 };
56
57 let script_zero_marks = shaper.zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
58 let script_fallback_position = shaper.fallback_position;
59
60 let apply_morx = face.aat_tables.morx.is_some()
62 && (direction.is_horizontal() || face.ot_tables.gsub.is_none());
63
64 if apply_morx && !ptr::eq(ptr::from_ref(shaper), ptr::from_ref(&DEFAULT_SHAPER)) {
66 shaper = &DUMBER_SHAPER;
67 }
68
69 hb_ot_shape_planner_t {
70 face,
71 direction,
72 script,
73 language: language.cloned(),
74 ot_map,
75 aat_map,
76 apply_morx,
77 script_zero_marks,
78 script_fallback_position,
79 shaper,
80 }
81 }
82
83 pub fn collect_features(&mut self, user_features: &[Feature]) {
84 static COMMON_FEATURES: &[(hb_tag_t, hb_ot_map_feature_flags_t)] = &[
85 (hb_tag_t::new(b"abvm"), F_GLOBAL),
86 (hb_tag_t::new(b"blwm"), F_GLOBAL),
87 (hb_tag_t::new(b"ccmp"), F_GLOBAL),
88 (hb_tag_t::new(b"locl"), F_GLOBAL),
89 (hb_tag_t::new(b"mark"), F_GLOBAL_MANUAL_JOINERS),
90 (hb_tag_t::new(b"mkmk"), F_GLOBAL_MANUAL_JOINERS),
91 (hb_tag_t::new(b"rlig"), F_GLOBAL),
92 ];
93
94 static HORIZONTAL_FEATURES: &[(hb_tag_t, hb_ot_map_feature_flags_t)] = &[
95 (hb_tag_t::new(b"calt"), F_GLOBAL),
96 (hb_tag_t::new(b"clig"), F_GLOBAL),
97 (hb_tag_t::new(b"curs"), F_GLOBAL),
98 (hb_tag_t::new(b"dist"), F_GLOBAL),
99 (hb_tag_t::new(b"kern"), F_GLOBAL_HAS_FALLBACK),
100 (hb_tag_t::new(b"liga"), F_GLOBAL),
101 (hb_tag_t::new(b"rclt"), F_GLOBAL),
102 ];
103
104 let empty = F_NONE;
105 self.ot_map.is_simple = true;
106
107 self.ot_map.enable_feature(hb_tag_t::new(b"rvrn"), empty, 1);
108 self.ot_map.add_gsub_pause(None);
109
110 match self.direction {
111 Direction::LeftToRight => {
112 self.ot_map.enable_feature(hb_tag_t::new(b"ltra"), empty, 1);
113 self.ot_map.enable_feature(hb_tag_t::new(b"ltrm"), empty, 1);
114 }
115 Direction::RightToLeft => {
116 self.ot_map.enable_feature(hb_tag_t::new(b"rtla"), empty, 1);
117 self.ot_map.add_feature(hb_tag_t::new(b"rtlm"), empty, 1);
118 }
119 _ => {}
120 }
121
122 self.ot_map.add_feature(hb_tag_t::new(b"frac"), empty, 1);
124 self.ot_map.add_feature(hb_tag_t::new(b"numr"), empty, 1);
125 self.ot_map.add_feature(hb_tag_t::new(b"dnom"), empty, 1);
126
127 self.ot_map
129 .enable_feature(hb_tag_t::new(b"rand"), F_RANDOM, hb_ot_map_t::MAX_VALUE);
130
131 self.ot_map
135 .enable_feature(hb_tag_t::new(b"trak"), F_HAS_FALLBACK, 1);
136
137 self.ot_map.enable_feature(hb_tag_t::new(b"Harf"), empty, 1); self.ot_map.enable_feature(hb_tag_t::new(b"HARF"), empty, 1); if let Some(func) = self.shaper.collect_features {
141 self.ot_map.is_simple = false;
142 func(self);
143 }
144
145 self.ot_map.enable_feature(hb_tag_t::new(b"Buzz"), empty, 1); self.ot_map.enable_feature(hb_tag_t::new(b"BUZZ"), empty, 1); for &(tag, flags) in COMMON_FEATURES {
149 self.ot_map.add_feature(tag, flags, 1);
150 }
151
152 if self.direction.is_horizontal() {
153 for &(tag, flags) in HORIZONTAL_FEATURES {
154 self.ot_map.add_feature(tag, flags, 1);
155 }
156 } else {
157 self.ot_map
166 .enable_feature(hb_tag_t::new(b"vert"), F_GLOBAL_SEARCH, 1);
167 }
168
169 if !user_features.is_empty() {
170 self.ot_map.is_simple = false;
171 }
172
173 for feature in user_features {
174 let flags = if feature.is_global() { F_GLOBAL } else { empty };
175 self.ot_map.add_feature(feature.tag, flags, feature.value);
176 }
177
178 if let Some(func) = self.shaper.override_features {
179 func(self);
180 }
181 }
182
183 pub fn compile(mut self, features: &[Feature]) -> hb_ot_shape_plan_t {
184 let ot_map = self.ot_map.compile();
185 let mut aat_map = AatMap::default();
186 if self.apply_morx {
187 self.aat_map.compile(self.face, &mut aat_map);
188 }
189
190 let frac_mask = ot_map.get_1_mask(hb_tag_t::new(b"frac"));
191 let numr_mask = ot_map.get_1_mask(hb_tag_t::new(b"numr"));
192 let dnom_mask = ot_map.get_1_mask(hb_tag_t::new(b"dnom"));
193 let has_frac = frac_mask != 0 || (numr_mask != 0 && dnom_mask != 0);
194
195 let rtlm_mask = ot_map.get_1_mask(hb_tag_t::new(b"rtlm"));
196 let has_vert = ot_map.get_1_mask(hb_tag_t::new(b"vert")) != 0;
197
198 let horizontal = self.direction.is_horizontal();
199 let kern_tag = if horizontal {
200 hb_tag_t::new(b"kern")
201 } else {
202 hb_tag_t::new(b"vkrn")
203 };
204 let kern_mask = ot_map.get_mask(kern_tag).0;
205 let requested_kerning = kern_mask != 0;
206
207 let has_gpos_kern = ot_map
208 .get_feature_index(TableIndex::GPOS, kern_tag)
209 .is_some();
210 let disable_gpos = self.shaper.gpos_tag.is_some()
211 && self.shaper.gpos_tag != ot_map.chosen_script(TableIndex::GPOS);
212
213 let fallback_glyph_classes = !hb_ot_layout_has_glyph_classes(self.face);
215
216 let apply_morx = self.apply_morx;
218
219 let mut apply_gpos = false;
220 let mut apply_kerx = false;
221 let mut apply_kern = false;
222
223 let has_kerx = self.face.aat_tables.kerx.is_some();
225 let has_gsub = !apply_morx && self.face.ot_tables.gsub.is_some();
226 let has_gpos = !disable_gpos && self.face.ot_tables.gpos.is_some();
227
228 if has_kerx && !(has_gsub && has_gpos) {
231 apply_kerx = true;
232 } else if has_gpos {
233 apply_gpos = true;
234 }
235
236 if !apply_kerx && (!has_gpos_kern || !apply_gpos) {
237 if has_kerx {
238 apply_kerx = true;
239 } else if hb_ot_layout_has_kerning(self.face) {
240 apply_kern = self.script_fallback_position;
241 }
242 }
243
244 let apply_fallback_kern = !(apply_gpos || apply_kerx || apply_kern);
245 let zero_marks = self.script_zero_marks
246 && !apply_kerx
247 && (!apply_kern || !hb_ot_layout_has_machine_kerning(self.face));
248
249 let has_gpos_mark = ot_map.get_1_mask(hb_tag_t::new(b"mark")) != 0;
250
251 let mut adjust_mark_positioning_when_zeroing = !apply_gpos
252 && !apply_kerx
253 && (!apply_kern || !hb_ot_layout_has_cross_kerning(self.face));
254
255 let fallback_mark_positioning =
256 adjust_mark_positioning_when_zeroing && self.script_fallback_position;
257
258 if apply_morx {
262 adjust_mark_positioning_when_zeroing = false;
263 }
264
265 let apply_trak = self.face.apply_trak;
268
269 let mut plan = hb_ot_shape_plan_t {
270 direction: self.direction,
271 script: self.script,
272 language: self.language,
273 shaper: self.shaper,
274 ot_map,
275 aat_map,
276 data: None,
277 frac_mask,
278 numr_mask,
279 dnom_mask,
280 rtlm_mask,
281 kern_mask,
282 requested_kerning,
283 has_frac,
284 has_vert,
285 has_gpos_mark,
286 zero_marks,
287 fallback_glyph_classes,
288 fallback_mark_positioning,
289 adjust_mark_positioning_when_zeroing,
290 apply_gpos,
291 apply_kern,
292 apply_fallback_kern,
293 apply_kerx,
294 apply_morx,
295 apply_trak,
296 user_features: features.into(),
297 };
298
299 if let Some(func) = self.shaper.create_data {
300 plan.data = Some(func(&plan));
301 }
302
303 plan
304 }
305}
306
307pub(crate) struct OtShapeContext<'a, 'u> {
309 pub plan: &'a hb_ot_shape_plan_t,
310 pub face: &'a hb_font_t<'a>,
311 pub buffer: &'a mut hb_buffer_t,
312 pub features: &'a [Feature],
313 pub target_direction: Direction,
315 pub point_size: Option<f32>,
316 pub font_funcs: &'a mut FontFuncsDispatch<'a, 'u>,
317}
318
319impl OtShapeContext<'_, '_> {
320 fn glyph_h_advances(&mut self) {
321 if self.buffer.len == 0 {
322 return;
323 }
324 let batched_advances = AdvanceWidthBatch::new(self.buffer);
325 self.font_funcs.populate_advance_widths(batched_advances);
326 }
327
328 pub(crate) fn shape_internal(&mut self) {
330 self.buffer.allocate_unicode_vars();
331
332 self.initialize_masks();
333 self.set_unicode_props();
334 self.insert_dotted_circle();
335
336 form_clusters(self.buffer);
337
338 ensure_native_direction(self.buffer);
339
340 if let Some(func) = self.plan.shaper.preprocess_text {
341 func(self.plan, self.font_funcs, self.buffer);
342 }
343
344 self.substitute_pre();
345 self.position();
346 self.substitute_post();
347
348 propagate_flags(self.buffer);
349
350 self.buffer.deallocate_unicode_vars();
351
352 self.buffer.direction = self.target_direction;
353 }
354
355 fn substitute_pre(&mut self) {
357 self.substitute_default();
358
359 self.buffer.allocate_gsubgpos_vars();
360
361 self.substitute_plan();
362
363 if self.plan.apply_morx && self.plan.apply_gpos {
364 aat::layout::remove_deleted_glyphs(self.buffer);
365 }
366 }
367
368 fn substitute_post(&mut self) {
370 if self.plan.apply_morx && !self.plan.apply_gpos {
371 aat::layout::remove_deleted_glyphs(self.buffer);
372 }
373
374 deal_with_variation_selectors(self.buffer);
375 hide_default_ignorables(self.buffer, self.font_funcs);
376
377 if let Some(func) = self.plan.shaper.postprocess_glyphs {
378 func(self.plan, self.font_funcs, self.buffer);
379 }
380 }
381
382 fn substitute_default(&mut self) {
384 self.rotate_chars();
385
386 self.buffer
387 .allocate_var(GlyphInfo::NORMALIZER_GLYPH_INDEX_VAR);
388
389 ot_shape_normalize::_hb_ot_shape_normalize(
390 self.plan,
391 self.buffer,
392 self.face,
393 self.font_funcs,
394 );
395
396 self.setup_masks();
397
398 if self.plan.fallback_mark_positioning {
400 ot_shape_fallback::_hb_ot_shape_fallback_mark_position_recategorize_marks(
401 self.plan,
402 self.face,
403 self.buffer,
404 );
405 }
406
407 map_glyphs_fast(self.buffer);
408
409 self.buffer
410 .deallocate_var(GlyphInfo::NORMALIZER_GLYPH_INDEX_VAR);
411 }
412
413 fn substitute_plan(&mut self) {
415 hb_ot_layout_substitute_start(self.face, self.buffer);
416
417 if self.plan.fallback_glyph_classes {
418 hb_synthesize_glyph_classes(self.buffer);
419 }
420
421 if self.plan.apply_morx {
422 aat::layout::substitute(self.plan, self.face, self.buffer, self.features);
423 self.buffer.update_digest();
424 } else {
425 self.buffer.update_digest();
426 ot_layout_gsub_table::substitute(self.plan, self.face, self.font_funcs, self.buffer);
427 }
428 }
429
430 fn position(&mut self) {
432 self.buffer.clear_positions();
433
434 self.position_default();
435 self.position_plan();
436
437 if self.buffer.direction.is_backward() {
438 self.buffer.reverse();
439 }
440
441 self.buffer.deallocate_gsubgpos_vars();
442 }
443
444 fn position_default(&mut self) {
446 let len = self.buffer.len;
447
448 if self.buffer.direction.is_horizontal() {
449 self.glyph_h_advances();
450 } else {
451 for (info, pos) in self.buffer.info[..len]
452 .iter()
453 .zip(&mut self.buffer.pos[..len])
454 {
455 let glyph = info.as_glyph();
456 pos.y_advance = self.font_funcs.advance_height(glyph);
457 let (x, y) = self.font_funcs.vertical_origin(glyph);
458 pos.x_offset -= x;
459 pos.y_offset -= y;
460 }
461 }
462
463 if self.buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK != 0 {
464 ot_shape_fallback::_hb_ot_shape_fallback_spaces(
465 self.plan,
466 self.face,
467 self.buffer,
468 self.font_funcs,
469 );
470 }
471 }
472
473 fn position_plan(&mut self) {
475 let adjust_offsets_when_zeroing =
484 self.plan.adjust_mark_positioning_when_zeroing && self.buffer.direction.is_forward();
485
486 GPOS::position_start(self.face, self.buffer);
489
490 if self.plan.zero_marks
491 && self.plan.shaper.zero_width_marks == HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY
492 {
493 zero_mark_widths_by_gdef(self.buffer, adjust_offsets_when_zeroing);
494 }
495
496 self.position_by_plan();
497
498 if self.plan.zero_marks
499 && self.plan.shaper.zero_width_marks == HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
500 {
501 zero_mark_widths_by_gdef(self.buffer, adjust_offsets_when_zeroing);
502 }
503
504 GPOS::position_finish_advances(self.face, self.buffer);
506 zero_width_default_ignorables(self.buffer);
507 GPOS::position_finish_offsets(self.face, self.buffer);
508
509 if self.plan.fallback_mark_positioning {
510 ot_shape_fallback::position_marks(
511 self.plan,
512 self.face,
513 self.buffer,
514 adjust_offsets_when_zeroing,
515 self.font_funcs,
516 );
517 }
518 }
519
520 fn position_by_plan(&mut self) {
522 let plan = self.plan;
523 let face = self.face;
524 let buffer = &mut *self.buffer;
525 if plan.apply_gpos {
526 ot_layout_gpos_table::position(plan, face, self.font_funcs, buffer);
527 } else if plan.apply_kerx {
528 aat::layout::position(plan, face, *self.font_funcs.scale(), buffer);
529 }
530 if plan.apply_kern {
531 kerning::hb_ot_layout_kern(plan, face, *self.font_funcs.scale(), buffer);
532 } else if plan.apply_fallback_kern {
533 ot_shape_fallback::_hb_ot_shape_fallback_kern(plan, face, buffer);
534 }
535
536 if plan.apply_trak {
537 aat::layout::track(
538 plan,
539 face,
540 *self.font_funcs.scale(),
541 self.point_size,
542 buffer,
543 );
544 }
545 }
546
547 fn initialize_masks(&mut self) {
549 let global_mask = self.plan.ot_map.get_global_mask();
550 self.buffer.reset_masks(global_mask);
551 }
552
553 fn setup_masks(&mut self) {
555 self.setup_masks_fraction();
556
557 if let Some(func) = self.plan.shaper.setup_masks {
558 func(self.plan, self.font_funcs, self.buffer);
559 }
560
561 for feature in self.features {
562 if !feature.is_global() {
563 let (mask, shift) = self.plan.ot_map.get_mask(feature.tag);
564 self.buffer
565 .set_masks(feature.value << shift, mask, feature.start, feature.end);
566 }
567 }
568 }
569
570 fn setup_masks_fraction(&mut self) {
572 let buffer = &mut *self.buffer;
573 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH == 0
574 || !self.plan.has_frac
575 {
576 return;
577 }
578
579 let (pre_mask, post_mask) = if buffer.direction.is_forward() {
580 (
581 self.plan.numr_mask | self.plan.frac_mask,
582 self.plan.frac_mask | self.plan.dnom_mask,
583 )
584 } else {
585 (
586 self.plan.frac_mask | self.plan.dnom_mask,
587 self.plan.numr_mask | self.plan.frac_mask,
588 )
589 };
590
591 let len = buffer.len;
592 let mut i = 0;
593 while i < len {
594 if buffer.info[i].glyph_id == 0x2044 {
596 let mut start = i;
597 while start > 0
598 && buffer.info[start - 1].general_category() == GeneralCategory::DECIMAL_NUMBER
599 {
600 start -= 1;
601 }
602
603 let mut end = i + 1;
604 while end < len
605 && buffer.info[end].general_category() == GeneralCategory::DECIMAL_NUMBER
606 {
607 end += 1;
608 }
609
610 if start == i || end == i + 1 {
611 if start == i {
612 buffer.unsafe_to_concat(Some(start), Some(start + 1));
613 }
614
615 if end == i + 1 {
616 buffer.unsafe_to_concat(Some(end - 1), Some(end));
617 }
618
619 i += 1;
620 continue;
621 }
622
623 buffer.unsafe_to_break(Some(start), Some(end));
624
625 for info in &mut buffer.info[start..i] {
626 info.mask |= pre_mask;
627 }
628
629 buffer.info[i].mask |= self.plan.frac_mask;
630
631 for info in &mut buffer.info[i + 1..end] {
632 info.mask |= post_mask;
633 }
634
635 i = end;
636 } else {
637 i += 1;
638 }
639 }
640 }
641
642 fn set_unicode_props(&mut self) {
644 let buffer = &mut *self.buffer;
645 let len = buffer.len;
654
655 let mut i = 0;
656 while i < len {
657 let info = &mut buffer.info[i];
658 info.init_unicode_props(&mut buffer.scratch_flags);
659
660 if info.glyph_id < 0x80 {
661 i += 1;
662 continue;
663 }
664
665 let gen_cat = info.general_category();
666
667 if (rb_flag_unsafe(gen_cat.to_u8() as u32)
668 & (rb_flag(HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER)
669 | rb_flag(HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER)
670 | rb_flag(HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER)
671 | rb_flag(HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER)
672 | rb_flag(HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR)))
673 != 0
674 {
675 i += 1;
676 continue;
677 }
678
679 let (prior, later) = buffer.info.split_at_mut(i);
683 let info = &mut later[0];
684
685 if gen_cat == GeneralCategory::MODIFIER_SYMBOL
688 && matches!(info.glyph_id, 0x1F3FB..=0x1F3FF)
689 {
690 info.set_continuation(&mut buffer.scratch_flags);
691 } else if i != 0 && matches!(info.glyph_id, 0x1F1E6..=0x1F1FF) {
692 let prev = prior.last().unwrap();
695 if matches!(prev.glyph_id, 0x1F1E6..=0x1F1FF) && !prev.is_continuation() {
696 info.set_continuation(&mut buffer.scratch_flags);
697 }
698 } else if info.is_zwj() {
699 info.set_continuation(&mut buffer.scratch_flags);
700 if let Some(next) = buffer.info[..len].get_mut(i + 1) {
701 if next.as_codepoint().is_emoji_extended_pictographic() {
702 next.init_unicode_props(&mut buffer.scratch_flags);
703 next.set_continuation(&mut buffer.scratch_flags);
704 i += 1;
705 }
706 }
707 } else if matches!(info.glyph_id, 0xFF9E..=0xFF9F | 0xE0020..=0xE007F) {
708 info.set_continuation(&mut buffer.scratch_flags);
722 } else if info.glyph_id == 0x2044
723 {
725 buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH;
726 }
727
728 i += 1;
729 }
730 }
731
732 fn insert_dotted_circle(&mut self) {
734 let should_insert = {
735 let buffer = &*self.buffer;
736 !buffer
737 .flags
738 .contains(BufferFlags::DO_NOT_INSERT_DOTTED_CIRCLE)
739 && buffer.flags.contains(BufferFlags::BEGINNING_OF_TEXT)
740 && buffer.context_len[0] == 0
741 && buffer.info[0].is_unicode_mark()
742 };
743
744 if should_insert && self.font_funcs.nominal_glyph(0x25CC).is_some() {
745 let mask = self.buffer.cur(0).mask;
746 let cluster = self.buffer.cur(0).cluster;
747 let buffer = &mut *self.buffer;
748 let mut info = GlyphInfo {
749 glyph_id: 0x25CC,
750 mask,
751 cluster,
752 ..GlyphInfo::default()
753 };
754
755 info.init_unicode_props(&mut buffer.scratch_flags);
756 buffer.clear_output();
757 buffer.output_info(info);
758 buffer.sync();
759 }
760 }
761
762 fn rotate_chars(&mut self) {
764 let len = self.buffer.len;
765
766 if self.target_direction.is_backward() {
767 let rtlm_mask = self.plan.rtlm_mask;
768
769 for info in &mut self.buffer.info[..len] {
770 if let Some(c) = info.as_codepoint().mirrored() {
771 if self.font_funcs.nominal_glyph(c).is_some() {
772 info.glyph_id = c;
773 continue;
774 }
775 }
776 info.mask |= rtlm_mask;
777 }
778 }
779
780 if self.target_direction.is_vertical() && !self.plan.has_vert {
781 for info in &mut self.buffer.info[..len] {
782 if let Some(c) = info.as_codepoint().vertical() {
783 if self.font_funcs.nominal_glyph(c).is_some() {
784 info.glyph_id = c;
785 }
786 }
787 }
788 }
789 }
790}
791
792fn form_clusters(buffer: &mut hb_buffer_t) {
793 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS != 0 {
794 foreach_grapheme!(buffer, start, end, {
795 buffer.merge_grapheme_clusters(start, end);
796 });
797 }
798}
799
800fn ensure_native_direction(buffer: &mut hb_buffer_t) {
801 let dir = buffer.direction;
802 let mut hor = buffer
803 .script
804 .and_then(Direction::from_script)
805 .unwrap_or_default();
806
807 if hor == Direction::RightToLeft && dir == Direction::LeftToRight {
825 let mut found_number = false;
826 let mut found_letter = false;
827 let mut found_ri = false;
828 for info in &buffer.info {
829 let gc = info.general_category();
830 if gc == GeneralCategory::DECIMAL_NUMBER {
831 found_number = true;
832 } else if gc.is_letter() {
833 found_letter = true;
834 break;
835 } else if matches!(info.glyph_id, 0x1F1E6..=0x1F1FF) {
836 found_ri = true;
837 }
838 }
839 if (found_number || found_ri) && !found_letter {
840 hor = Direction::LeftToRight;
841 }
842 }
843
844 if (dir.is_horizontal() && dir != hor && hor != Direction::Invalid)
849 || (dir.is_vertical() && dir != Direction::TopToBottom)
850 {
851 _hb_ot_layout_reverse_graphemes(buffer);
852 buffer.direction = buffer.direction.reverse();
853 }
854}
855
856fn map_glyphs_fast(buffer: &mut hb_buffer_t) {
857 let len = buffer.len;
859 for info in &mut buffer.info[..len] {
860 info.glyph_id = info.normalizer_glyph_index();
861 }
862
863 for info in &mut buffer.out_info_mut()[..len] {
864 info.glyph_id = info.normalizer_glyph_index();
865 }
866}
867
868fn hb_synthesize_glyph_classes(buffer: &mut hb_buffer_t) {
869 let len = buffer.len;
870 for info in &mut buffer.info[..len] {
871 let class = if info.general_category() != GeneralCategory::NON_SPACING_MARK
880 || info.is_default_ignorable()
881 {
882 GlyphPropsFlags::BASE_GLYPH
883 } else {
884 GlyphPropsFlags::MARK
885 };
886
887 info.set_glyph_props(class.bits());
888 }
889}
890
891fn zero_width_default_ignorables(buffer: &mut hb_buffer_t) {
892 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES != 0
893 && !buffer
894 .flags
895 .contains(BufferFlags::PRESERVE_DEFAULT_IGNORABLES)
896 && !buffer
897 .flags
898 .contains(BufferFlags::REMOVE_DEFAULT_IGNORABLES)
899 {
900 let len = buffer.len;
901 for (info, pos) in buffer.info[..len].iter().zip(&mut buffer.pos[..len]) {
902 if info.is_default_ignorable() {
903 pos.x_advance = 0;
904 pos.y_advance = 0;
905 if buffer.direction.is_horizontal() {
906 pos.x_offset = 0;
907 } else {
908 pos.y_offset = 0;
909 }
910 }
911 }
912 }
913}
914
915fn deal_with_variation_selectors(buffer: &mut hb_buffer_t) {
916 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK == 0 {
917 return;
918 }
919
920 let Some(nf) = buffer.not_found_variation_selector else {
923 return;
924 };
925
926 let count = buffer.len;
927 let info = &mut buffer.info;
928 let pos = &mut buffer.pos;
929
930 for i in 0..count {
931 if info[i].is_variation_selector() {
932 info[i].glyph_id = nf;
933 pos[i].x_advance = 0;
934 pos[i].y_advance = 0;
935 pos[i].x_offset = 0;
936 pos[i].y_offset = 0;
937 info[0].set_variation_selector(false);
938 }
939 }
940}
941
942fn zero_mark_widths_by_gdef(buffer: &mut hb_buffer_t, adjust_offsets: bool) {
943 let len = buffer.len;
944 for (info, pos) in buffer.info[..len].iter().zip(&mut buffer.pos[..len]) {
945 if info.is_mark() {
946 if adjust_offsets {
947 pos.x_offset -= pos.x_advance;
948 pos.y_offset -= pos.y_advance;
949 }
950
951 pos.x_advance = 0;
952 pos.y_advance = 0;
953 }
954 }
955}
956
957fn hide_default_ignorables(buffer: &mut hb_buffer_t, font_funcs: &mut FontFuncsDispatch<'_, '_>) {
958 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES != 0
959 && !buffer
960 .flags
961 .contains(BufferFlags::PRESERVE_DEFAULT_IGNORABLES)
962 {
963 if !buffer
964 .flags
965 .contains(BufferFlags::REMOVE_DEFAULT_IGNORABLES)
966 {
967 if let Some(invisible) = buffer
968 .invisible
969 .or_else(|| font_funcs.nominal_glyph(u32::from(' ')))
970 {
971 let len = buffer.len;
972 for info in &mut buffer.info[..len] {
973 if info.is_default_ignorable() {
974 info.glyph_id = invisible.to_u32();
975 }
976 }
977 return;
978 }
979 }
980
981 buffer.delete_glyphs_inplace(GlyphInfo::is_default_ignorable);
982 }
983}
984
985fn propagate_flags(buffer: &mut hb_buffer_t) {
986 let mut and_mask = GlyphFlags::DEFINED_BITS;
990 if !buffer.flags.contains(BufferFlags::PRODUCE_UNSAFE_TO_CONCAT) {
991 and_mask &= !GlyphFlags::UNSAFE_TO_CONCAT.0;
992 }
993
994 if !buffer
995 .flags
996 .contains(BufferFlags::PRODUCE_SAFE_TO_INSERT_TATWEEL)
997 {
998 foreach_cluster!(buffer, start, end, {
999 if end - start == 1 {
1000 buffer.info[start].mask &= and_mask;
1001 } else {
1002 let mut mask = 0;
1003 for info in &buffer.info[start..end] {
1004 mask |= info.mask;
1005 }
1006
1007 mask &= and_mask;
1008
1009 for info in &mut buffer.info[start..end] {
1010 info.mask = mask;
1011 }
1012 }
1013 });
1014 return;
1015 }
1016
1017 foreach_cluster!(buffer, start, end, {
1026 if end - start != 1 {
1028 let mut mask = 0;
1029 for info in &buffer.info[start..end] {
1030 mask |= info.mask;
1031 }
1032
1033 mask &= GlyphFlags::DEFINED_BITS;
1034
1035 if mask & GlyphFlags::UNSAFE_TO_BREAK.0 != 0 {
1036 mask &= !GlyphFlags::SAFE_TO_INSERT_TATWEEL.0;
1037 }
1038
1039 if mask & GlyphFlags::SAFE_TO_INSERT_TATWEEL.0 != 0 {
1040 mask |= GlyphFlags::UNSAFE_TO_BREAK.0 | GlyphFlags::UNSAFE_TO_CONCAT.0;
1041 }
1042
1043 mask &= and_mask;
1044
1045 for info in &mut buffer.info[start..end] {
1046 info.mask = mask;
1047 }
1048 }
1049 });
1050}