1use ttf_parser::GlyphId;
2
3use super::buffer::{hb_buffer_t, GlyphPosition};
4use super::face::GlyphExtents;
5use super::ot_layout::*;
6use super::ot_shape_plan::hb_ot_shape_plan_t;
7use super::unicode::*;
8use super::{hb_font_t, Direction};
9
10fn recategorize_combining_class(u: u32, mut class: u8) -> u8 {
11 use modified_combining_class as mcc;
12 use CanonicalCombiningClass as Class;
13
14 if class >= 200 {
15 return class;
16 }
17
18 if u & !0xFF == 0x0E00 {
20 if class == 0 {
21 match u {
22 0x0E31 | 0x0E34 | 0x0E35 | 0x0E36 | 0x0E37 | 0x0E47 | 0x0E4C | 0x0E4D | 0x0E4E => {
23 class = Class::AboveRight as u8
24 }
25
26 0x0EB1 | 0x0EB4 | 0x0EB5 | 0x0EB6 | 0x0EB7 | 0x0EBB | 0x0ECC | 0x0ECD => {
27 class = Class::Above as u8
28 }
29
30 0x0EBC => class = Class::Below as u8,
31
32 _ => {}
33 }
34 } else {
35 if u == 0x0E3A {
37 class = Class::BelowRight as u8;
38 }
39 }
40 }
41
42 match class {
43 mcc::CCC10 => Class::Below as u8, mcc::CCC11 => Class::Below as u8, mcc::CCC12 => Class::Below as u8, mcc::CCC13 => Class::Below as u8, mcc::CCC14 => Class::Below as u8, mcc::CCC15 => Class::Below as u8, mcc::CCC16 => Class::Below as u8, mcc::CCC17 => Class::Below as u8, mcc::CCC18 => Class::Below as u8, mcc::CCC20 => Class::Below as u8, mcc::CCC22 => Class::Below as u8, mcc::CCC23 => Class::AttachedAbove as u8, mcc::CCC24 => Class::AboveRight as u8, mcc::CCC25 => Class::AboveLeft as u8, mcc::CCC19 => Class::AboveLeft as u8, mcc::CCC26 => Class::Above as u8, mcc::CCC21 => class, mcc::CCC27 => Class::Above as u8, mcc::CCC28 => Class::Above as u8, mcc::CCC30 => Class::Above as u8, mcc::CCC31 => Class::Above as u8, mcc::CCC33 => Class::Above as u8, mcc::CCC34 => Class::Above as u8, mcc::CCC35 => Class::Above as u8, mcc::CCC36 => Class::Above as u8, mcc::CCC29 => Class::Below as u8, mcc::CCC32 => Class::Below as u8, mcc::CCC103 => Class::BelowRight as u8, mcc::CCC107 => Class::AboveRight as u8, mcc::CCC118 => Class::Below as u8, mcc::CCC122 => Class::Above as u8, mcc::CCC129 => Class::Below as u8, mcc::CCC130 => Class::Above as u8, mcc::CCC132 => Class::Below as u8, _ => class,
88 }
89}
90
91pub fn _hb_ot_shape_fallback_mark_position_recategorize_marks(
92 _: &hb_ot_shape_plan_t,
93 _: &hb_font_t,
94 buffer: &mut hb_buffer_t,
95) {
96 let len = buffer.len;
97 for info in &mut buffer.info[..len] {
98 if _hb_glyph_info_get_general_category(info)
99 == hb_unicode_general_category_t::NonspacingMark
100 {
101 let mut class = _hb_glyph_info_get_modified_combining_class(info);
102 class = recategorize_combining_class(info.glyph_id, class);
103 _hb_glyph_info_set_modified_combining_class(info, class);
104 }
105 }
106}
107
108fn zero_mark_advances(
109 buffer: &mut hb_buffer_t,
110 start: usize,
111 end: usize,
112 adjust_offsets_when_zeroing: bool,
113) {
114 for (info, pos) in buffer.info[start..end]
115 .iter()
116 .zip(&mut buffer.pos[start..end])
117 {
118 if _hb_glyph_info_get_general_category(info)
119 == hb_unicode_general_category_t::NonspacingMark
120 {
121 if adjust_offsets_when_zeroing {
122 pos.x_offset -= pos.x_advance;
123 pos.y_offset -= pos.y_advance;
124 }
125 pos.x_advance = 0;
126 pos.y_advance = 0;
127 }
128 }
129}
130
131fn position_mark(
132 _: &hb_ot_shape_plan_t,
133 face: &hb_font_t,
134 direction: Direction,
135 glyph: GlyphId,
136 pos: &mut GlyphPosition,
137 base_extents: &mut GlyphExtents,
138 combining_class: CanonicalCombiningClass,
139) {
140 use CanonicalCombiningClass as Class;
141
142 let mut mark_extents = GlyphExtents::default();
143 if !face.glyph_extents(glyph, &mut mark_extents) {
144 return;
145 };
146
147 let y_gap = face.units_per_em as i32 / 16;
148 pos.x_offset = 0;
149 pos.y_offset = 0;
150
151 match combining_class {
155 Class::DoubleBelow | Class::DoubleAbove if direction.is_horizontal() => {
156 pos.x_offset += base_extents.x_bearing
157 + if direction.is_forward() {
158 base_extents.width
159 } else {
160 0
161 }
162 - mark_extents.width / 2
163 - mark_extents.x_bearing;
164 }
165
166 Class::AttachedBelowLeft | Class::BelowLeft | Class::AboveLeft => {
167 pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing;
169 }
170
171 Class::AttachedAboveRight | Class::BelowRight | Class::AboveRight => {
172 pos.x_offset += base_extents.x_bearing + base_extents.width
174 - mark_extents.width
175 - mark_extents.x_bearing;
176 }
177
178 Class::AttachedBelow | Class::AttachedAbove | Class::Below | Class::Above | _ => {
179 pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2
181 - mark_extents.x_bearing;
182 }
183 }
184
185 let is_attached = matches!(
186 combining_class,
187 Class::AttachedBelowLeft
188 | Class::AttachedBelow
189 | Class::AttachedAbove
190 | Class::AttachedAboveRight
191 );
192
193 match combining_class {
195 Class::DoubleBelow
196 | Class::BelowLeft
197 | Class::Below
198 | Class::BelowRight
199 | Class::AttachedBelowLeft
200 | Class::AttachedBelow => {
201 if !is_attached {
202 base_extents.height -= y_gap;
204 }
205
206 pos.y_offset = base_extents.y_bearing + base_extents.height - mark_extents.y_bearing;
207
208 if (y_gap > 0) == (pos.y_offset > 0) {
210 base_extents.height -= pos.y_offset;
211 pos.y_offset = 0;
212 }
213
214 base_extents.height += mark_extents.height;
215 }
216
217 Class::DoubleAbove
218 | Class::AboveLeft
219 | Class::Above
220 | Class::AboveRight
221 | Class::AttachedAbove
222 | Class::AttachedAboveRight => {
223 if !is_attached {
224 base_extents.y_bearing += y_gap;
226 base_extents.height -= y_gap;
227 }
228
229 pos.y_offset = base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height);
230
231 if (y_gap > 0) != (pos.y_offset > 0) {
233 let correction = -pos.y_offset / 2;
234 base_extents.y_bearing += correction;
235 base_extents.height -= correction;
236 pos.y_offset += correction;
237 }
238
239 base_extents.y_bearing -= mark_extents.height;
240 base_extents.height += mark_extents.height;
241 }
242
243 _ => {}
244 }
245}
246
247fn position_around_base(
248 plan: &hb_ot_shape_plan_t,
249 face: &hb_font_t,
250 buffer: &mut hb_buffer_t,
251 base: usize,
252 end: usize,
253 adjust_offsets_when_zeroing: bool,
254) {
255 let mut horizontal_dir = Direction::Invalid;
256 buffer.unsafe_to_break(Some(base), Some(end));
257
258 let base_info = &buffer.info[base];
259 let base_pos = &buffer.pos[base];
260 let base_glyph = base_info.as_glyph();
261
262 let mut base_extents = GlyphExtents::default();
263 if !face.glyph_extents(base_glyph, &mut base_extents) {
264 zero_mark_advances(buffer, base + 1, end, adjust_offsets_when_zeroing);
265 return;
266 };
267
268 base_extents.y_bearing += base_pos.y_offset;
269 base_extents.x_bearing = 0;
270
271 base_extents.width = face.glyph_h_advance(base_glyph) as i32;
275
276 let lig_id = _hb_glyph_info_get_lig_id(base_info) as u32;
277 let num_lig_components = _hb_glyph_info_get_lig_num_comps(base_info) as i32;
278
279 let mut x_offset = 0;
280 let mut y_offset = 0;
281 if buffer.direction.is_forward() {
282 x_offset -= base_pos.x_advance;
283 y_offset -= base_pos.y_advance;
284 }
285
286 let mut last_lig_component: i32 = -1;
287 let mut last_combining_class: u8 = 255;
288 let mut component_extents = base_extents;
289 let mut cluster_extents = base_extents;
290
291 for (info, pos) in buffer.info[base + 1..end]
292 .iter()
293 .zip(&mut buffer.pos[base + 1..end])
294 {
295 if _hb_glyph_info_get_modified_combining_class(info) != 0 {
296 if num_lig_components > 1 {
297 let this_lig_id = _hb_glyph_info_get_lig_id(info) as u32;
298 let mut this_lig_component = _hb_glyph_info_get_lig_comp(info) as i32 - 1;
299
300 if lig_id == 0 || lig_id != this_lig_id || this_lig_component >= num_lig_components
302 {
303 this_lig_component = num_lig_components - 1;
304 }
305
306 if last_lig_component != this_lig_component {
307 last_lig_component = this_lig_component;
308 last_combining_class = 255;
309 component_extents = base_extents;
310
311 if horizontal_dir == Direction::Invalid {
312 horizontal_dir = if plan.direction.is_horizontal() {
313 plan.direction
314 } else {
315 plan.script
316 .and_then(Direction::from_script)
317 .unwrap_or(Direction::LeftToRight)
318 };
319 }
320
321 component_extents.x_bearing += (if horizontal_dir == Direction::LeftToRight {
322 this_lig_component
323 } else {
324 num_lig_components - 1 - this_lig_component
325 } * component_extents.width)
326 / num_lig_components;
327
328 component_extents.width /= num_lig_components;
329 }
330 }
331
332 let this_combining_class = _hb_glyph_info_get_modified_combining_class(info);
333 if last_combining_class != this_combining_class {
334 last_combining_class = this_combining_class;
335 cluster_extents = component_extents;
336 }
337
338 position_mark(
339 plan,
340 face,
341 buffer.direction,
342 info.as_glyph(),
343 pos,
344 &mut cluster_extents,
345 conv_combining_class(this_combining_class),
346 );
347
348 pos.x_advance = 0;
349 pos.y_advance = 0;
350 pos.x_offset += x_offset;
351 pos.y_offset += y_offset;
352 } else {
353 if buffer.direction.is_forward() {
354 x_offset -= pos.x_advance;
355 y_offset -= pos.y_advance;
356 } else {
357 x_offset += pos.x_advance;
358 y_offset += pos.y_advance;
359 }
360 }
361 }
362}
363
364fn position_cluster(
365 plan: &hb_ot_shape_plan_t,
366 face: &hb_font_t,
367 buffer: &mut hb_buffer_t,
368 start: usize,
369 end: usize,
370 adjust_offsets_when_zeroing: bool,
371) {
372 if end - start < 2 {
373 return;
374 }
375
376 let mut i = start;
378 while i < end {
379 if !_hb_glyph_info_is_unicode_mark(&buffer.info[i]) {
380 let mut j = i + 1;
382 while j < end && _hb_glyph_info_is_unicode_mark(&buffer.info[j]) {
383 j += 1;
384 }
385
386 position_around_base(plan, face, buffer, i, j, adjust_offsets_when_zeroing);
387 i = j - 1;
388 }
389 i += 1;
390 }
391}
392
393pub fn position_marks(
394 plan: &hb_ot_shape_plan_t,
395 face: &hb_font_t,
396 buffer: &mut hb_buffer_t,
397 adjust_offsets_when_zeroing: bool,
398) {
399 let mut start = 0;
400 let len = buffer.len;
401 for i in 1..len {
402 if !_hb_glyph_info_is_unicode_mark(&buffer.info[i]) {
403 position_cluster(plan, face, buffer, start, i, adjust_offsets_when_zeroing);
404 start = i;
405 }
406 }
407
408 position_cluster(plan, face, buffer, start, len, adjust_offsets_when_zeroing);
409}
410
411pub fn _hb_ot_shape_fallback_kern(_: &hb_ot_shape_plan_t, _: &hb_font_t, _: &mut hb_buffer_t) {
412 }
414
415pub fn _hb_ot_shape_fallback_spaces(
416 _: &hb_ot_shape_plan_t,
417 face: &hb_font_t,
418 buffer: &mut hb_buffer_t,
419) {
420 use super::unicode::hb_unicode_funcs_t as t;
421
422 let len = buffer.len;
423 let horizontal = buffer.direction.is_horizontal();
424 for (info, pos) in buffer.info[..len].iter().zip(&mut buffer.pos[..len]) {
425 if _hb_glyph_info_is_unicode_space(&info) && !_hb_glyph_info_ligated(info) {
426 let space_type = _hb_glyph_info_get_unicode_space_fallback_type(info);
427 match space_type {
428 t::SPACE_EM
429 | t::SPACE_EM_2
430 | t::SPACE_EM_3
431 | t::SPACE_EM_4
432 | t::SPACE_EM_5
433 | t::SPACE_EM_6
434 | t::SPACE_EM_16 => {
435 let length =
436 (face.units_per_em as i32 + (space_type as i32) / 2) / space_type as i32;
437 if horizontal {
438 pos.x_advance = length;
439 } else {
440 pos.y_advance = -length;
441 }
442 }
443
444 t::SPACE_4_EM_18 => {
445 let length = ((face.units_per_em as i64) * 4 / 18) as i32;
446 if horizontal {
447 pos.x_advance = length
448 } else {
449 pos.y_advance = -length;
450 }
451 }
452
453 t::SPACE_FIGURE => {
454 for u in '0'..='9' {
455 if let Some(glyph) = face.get_nominal_glyph(u as u32) {
456 if horizontal {
457 pos.x_advance = face.glyph_h_advance(glyph) as i32;
458 } else {
459 pos.y_advance = face.glyph_v_advance(glyph);
460 }
461 break;
462 }
463 }
464 }
465
466 t::SPACE_PUNCTUATION => {
467 let punct = face
468 .get_nominal_glyph('.' as u32)
469 .or_else(|| face.get_nominal_glyph(',' as u32));
470
471 if let Some(glyph) = punct {
472 if horizontal {
473 pos.x_advance = face.glyph_h_advance(glyph) as i32;
474 } else {
475 pos.y_advance = face.glyph_v_advance(glyph);
476 }
477 }
478 }
479
480 t::SPACE_NARROW => {
481 if horizontal {
487 pos.x_advance /= 2;
488 } else {
489 pos.y_advance /= 2;
490 }
491 }
492
493 _ => {}
494 }
495 }
496 }
497}
498
499fn conv_combining_class(n: u8) -> CanonicalCombiningClass {
501 use CanonicalCombiningClass as Class;
502 match n {
503 1 => Class::Overlay,
504 6 => Class::HanReading,
505 7 => Class::Nukta,
506 8 => Class::KanaVoicing,
507 9 => Class::Virama,
508 10 => Class::CCC10,
509 11 => Class::CCC11,
510 12 => Class::CCC12,
511 13 => Class::CCC13,
512 14 => Class::CCC14,
513 15 => Class::CCC15,
514 16 => Class::CCC16,
515 17 => Class::CCC17,
516 18 => Class::CCC18,
517 19 => Class::CCC19,
518 20 => Class::CCC20,
519 21 => Class::CCC21,
520 22 => Class::CCC22,
521 23 => Class::CCC23,
522 24 => Class::CCC24,
523 25 => Class::CCC25,
524 26 => Class::CCC26,
525 27 => Class::CCC27,
526 28 => Class::CCC28,
527 29 => Class::CCC29,
528 30 => Class::CCC30,
529 31 => Class::CCC31,
530 32 => Class::CCC32,
531 33 => Class::CCC33,
532 34 => Class::CCC34,
533 35 => Class::CCC35,
534 36 => Class::CCC36,
535 84 => Class::CCC84,
536 91 => Class::CCC91,
537 103 => Class::CCC103,
538 107 => Class::CCC107,
539 118 => Class::CCC118,
540 122 => Class::CCC122,
541 129 => Class::CCC129,
542 130 => Class::CCC130,
543 132 => Class::CCC132,
544 200 => Class::AttachedBelowLeft,
545 202 => Class::AttachedBelow,
546 214 => Class::AttachedAbove,
547 216 => Class::AttachedAboveRight,
548 218 => Class::BelowLeft,
549 220 => Class::Below,
550 222 => Class::BelowRight,
551 224 => Class::Left,
552 226 => Class::Right,
553 228 => Class::AboveLeft,
554 230 => Class::Above,
555 232 => Class::AboveRight,
556 233 => Class::DoubleBelow,
557 234 => Class::DoubleAbove,
558 240 => Class::IotaSubscript,
559 _ => Class::NotReordered,
560 }
561}