1use super::aat_map;
2use super::buffer::*;
3use super::ot_layout::*;
4use super::ot_layout_gpos_table::GPOS;
5use super::ot_map::*;
6use super::ot_shape_complex::*;
7use super::ot_shape_plan::hb_ot_shape_plan_t;
8use super::unicode::{hb_unicode_general_category_t, CharExt, GeneralCategoryExt};
9use super::*;
10use super::{hb_font_t, hb_tag_t};
11use crate::hb::aat_layout::hb_aat_layout_remove_deleted_glyphs;
12use crate::BufferFlags;
13use crate::{Direction, Feature, Language, Script};
14
15pub struct hb_ot_shape_planner_t<'a> {
16 pub face: &'a hb_font_t<'a>,
17 pub direction: Direction,
18 pub script: Option<Script>,
19 pub ot_map: hb_ot_map_builder_t<'a>,
20 pub aat_map: aat_map::hb_aat_map_builder_t,
21 pub apply_morx: bool,
22 pub script_zero_marks: bool,
23 pub script_fallback_mark_positioning: bool,
24 pub shaper: &'static hb_ot_complex_shaper_t,
25}
26
27impl<'a> hb_ot_shape_planner_t<'a> {
28 pub fn new(
29 face: &'a hb_font_t<'a>,
30 direction: Direction,
31 script: Option<Script>,
32 language: Option<&Language>,
33 ) -> Self {
34 let ot_map = hb_ot_map_builder_t::new(face, script, language);
35 let aat_map = aat_map::hb_aat_map_builder_t::default();
36
37 let mut shaper = match script {
38 Some(script) => hb_ot_shape_complex_categorize(
39 script,
40 direction,
41 ot_map.chosen_script(TableIndex::GSUB),
42 ),
43 None => &DEFAULT_SHAPER,
44 };
45
46 let script_zero_marks = shaper.zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
47 let script_fallback_mark_positioning = shaper.fallback_position;
48
49 let apply_morx =
51 face.tables().morx.is_some() && (direction.is_horizontal() || face.gsub.is_none());
52
53 if apply_morx && shaper as *const _ != &DEFAULT_SHAPER as *const _ {
55 shaper = &DUMBER_SHAPER;
56 }
57
58 hb_ot_shape_planner_t {
59 face,
60 direction,
61 script,
62 ot_map,
63 aat_map,
64 apply_morx,
65 script_zero_marks,
66 script_fallback_mark_positioning,
67 shaper,
68 }
69 }
70
71 pub fn collect_features(&mut self, user_features: &[Feature]) {
72 const COMMON_FEATURES: &[(hb_tag_t, hb_ot_map_feature_flags_t)] = &[
73 (hb_tag_t::from_bytes(b"abvm"), F_GLOBAL),
74 (hb_tag_t::from_bytes(b"blwm"), F_GLOBAL),
75 (hb_tag_t::from_bytes(b"ccmp"), F_GLOBAL),
76 (hb_tag_t::from_bytes(b"locl"), F_GLOBAL),
77 (hb_tag_t::from_bytes(b"mark"), F_GLOBAL_MANUAL_JOINERS),
78 (hb_tag_t::from_bytes(b"mkmk"), F_GLOBAL_MANUAL_JOINERS),
79 (hb_tag_t::from_bytes(b"rlig"), F_GLOBAL),
80 ];
81
82 const HORIZONTAL_FEATURES: &[(hb_tag_t, hb_ot_map_feature_flags_t)] = &[
83 (hb_tag_t::from_bytes(b"calt"), F_GLOBAL),
84 (hb_tag_t::from_bytes(b"clig"), F_GLOBAL),
85 (hb_tag_t::from_bytes(b"curs"), F_GLOBAL),
86 (hb_tag_t::from_bytes(b"dist"), F_GLOBAL),
87 (hb_tag_t::from_bytes(b"kern"), F_GLOBAL_HAS_FALLBACK),
88 (hb_tag_t::from_bytes(b"liga"), F_GLOBAL),
89 (hb_tag_t::from_bytes(b"rclt"), F_GLOBAL),
90 ];
91
92 let empty = F_NONE;
93
94 self.ot_map
95 .enable_feature(hb_tag_t::from_bytes(b"rvrn"), empty, 1);
96 self.ot_map.add_gsub_pause(None);
97
98 match self.direction {
99 Direction::LeftToRight => {
100 self.ot_map
101 .enable_feature(hb_tag_t::from_bytes(b"ltra"), empty, 1);
102 self.ot_map
103 .enable_feature(hb_tag_t::from_bytes(b"ltrm"), empty, 1);
104 }
105 Direction::RightToLeft => {
106 self.ot_map
107 .enable_feature(hb_tag_t::from_bytes(b"rtla"), empty, 1);
108 self.ot_map
109 .add_feature(hb_tag_t::from_bytes(b"rtlm"), empty, 1);
110 }
111 _ => {}
112 }
113
114 self.ot_map
116 .add_feature(hb_tag_t::from_bytes(b"frac"), empty, 1);
117 self.ot_map
118 .add_feature(hb_tag_t::from_bytes(b"numr"), empty, 1);
119 self.ot_map
120 .add_feature(hb_tag_t::from_bytes(b"dnom"), empty, 1);
121
122 self.ot_map.enable_feature(
124 hb_tag_t::from_bytes(b"rand"),
125 F_RANDOM,
126 hb_ot_map_t::MAX_VALUE,
127 );
128
129 self.ot_map
133 .enable_feature(hb_tag_t::from_bytes(b"trak"), F_HAS_FALLBACK, 1);
134
135 self.ot_map
136 .enable_feature(hb_tag_t::from_bytes(b"Harf"), empty, 1); self.ot_map
138 .enable_feature(hb_tag_t::from_bytes(b"HARF"), empty, 1); if let Some(func) = self.shaper.collect_features {
141 func(self);
142 }
143
144 self.ot_map
145 .enable_feature(hb_tag_t::from_bytes(b"Buzz"), empty, 1); self.ot_map
147 .enable_feature(hb_tag_t::from_bytes(b"BUZZ"), empty, 1); for &(tag, flags) in COMMON_FEATURES {
150 self.ot_map.add_feature(tag, flags, 1);
151 }
152
153 if self.direction.is_horizontal() {
154 for &(tag, flags) in HORIZONTAL_FEATURES {
155 self.ot_map.add_feature(tag, flags, 1);
156 }
157 } else {
158 self.ot_map
167 .enable_feature(hb_tag_t::from_bytes(b"vert"), F_GLOBAL_SEARCH, 1);
168 }
169
170 for feature in user_features {
171 let flags = if feature.is_global() { F_GLOBAL } else { empty };
172 self.ot_map.add_feature(feature.tag, flags, feature.value);
173 }
174
175 if self.apply_morx {
176 for feature in user_features {
177 self.aat_map
178 .add_feature(self.face, feature.tag, feature.value);
179 }
180 }
181
182 if let Some(func) = self.shaper.override_features {
183 func(self);
184 }
185 }
186
187 pub fn compile(mut self, user_features: &[Feature]) -> hb_ot_shape_plan_t {
188 let ot_map = self.ot_map.compile();
189
190 let aat_map = if self.apply_morx {
191 self.aat_map.compile(self.face)
192 } else {
193 aat_map::hb_aat_map_t::default()
194 };
195
196 let frac_mask = ot_map.get_1_mask(hb_tag_t::from_bytes(b"frac"));
197 let numr_mask = ot_map.get_1_mask(hb_tag_t::from_bytes(b"numr"));
198 let dnom_mask = ot_map.get_1_mask(hb_tag_t::from_bytes(b"dnom"));
199 let has_frac = frac_mask != 0 || (numr_mask != 0 && dnom_mask != 0);
200
201 let rtlm_mask = ot_map.get_1_mask(hb_tag_t::from_bytes(b"rtlm"));
202 let has_vert = ot_map.get_1_mask(hb_tag_t::from_bytes(b"vert")) != 0;
203
204 let horizontal = self.direction.is_horizontal();
205 let kern_tag = if horizontal {
206 hb_tag_t::from_bytes(b"kern")
207 } else {
208 hb_tag_t::from_bytes(b"vkrn")
209 };
210 let kern_mask = ot_map.get_mask(kern_tag).0;
211 let requested_kerning = kern_mask != 0;
212 let trak_mask = ot_map.get_mask(hb_tag_t::from_bytes(b"trak")).0;
213 let requested_tracking = trak_mask != 0;
214
215 let has_gpos_kern = ot_map
216 .get_feature_index(TableIndex::GPOS, kern_tag)
217 .is_some();
218 let disable_gpos = self.shaper.gpos_tag.is_some()
219 && self.shaper.gpos_tag != ot_map.chosen_script(TableIndex::GPOS);
220
221 let fallback_glyph_classes = !hb_ot_layout_has_glyph_classes(self.face);
223
224 let apply_morx = self.apply_morx;
226
227 let mut apply_gpos = false;
228 let mut apply_kerx = false;
229 let mut apply_kern = false;
230
231 let has_kerx = self.face.tables().kerx.is_some();
233 let has_gsub = self.face.tables().gsub.is_some();
234 let has_gpos = !disable_gpos && self.face.tables().gpos.is_some();
235
236 if has_kerx && !(has_gsub && has_gpos) {
239 apply_kerx = true;
240 } else if has_gpos {
241 apply_gpos = true;
242 }
243
244 if !apply_kerx && (!has_gpos_kern || !apply_gpos) {
245 if has_kerx {
246 apply_kerx = true;
247 } else if hb_ot_layout_has_kerning(self.face) {
248 apply_kern = true;
249 }
250 }
251
252 let apply_fallback_kern = !(apply_gpos || apply_kerx || apply_kern);
253 let zero_marks = self.script_zero_marks
254 && !apply_kerx
255 && (!apply_kern || !hb_ot_layout_has_machine_kerning(self.face));
256
257 let has_gpos_mark = ot_map.get_1_mask(hb_tag_t::from_bytes(b"mark")) != 0;
258
259 let mut adjust_mark_positioning_when_zeroing = !apply_gpos
260 && !apply_kerx
261 && (!apply_kern || !hb_ot_layout_has_cross_kerning(self.face));
262
263 let fallback_mark_positioning =
264 adjust_mark_positioning_when_zeroing && self.script_fallback_mark_positioning;
265
266 if apply_morx {
270 adjust_mark_positioning_when_zeroing = false;
271 }
272
273 let apply_trak = requested_tracking && self.face.tables().trak.is_some();
275
276 let mut plan = hb_ot_shape_plan_t {
277 direction: self.direction,
278 script: self.script,
279 shaper: self.shaper,
280 ot_map,
281 aat_map,
282 data: None,
283 frac_mask,
284 numr_mask,
285 dnom_mask,
286 rtlm_mask,
287 kern_mask,
288 trak_mask,
289 requested_kerning,
290 has_frac,
291 has_vert,
292 has_gpos_mark,
293 zero_marks,
294 fallback_glyph_classes,
295 fallback_mark_positioning,
296 adjust_mark_positioning_when_zeroing,
297 apply_gpos,
298 apply_kern,
299 apply_fallback_kern,
300 apply_kerx,
301 apply_morx,
302 apply_trak,
303 user_features: user_features.to_vec(),
304 };
305
306 if let Some(func) = self.shaper.create_data {
307 plan.data = Some(func(&plan));
308 }
309
310 plan
311 }
312}
313
314pub struct ShapeContext<'a> {
315 pub plan: &'a hb_ot_shape_plan_t,
316 pub face: &'a hb_font_t<'a>,
317 pub buffer: &'a mut hb_buffer_t,
318 pub target_direction: Direction,
320}
321
322pub fn shape_internal(ctx: &mut ShapeContext) {
324 ctx.buffer.enter();
325
326 initialize_masks(ctx);
327 set_unicode_props(ctx.buffer);
328 insert_dotted_circle(ctx.buffer, ctx.face);
329
330 form_clusters(ctx.buffer);
331
332 ensure_native_direction(ctx.buffer);
333
334 if let Some(func) = ctx.plan.shaper.preprocess_text {
335 func(ctx.plan, ctx.face, ctx.buffer);
336 }
337
338 substitute_pre(ctx);
339 position(ctx);
340 substitute_post(ctx);
341
342 propagate_flags(ctx.buffer);
343
344 ctx.buffer.direction = ctx.target_direction;
345 ctx.buffer.leave();
346}
347
348fn substitute_pre(ctx: &mut ShapeContext) {
349 hb_ot_substitute_default(ctx);
350 hb_ot_substitute_complex(ctx);
351
352 if ctx.plan.apply_morx && !ctx.plan.apply_gpos {
353 hb_aat_layout_remove_deleted_glyphs(&mut ctx.buffer);
354 }
355}
356
357fn substitute_post(ctx: &mut ShapeContext) {
358 if ctx.plan.apply_morx && ctx.plan.apply_gpos {
359 aat_layout::hb_aat_layout_remove_deleted_glyphs(ctx.buffer);
360 }
361
362 hide_default_ignorables(ctx.buffer, ctx.face);
363
364 if let Some(func) = ctx.plan.shaper.postprocess_glyphs {
365 func(ctx.plan, ctx.face, ctx.buffer);
366 }
367}
368
369fn hb_ot_substitute_default(ctx: &mut ShapeContext) {
370 rotate_chars(ctx);
371
372 ot_shape_normalize::_hb_ot_shape_normalize(ctx.plan, ctx.buffer, ctx.face);
373
374 setup_masks(ctx);
375
376 if ctx.plan.fallback_mark_positioning {
378 ot_shape_fallback::_hb_ot_shape_fallback_mark_position_recategorize_marks(
379 ctx.plan, ctx.face, ctx.buffer,
380 );
381 }
382
383 map_glyphs_fast(ctx.buffer);
384}
385
386fn hb_ot_substitute_complex(ctx: &mut ShapeContext) {
387 hb_ot_layout_substitute_start(ctx.face, ctx.buffer);
388
389 if ctx.plan.fallback_glyph_classes {
390 hb_synthesize_glyph_classes(ctx.buffer);
391 }
392
393 substitute_by_plan(ctx.plan, ctx.face, ctx.buffer);
394}
395
396fn substitute_by_plan(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) {
397 if plan.apply_morx {
398 aat_layout::hb_aat_layout_substitute(plan, face, buffer);
399 } else {
400 super::ot_layout_gsub_table::substitute(plan, face, buffer);
401 }
402}
403
404fn position(ctx: &mut ShapeContext) {
405 ctx.buffer.clear_positions();
406
407 position_default(ctx);
408
409 position_complex(ctx);
410
411 if ctx.buffer.direction.is_backward() {
412 ctx.buffer.reverse();
413 }
414}
415
416fn position_default(ctx: &mut ShapeContext) {
417 let len = ctx.buffer.len;
418
419 if ctx.buffer.direction.is_horizontal() {
420 for (info, pos) in ctx.buffer.info[..len]
421 .iter()
422 .zip(&mut ctx.buffer.pos[..len])
423 {
424 pos.x_advance = ctx.face.glyph_h_advance(info.as_glyph());
425 }
426 } else {
427 for (info, pos) in ctx.buffer.info[..len]
428 .iter()
429 .zip(&mut ctx.buffer.pos[..len])
430 {
431 let glyph = info.as_glyph();
432 pos.y_advance = ctx.face.glyph_v_advance(glyph);
433 pos.x_offset -= ctx.face.glyph_h_origin(glyph);
434 pos.y_offset -= ctx.face.glyph_v_origin(glyph);
435 }
436 }
437
438 if ctx.buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK != 0 {
439 ot_shape_fallback::_hb_ot_shape_fallback_spaces(ctx.plan, ctx.face, ctx.buffer);
440 }
441}
442
443fn position_complex(ctx: &mut ShapeContext) {
444 let adjust_offsets_when_zeroing =
453 ctx.plan.adjust_mark_positioning_when_zeroing && ctx.buffer.direction.is_forward();
454
455 GPOS::position_start(ctx.face, ctx.buffer);
458
459 if ctx.plan.zero_marks
460 && ctx.plan.shaper.zero_width_marks == HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY
461 {
462 zero_mark_widths_by_gdef(ctx.buffer, adjust_offsets_when_zeroing);
463 }
464
465 position_by_plan(ctx.plan, ctx.face, ctx.buffer);
466
467 if ctx.plan.zero_marks
468 && ctx.plan.shaper.zero_width_marks == HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
469 {
470 zero_mark_widths_by_gdef(ctx.buffer, adjust_offsets_when_zeroing);
471 }
472
473 GPOS::position_finish_advances(ctx.face, ctx.buffer);
475 zero_width_default_ignorables(ctx.buffer);
476
477 if ctx.plan.apply_morx {
478 aat_layout::hb_aat_layout_zero_width_deleted_glyphs(ctx.buffer);
479 }
480
481 GPOS::position_finish_offsets(ctx.face, ctx.buffer);
482
483 if ctx.plan.fallback_mark_positioning {
484 ot_shape_fallback::position_marks(
485 ctx.plan,
486 ctx.face,
487 ctx.buffer,
488 adjust_offsets_when_zeroing,
489 );
490 }
491}
492
493fn position_by_plan(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) {
494 if plan.apply_gpos {
495 super::ot_layout_gpos_table::position(plan, face, buffer);
496 } else if plan.apply_kerx {
497 aat_layout::hb_aat_layout_position(plan, face, buffer);
498 }
499 if plan.apply_kern {
500 super::kerning::kern(plan, face, buffer);
501 } else if plan.apply_fallback_kern {
502 ot_shape_fallback::_hb_ot_shape_fallback_kern(plan, face, buffer);
503 }
504
505 if plan.apply_trak {
506 aat_layout::hb_aat_layout_track(plan, face, buffer);
507 }
508}
509
510fn initialize_masks(ctx: &mut ShapeContext) {
511 let global_mask = ctx.plan.ot_map.get_global_mask();
512 ctx.buffer.reset_masks(global_mask);
513}
514
515fn setup_masks(ctx: &mut ShapeContext) {
516 setup_masks_fraction(ctx);
517
518 if let Some(func) = ctx.plan.shaper.setup_masks {
519 func(ctx.plan, ctx.face, ctx.buffer);
520 }
521
522 for feature in &ctx.plan.user_features {
523 if !feature.is_global() {
524 let (mask, shift) = ctx.plan.ot_map.get_mask(feature.tag);
525 ctx.buffer
526 .set_masks(feature.value << shift, mask, feature.start, feature.end);
527 }
528 }
529}
530
531fn setup_masks_fraction(ctx: &mut ShapeContext) {
532 let buffer = &mut ctx.buffer;
533 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII == 0 || !ctx.plan.has_frac {
534 return;
535 }
536
537 let (pre_mask, post_mask) = if buffer.direction.is_forward() {
538 (
539 ctx.plan.numr_mask | ctx.plan.frac_mask,
540 ctx.plan.frac_mask | ctx.plan.dnom_mask,
541 )
542 } else {
543 (
544 ctx.plan.frac_mask | ctx.plan.dnom_mask,
545 ctx.plan.numr_mask | ctx.plan.frac_mask,
546 )
547 };
548
549 let len = buffer.len;
550 let mut i = 0;
551 while i < len {
552 if buffer.info[i].glyph_id == 0x2044 {
554 let mut start = i;
555 while start > 0
556 && _hb_glyph_info_get_general_category(&buffer.info[start - 1])
557 == hb_unicode_general_category_t::DecimalNumber
558 {
559 start -= 1;
560 }
561
562 let mut end = i + 1;
563 while end < len
564 && _hb_glyph_info_get_general_category(&buffer.info[end])
565 == hb_unicode_general_category_t::DecimalNumber
566 {
567 end += 1;
568 }
569
570 buffer.unsafe_to_break(Some(start), Some(end));
571
572 for info in &mut buffer.info[start..i] {
573 info.mask |= pre_mask;
574 }
575
576 buffer.info[i].mask |= ctx.plan.frac_mask;
577
578 for info in &mut buffer.info[i + 1..end] {
579 info.mask |= post_mask;
580 }
581
582 i = end;
583 } else {
584 i += 1;
585 }
586 }
587}
588
589fn set_unicode_props(buffer: &mut hb_buffer_t) {
590 let len = buffer.len;
599
600 let mut i = 0;
601 while i < len {
602 let (prior, later) = buffer.info.split_at_mut(i);
606 let info = &mut later[0];
607 info.init_unicode_props(&mut buffer.scratch_flags);
608
609 if _hb_glyph_info_get_general_category(info)
612 == hb_unicode_general_category_t::ModifierSymbol
613 && matches!(info.glyph_id, 0x1F3FB..=0x1F3FF)
614 {
615 _hb_glyph_info_set_continuation(info);
616 } else if i != 0 && matches!(info.glyph_id, 0x1F1E6..=0x1F1FF) {
617 let prev = prior.last().unwrap();
620 if matches!(prev.glyph_id, 0x1F1E6..=0x1F1FF) && !_hb_glyph_info_is_continuation(prev) {
621 _hb_glyph_info_set_continuation(info);
622 }
623 } else if _hb_glyph_info_is_zwj(info) {
624 _hb_glyph_info_set_continuation(info);
625 if let Some(next) = buffer.info[..len].get_mut(i + 1) {
626 if next.as_char().is_emoji_extended_pictographic() {
627 next.init_unicode_props(&mut buffer.scratch_flags);
628 _hb_glyph_info_set_continuation(next);
629 i += 1;
630 }
631 }
632 } else if matches!(info.glyph_id, 0xE0020..=0xE007F) {
633 _hb_glyph_info_set_continuation(info);
645 }
646
647 i += 1;
648 }
649}
650
651fn insert_dotted_circle(buffer: &mut hb_buffer_t, face: &hb_font_t) {
652 if !buffer
653 .flags
654 .contains(BufferFlags::DO_NOT_INSERT_DOTTED_CIRCLE)
655 && buffer.flags.contains(BufferFlags::BEGINNING_OF_TEXT)
656 && buffer.context_len[0] == 0
657 && _hb_glyph_info_is_unicode_mark(&buffer.info[0])
658 && face.has_glyph(0x25CC)
659 {
660 let mut info = hb_glyph_info_t {
661 glyph_id: 0x25CC,
662 mask: buffer.cur(0).mask,
663 cluster: buffer.cur(0).cluster,
664 ..hb_glyph_info_t::default()
665 };
666
667 info.init_unicode_props(&mut buffer.scratch_flags);
668 buffer.clear_output();
669 buffer.output_info(info);
670 buffer.sync();
671 }
672}
673
674fn form_clusters(buffer: &mut hb_buffer_t) {
675 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII != 0 {
676 if buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES {
677 foreach_grapheme!(buffer, start, end, { buffer.merge_clusters(start, end) });
678 } else {
679 foreach_grapheme!(buffer, start, end, {
680 buffer.unsafe_to_break(Some(start), Some(end));
681 });
682 }
683 }
684}
685
686fn ensure_native_direction(buffer: &mut hb_buffer_t) {
687 let dir = buffer.direction;
688 let mut hor = buffer
689 .script
690 .and_then(Direction::from_script)
691 .unwrap_or_default();
692
693 if hor == Direction::RightToLeft && dir == Direction::LeftToRight {
711 let mut found_number = false;
712 let mut found_letter = false;
713 let mut found_ri = false;
714 for info in &buffer.info {
715 let gc = _hb_glyph_info_get_general_category(info);
716 if gc == hb_unicode_general_category_t::DecimalNumber {
717 found_number = true;
718 } else if gc.is_letter() {
719 found_letter = true;
720 break;
721 } else if matches!(info.glyph_id, 0x1F1E6..=0x1F1FF) {
722 found_ri = true;
723 }
724 }
725 if (found_number || found_ri) && !found_letter {
726 hor = Direction::LeftToRight;
727 }
728 }
729
730 if (dir.is_horizontal() && dir != hor && hor != Direction::Invalid)
735 || (dir.is_vertical() && dir != Direction::TopToBottom)
736 {
737 _hb_ot_layout_reverse_graphemes(buffer);
738 buffer.direction = buffer.direction.reverse();
739 }
740}
741
742fn rotate_chars(ctx: &mut ShapeContext) {
743 let len = ctx.buffer.len;
744
745 if ctx.target_direction.is_backward() {
746 let rtlm_mask = ctx.plan.rtlm_mask;
747
748 for info in &mut ctx.buffer.info[..len] {
749 if let Some(c) = info.as_char().mirrored().map(u32::from) {
750 if ctx.face.has_glyph(c) {
751 info.glyph_id = c;
752 continue;
753 }
754 }
755 info.mask |= rtlm_mask;
756 }
757 }
758
759 if ctx.target_direction.is_vertical() && !ctx.plan.has_vert {
760 for info in &mut ctx.buffer.info[..len] {
761 if let Some(c) = info.as_char().vertical().map(u32::from) {
762 if ctx.face.has_glyph(c) {
763 info.glyph_id = c;
764 }
765 }
766 }
767 }
768}
769
770fn map_glyphs_fast(buffer: &mut hb_buffer_t) {
771 let len = buffer.len;
773 for info in &mut buffer.info[..len] {
774 info.glyph_id = info.glyph_index();
775 }
776}
777
778fn hb_synthesize_glyph_classes(buffer: &mut hb_buffer_t) {
779 let len = buffer.len;
780 for info in &mut buffer.info[..len] {
781 let class = if _hb_glyph_info_get_general_category(info)
790 != hb_unicode_general_category_t::NonspacingMark
791 || _hb_glyph_info_is_default_ignorable(info)
792 {
793 GlyphPropsFlags::BASE_GLYPH
794 } else {
795 GlyphPropsFlags::MARK
796 };
797
798 info.set_glyph_props(class.bits());
799 }
800}
801
802fn zero_width_default_ignorables(buffer: &mut hb_buffer_t) {
803 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES != 0
804 && !buffer
805 .flags
806 .contains(BufferFlags::PRESERVE_DEFAULT_IGNORABLES)
807 && !buffer
808 .flags
809 .contains(BufferFlags::REMOVE_DEFAULT_IGNORABLES)
810 {
811 let len = buffer.len;
812 for (info, pos) in buffer.info[..len].iter().zip(&mut buffer.pos[..len]) {
813 if _hb_glyph_info_is_default_ignorable(info) {
814 pos.x_advance = 0;
815 pos.y_advance = 0;
816 pos.x_offset = 0;
817 pos.y_offset = 0;
818 }
819 }
820 }
821}
822
823fn zero_mark_widths_by_gdef(buffer: &mut hb_buffer_t, adjust_offsets: bool) {
824 let len = buffer.len;
825 for (info, pos) in buffer.info[..len].iter().zip(&mut buffer.pos[..len]) {
826 if _hb_glyph_info_is_mark(info) {
827 if adjust_offsets {
828 pos.x_offset -= pos.x_advance;
829 pos.y_offset -= pos.y_advance;
830 }
831
832 pos.x_advance = 0;
833 pos.y_advance = 0;
834 }
835 }
836}
837
838fn hide_default_ignorables(buffer: &mut hb_buffer_t, face: &hb_font_t) {
839 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES != 0
840 && !buffer
841 .flags
842 .contains(BufferFlags::PRESERVE_DEFAULT_IGNORABLES)
843 {
844 if !buffer
845 .flags
846 .contains(BufferFlags::REMOVE_DEFAULT_IGNORABLES)
847 {
848 if let Some(invisible) = buffer
849 .invisible
850 .or_else(|| face.get_nominal_glyph(u32::from(' ')))
851 {
852 let len = buffer.len;
853 for info in &mut buffer.info[..len] {
854 if _hb_glyph_info_is_default_ignorable(info) {
855 info.glyph_id = u32::from(invisible.0);
856 }
857 }
858 return;
859 }
860 }
861
862 buffer.delete_glyphs_inplace(_hb_glyph_info_is_default_ignorable);
863 }
864}
865
866fn propagate_flags(buffer: &mut hb_buffer_t) {
867 if buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS != 0 {
870 foreach_cluster!(buffer, start, end, {
871 let mut mask = 0;
872 for info in &buffer.info[start..end] {
873 mask |= info.mask * glyph_flag::DEFINED;
874 }
875
876 if mask != 0 {
877 for info in &mut buffer.info[start..end] {
878 info.mask |= mask;
879 }
880 }
881 });
882 }
883}