1use crate::hb::unicode::Codepoint;
2
3use super::buffer::*;
4use super::font_funcs::FontFuncsDispatch;
5use super::hb_font_t;
6use super::ot_shape_plan::hb_ot_shape_plan_t;
7use super::ot_shaper::{ComposeFn, DecomposeFn, MAX_COMBINING_MARKS};
8use super::unicode::{hb_unicode_funcs_t, CharExt};
9use read_fonts::types::GlyphId;
10
11impl GlyphInfo {
12 declare_buffer_var!(
13 u32,
14 1,
15 0,
16 NORMALIZER_GLYPH_INDEX_VAR,
17 normalizer_glyph_index,
18 set_normalizer_glyph_index
19 );
20}
21
22pub struct hb_ot_shape_normalize_context_t<'a, 'x, 'u> {
23 pub plan: &'a hb_ot_shape_plan_t,
24 pub buffer: &'x mut hb_buffer_t,
25 pub font_funcs: &'x mut FontFuncsDispatch<'a, 'u>,
26 pub decompose: DecomposeFn,
27 pub compose: ComposeFn,
28}
29
30impl hb_ot_shape_normalize_context_t<'_, '_, '_> {
31 fn nominal_glyph(&mut self, codepoint: u32) -> Option<GlyphId> {
32 self.font_funcs.nominal_glyph(codepoint)
33 }
34
35 fn variant_glyph(&mut self, codepoint: u32, selector: u32) -> Option<GlyphId> {
36 self.font_funcs.variant_glyph(codepoint, selector)
37 }
38
39 fn set_current_glyph(&mut self) {
40 let info = self.buffer.cur_mut(0);
41 if let Some(glyph_id) = self.font_funcs.nominal_glyph(info.glyph_id) {
42 info.set_normalizer_glyph_index(u32::from(glyph_id));
43 }
44 }
45}
46
47pub type hb_ot_shape_normalization_mode_t = i32;
48pub const HB_OT_SHAPE_NORMALIZATION_MODE_NONE: i32 = 0;
49pub const HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED: i32 = 1;
50pub const HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS: i32 = 2; pub const HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT: i32 = 3; pub const HB_OT_SHAPE_NORMALIZATION_MODE_AUTO: i32 = 4; #[allow(dead_code)]
54pub const HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT: i32 = HB_OT_SHAPE_NORMALIZATION_MODE_AUTO;
55
56fn decompose_unicode(
96 _: &hb_ot_shape_normalize_context_t,
97 ab: Codepoint,
98) -> Option<(Codepoint, Codepoint)> {
99 super::unicode::decompose(ab)
100}
101
102fn compose_unicode(
103 _: &hb_ot_shape_normalize_context_t,
104 a: Codepoint,
105 b: Codepoint,
106) -> Option<Codepoint> {
107 super::unicode::compose(a, b)
108}
109
110fn output_char(buffer: &mut hb_buffer_t, unichar: u32, glyph: u32) {
111 buffer.cur_mut(0).set_normalizer_glyph_index(glyph);
113 buffer.output_glyph(unichar);
114 let mut flags = buffer.scratch_flags;
116 buffer.prev_mut().init_unicode_props(&mut flags);
117 buffer.scratch_flags = flags;
118}
119
120fn next_char(buffer: &mut hb_buffer_t, glyph: u32) {
121 buffer.cur_mut(0).set_normalizer_glyph_index(glyph);
122 buffer.next_glyph();
123}
124
125fn skip_char(buffer: &mut hb_buffer_t) {
126 buffer.skip_glyph();
127}
128
129fn decompose(ctx: &mut hb_ot_shape_normalize_context_t, shortest: bool, ab: Codepoint) -> u32 {
131 let Some((a, b)) = (ctx.decompose)(ctx, ab) else {
132 return 0;
133 };
134
135 let a_glyph = ctx.nominal_glyph(a);
136 let b_glyph = if b != 0 {
137 match ctx.nominal_glyph(b) {
138 Some(glyph_id) => Some(glyph_id),
139 None => return 0,
140 }
141 } else {
142 None
143 };
144
145 if let Some(a_glyph) = a_glyph {
146 if shortest {
147 output_char(ctx.buffer, a, u32::from(a_glyph));
149 if let Some(b_glyph) = b_glyph {
150 output_char(ctx.buffer, b, u32::from(b_glyph));
151 return 2;
152 }
153 return 1;
154 }
155 }
156
157 let ret = decompose(ctx, shortest, a);
158 if ret != 0 {
159 if let Some(b_glyph) = b_glyph {
160 output_char(ctx.buffer, b, u32::from(b_glyph));
161 return ret + 1;
162 }
163 return ret;
164 }
165
166 if let Some(a_glyph) = a_glyph {
167 output_char(ctx.buffer, a, u32::from(a_glyph));
168 if let Some(b_glyph) = b_glyph {
169 output_char(ctx.buffer, b, u32::from(b_glyph));
170 return 2;
171 }
172 return 1;
173 }
174
175 0
176}
177
178fn decompose_current_character(ctx: &mut hb_ot_shape_normalize_context_t, shortest: bool) {
179 let u = ctx.buffer.cur(0).as_codepoint();
180 let glyph = ctx.nominal_glyph(u);
181
182 if let Some(glyph) = glyph {
183 if shortest {
184 next_char(ctx.buffer, u32::from(glyph));
185 return;
186 }
187 }
188
189 if decompose(ctx, shortest, u) > 0 {
190 skip_char(ctx.buffer);
191 return;
192 }
193
194 if let Some(glyph) = glyph {
195 next_char(ctx.buffer, u32::from(glyph));
196 return;
197 }
198
199 if ctx.buffer.cur(0).is_unicode_space() {
200 let space_type = u.space_fallback();
201 if space_type != hb_unicode_funcs_t::NOT_SPACE {
202 let space_glyph = ctx.nominal_glyph(0x0020).or(ctx.buffer.invisible);
203
204 if let Some(space_glyph) = space_glyph {
205 ctx.buffer
206 .cur_mut(0)
207 .set_unicode_space_fallback_type(space_type);
208 next_char(ctx.buffer, u32::from(space_glyph));
209 ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK;
210 return;
211 }
212 }
213 }
214
215 if u == 0x2011 {
218 if let Some(other_glyph) = ctx.nominal_glyph(0x2010) {
219 next_char(ctx.buffer, u32::from(other_glyph));
220 return;
221 }
222 }
223
224 next_char(ctx.buffer, 0);
226}
227
228fn handle_variation_selector_cluster(
229 ctx: &mut hb_ot_shape_normalize_context_t,
230 end: usize,
231 _: bool,
232) {
233 while ctx.buffer.idx < end - 1 && ctx.buffer.successful {
235 if ctx.buffer.cur(1).as_codepoint().is_variation_selector() {
236 let base = ctx.buffer.cur(0).as_codepoint();
237 let selector = ctx.buffer.cur(1).as_codepoint();
238 if let Some(glyph_id) = ctx.variant_glyph(base, selector) {
239 ctx.buffer
240 .cur_mut(0)
241 .set_normalizer_glyph_index(u32::from(glyph_id));
242 let unicode = ctx.buffer.cur(0).glyph_id;
243 ctx.buffer.replace_glyphs(2, 1, &[unicode]);
244 } else {
245 ctx.set_current_glyph();
247 ctx.buffer.next_glyph();
248
249 ctx.buffer.scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK;
250
251 ctx.buffer.cur_mut(0).set_variation_selector(true);
252
253 if ctx.buffer.not_found_variation_selector.is_some() {
254 ctx.buffer.cur_mut(0).clear_default_ignorable();
255 }
256
257 ctx.set_current_glyph();
258 ctx.buffer.next_glyph();
259 }
260
261 while ctx.buffer.idx < end && ctx.buffer.cur(0).as_codepoint().is_variation_selector() {
263 ctx.set_current_glyph();
264 ctx.buffer.next_glyph();
265 }
266 } else {
267 ctx.set_current_glyph();
268 ctx.buffer.next_glyph();
269 }
270 }
271
272 if ctx.buffer.idx < end {
273 ctx.set_current_glyph();
274 ctx.buffer.next_glyph();
275 }
276}
277
278fn decompose_multi_char_cluster(
279 ctx: &mut hb_ot_shape_normalize_context_t,
280 end: usize,
281 short_circuit: bool,
282) {
283 let mut i = ctx.buffer.idx;
284 while i < end && ctx.buffer.successful {
285 if ctx.buffer.info[i].as_codepoint().is_variation_selector() {
286 handle_variation_selector_cluster(ctx, end, short_circuit);
287 return;
288 }
289 i += 1;
290 }
291
292 while ctx.buffer.idx < end && ctx.buffer.successful {
293 decompose_current_character(ctx, short_circuit);
294 }
295}
296
297fn compare_combining_class(pa: &GlyphInfo, pb: &GlyphInfo) -> bool {
298 let a = pa.modified_combining_class();
299 let b = pb.modified_combining_class();
300 a > b
301}
302
303pub fn _hb_ot_shape_normalize<'a, 'x>(
304 plan: &'a hb_ot_shape_plan_t,
305 buffer: &'x mut hb_buffer_t,
306 _face: &'a hb_font_t<'a>,
307 font_funcs: &'x mut FontFuncsDispatch<'a, '_>,
308) {
309 if buffer.is_empty() {
310 return;
311 }
312
313 buffer.assert_unicode_vars();
314
315 let mut mode = plan.shaper.normalization_preference;
316 if mode == HB_OT_SHAPE_NORMALIZATION_MODE_AUTO {
317 if plan.has_gpos_mark {
318 mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
321 } else {
322 mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
323 }
324 }
325
326 let mut ctx = hb_ot_shape_normalize_context_t {
327 plan,
328 buffer,
329 font_funcs,
330 decompose: plan.shaper.decompose.unwrap_or(decompose_unicode),
331 compose: plan.shaper.compose.unwrap_or(compose_unicode),
332 };
333
334 let always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE;
335 let might_short_circuit = always_short_circuit
336 || (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED
337 && mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT);
338
339 let mut all_simple = true;
347 {
348 ctx.buffer.clear_output();
349 let count = ctx.buffer.len;
350 ctx.buffer.idx = 0;
351 loop {
352 let mut end = ctx.buffer.idx + 1;
353 while end < count && !ctx.buffer.info[end].is_unicode_mark() {
354 end += 1;
355 }
356
357 if end < count {
358 end -= 1;
360 }
361
362 if might_short_circuit {
364 let len = end - ctx.buffer.idx;
365 let mut done = 0;
366 while done < len {
367 let codepoint = ctx.buffer.cur(done).glyph_id;
368 let glyph_id = match ctx.nominal_glyph(codepoint) {
369 Some(glyph_id) => u32::from(glyph_id),
370 None => break,
371 };
372 let cur = ctx.buffer.cur_mut(done);
373 cur.set_normalizer_glyph_index(glyph_id);
374 done += 1;
375 }
376 ctx.buffer.next_glyphs(done);
377 }
378
379 while ctx.buffer.idx < end && ctx.buffer.successful {
380 decompose_current_character(&mut ctx, might_short_circuit);
381 }
382
383 if ctx.buffer.idx == count || !ctx.buffer.successful {
384 break;
385 }
386
387 all_simple = false;
388
389 end = ctx.buffer.idx + 1;
391 while end < count && ctx.buffer.info[end].is_unicode_mark() {
392 end += 1;
393 }
394
395 decompose_multi_char_cluster(&mut ctx, end, always_short_circuit);
397
398 if ctx.buffer.idx >= count || !ctx.buffer.successful {
399 break;
400 }
401 }
402
403 ctx.buffer.sync();
404 }
405
406 if !all_simple {
408 let count = ctx.buffer.len;
409 let mut i = 0;
410 while i < count {
411 if ctx.buffer.info[i].modified_combining_class() == 0 {
412 i += 1;
413 continue;
414 }
415
416 let mut end = i + 1;
417 while end < count && ctx.buffer.info[end].modified_combining_class() != 0 {
418 end += 1;
419 }
420
421 if end - i <= MAX_COMBINING_MARKS {
423 ctx.buffer.sort(i, end, compare_combining_class);
424
425 if let Some(reorder_marks) = ctx.plan.shaper.reorder_marks {
426 reorder_marks(ctx.plan, ctx.buffer, i, end);
427 }
428 }
429
430 i = end + 1;
431 }
432 }
433 if ctx.buffer.scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ != 0 {
434 for i in 1..ctx.buffer.len.saturating_sub(1) {
438 if ctx.buffer.info[i].glyph_id == 0x034F
439 {
441 let last = ctx.buffer.info[i - 1].modified_combining_class();
442 let next = ctx.buffer.info[i + 1].modified_combining_class();
443 if next == 0 || last <= next {
444 ctx.buffer.info[i].unhide();
445 }
446 }
447 }
448 }
449
450 if !all_simple
452 && ctx.buffer.successful
453 && (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
454 || mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT)
455 {
456 let count = ctx.buffer.len;
460 let mut starter = 0;
461 ctx.buffer.clear_output();
462 ctx.buffer.next_glyph();
463 while ctx.buffer.idx < count && ctx.buffer.successful {
464 let cur = ctx.buffer.cur(0);
469 if cur.is_unicode_mark() &&
470 (starter == ctx.buffer.out_len - 1
473 || ctx.buffer.prev().modified_combining_class() < cur.modified_combining_class())
474 {
475 let a = ctx.buffer.out_info()[starter].as_codepoint();
476 let b = cur.as_codepoint();
477 if let Some(composed) = (ctx.compose)(&ctx, a, b) {
478 if let Some(glyph_id) = ctx.nominal_glyph(composed) {
479 ctx.buffer.next_glyph();
481 if !ctx.buffer.successful {
482 return;
483 }
484
485 ctx.buffer.merge_out_clusters(starter, ctx.buffer.out_len);
487 ctx.buffer.out_len -= 1;
488
489 let mut flags = ctx.buffer.scratch_flags;
491 let info = &mut ctx.buffer.out_info_mut()[starter];
492 info.glyph_id = composed;
493 info.set_normalizer_glyph_index(u32::from(glyph_id));
494 info.init_unicode_props(&mut flags);
495 ctx.buffer.scratch_flags = flags;
496
497 continue;
498 }
499 }
500 }
501
502 ctx.buffer.next_glyph();
504
505 if ctx.buffer.prev().modified_combining_class() == 0 {
506 starter = ctx.buffer.out_len - 1;
507 }
508 }
509
510 ctx.buffer.sync();
511 }
512}