1use super::super::{
8 metrics::{fixed_mul_div, pix_floor, pix_round, Scale, ScaledAxisMetrics, ScaledWidth},
9 style::ScriptGroup,
10 topo::{Axis, Edge},
11};
12
13pub(crate) fn hint_edges(
19 axis: &mut Axis,
20 metrics: &ScaledAxisMetrics,
21 group: ScriptGroup,
22 scale: &Scale,
23 mut top_to_bottom_hinting: bool,
24) {
25 if axis.dim != Axis::VERTICAL {
26 top_to_bottom_hinting = false;
27 }
28 let anchor_ix = align_edges_to_blues(axis, metrics, group, scale);
30 let (serif_count, anchor_ix) = align_stem_edges(
32 axis,
33 metrics,
34 group,
35 scale,
36 top_to_bottom_hinting,
37 anchor_ix,
38 );
39 let edges = axis.edges.as_mut_slice();
40 if axis.dim == Axis::HORIZONTAL && (edges.len() == 6 || edges.len() == 12) {
42 hint_lowercase_m(edges, group);
43 }
44 if serif_count > 0 || anchor_ix.is_none() {
46 align_remaining_edges(axis, group, top_to_bottom_hinting, serif_count, anchor_ix);
47 }
48}
49
50fn align_edges_to_blues(
54 axis: &mut Axis,
55 metrics: &ScaledAxisMetrics,
56 group: ScriptGroup,
57 scale: &Scale,
58) -> Option<usize> {
59 let mut anchor_ix = None;
60 if group == ScriptGroup::Default && axis.dim != Axis::VERTICAL {
62 return anchor_ix;
63 }
64 for edge_ix in 0..axis.edges.len() {
65 let edges = axis.edges.as_mut_slice();
66 let edge = &edges[edge_ix];
67 if edge.flags & Edge::DONE != 0 {
68 continue;
69 }
70 let edge2_ix = edge.link_ix.map(|x| x as usize);
71 let edge2 = edge2_ix.map(|ix| &edges[ix]);
72 if let (true, Some(edge2)) = (edge.blue_edge.is_some(), edge2) {
74 if edge2.blue_edge.is_some() {
75 let skip_ix = if edge2.flags & Edge::NEUTRAL != 0 {
76 edge2_ix
77 } else if edge.flags & Edge::NEUTRAL != 0 {
78 Some(edge_ix)
79 } else {
80 None
81 };
82 if let Some(skip_ix) = skip_ix {
83 let skip_edge = &mut edges[skip_ix];
84 skip_edge.blue_edge = None;
85 skip_edge.flags &= !Edge::NEUTRAL;
86 }
87 }
88 }
89 let blue = edges[edge_ix].blue_edge;
91 let (blue, edge1_ix, edge2_ix) = if let Some(blue) = blue {
92 (blue, Some(edge_ix), edge2_ix)
93 } else if let Some(edge2_blue) = edge2_ix.and_then(|ix| edges[ix].blue_edge) {
94 (edge2_blue, edge2_ix, Some(edge_ix))
95 } else {
96 (Default::default(), None, None)
97 };
98 let Some(edge1_ix) = edge1_ix else {
99 continue;
100 };
101 let edge1 = &mut edges[edge1_ix];
102 edge1.pos = blue.fitted;
103 edge1.flags |= Edge::DONE;
104 if let Some(edge2_ix) = edge2_ix {
105 let edge2 = &mut edges[edge2_ix];
106 if edge2.blue_edge.is_none() {
107 edge2.flags |= Edge::DONE;
108 align_linked_edge(axis, metrics, group, scale, edge1_ix, edge2_ix);
109 }
110 }
111 if anchor_ix.is_none() {
112 anchor_ix = Some(edge_ix);
113 }
114 }
115 anchor_ix
116}
117
118fn align_stem_edges(
122 axis: &mut Axis,
123 metrics: &ScaledAxisMetrics,
124 group: ScriptGroup,
125 scale: &Scale,
126 top_to_bottom_hinting: bool,
127 mut anchor_ix: Option<usize>,
128) -> (usize, Option<usize>) {
129 let mut serif_count = 0;
130 let mut last_stem_pos = None;
131 let mut delta = 0;
132 for edge_ix in 0..axis.edges.len() {
135 let edges = axis.edges.as_mut_slice();
136 let edge = &edges[edge_ix];
137 if edge.flags & Edge::DONE != 0 {
138 continue;
139 }
140 let Some(edge2_ix) = edge.link_ix.map(|ix| ix as usize) else {
142 serif_count += 1;
143 continue;
144 };
145 if group != ScriptGroup::Default {
148 if let Some(last_pos) = last_stem_pos {
149 if edge.pos < last_pos + 64 || edges[edge2_ix].pos < last_pos + 64 {
150 serif_count += 1;
151 continue;
152 }
153 }
154 }
155 if edges[edge2_ix].blue_edge.is_some() {
157 edges[edge2_ix].flags |= Edge::DONE;
158 align_linked_edge(axis, metrics, group, scale, edge2_ix, edge_ix);
159 continue;
160 }
161 if group == ScriptGroup::Default {
162 if let Some(anchor_ix) = anchor_ix {
166 let anchor = &edges[anchor_ix];
167 let edge = edges[edge_ix];
168 let edge2 = edges[edge2_ix];
169 let original_pos = anchor.pos + (edge.opos - anchor.opos);
170 let original_len = edge2.opos - edge.opos;
171 let original_center = original_pos + (original_len >> 1);
172 let cur_len = stem_width(
173 metrics,
174 group,
175 scale,
176 original_len,
177 0,
178 edge.flags,
179 edge2.flags,
180 );
181 if edge2.flags & Edge::DONE != 0 {
182 let new_pos = edge2.pos - cur_len;
183 edges[edge_ix].pos = new_pos;
184 } else if cur_len < 96 {
185 let cur_pos1 = pix_round(original_center);
186 let (u_off, d_off) = if cur_len <= 64 { (32, 32) } else { (38, 26) };
187 let delta1 = (original_center - (cur_pos1 - u_off)).abs();
188 let delta2 = (original_center - (cur_pos1 + d_off)).abs();
189 let cur_pos1 = if delta1 < delta2 {
190 cur_pos1 - u_off
191 } else {
192 cur_pos1 + d_off
193 };
194 edges[edge_ix].pos = cur_pos1 - cur_len / 2;
195 edges[edge2_ix].pos = cur_pos1 + cur_len / 2;
196 } else {
197 let cur_pos1 = pix_round(original_pos);
198 let delta1 = (cur_pos1 + (cur_len >> 1) - original_center).abs();
199 let cur_pos2 = pix_round(original_pos + original_len) - cur_len;
200 let delta2 = (cur_pos2 + (cur_len >> 1) - original_center).abs();
201 let new_pos = if delta1 < delta2 { cur_pos1 } else { cur_pos2 };
202 let new_pos2 = new_pos + cur_len;
203 edges[edge_ix].pos = new_pos;
204 edges[edge2_ix].pos = new_pos2;
205 }
206 edges[edge_ix].flags |= Edge::DONE;
207 edges[edge2_ix].flags |= Edge::DONE;
208 if edge_ix > 0 {
209 adjust_link(edges, edge_ix, LinkDir::Prev, top_to_bottom_hinting);
210 }
211 } else {
212 let edge = edges[edge_ix];
214 let edge2 = edges[edge2_ix];
215 let original_len = edge2.opos - edge.opos;
216 let cur_len = stem_width(
217 metrics,
218 group,
219 scale,
220 original_len,
221 0,
222 edge.flags,
223 edge2.flags,
224 );
225 let (u_off, d_off) = if cur_len <= 64 {
227 (32, 32)
229 } else {
230 (38, 26)
232 };
233 if cur_len < 96 {
234 let original_center = edge.opos + (original_len >> 1);
235 let mut cur_pos1 = pix_round(original_center);
236 let error1 = (original_center - (cur_pos1 - u_off)).abs();
237 let error2 = (original_center - (cur_pos1 + d_off)).abs();
238 if error1 < error2 {
239 cur_pos1 -= u_off;
240 } else {
241 cur_pos1 += d_off;
242 }
243 let edge_pos = cur_pos1 - cur_len / 2;
244 edges[edge_ix].pos = edge_pos;
245 edges[edge2_ix].pos = edge_pos + cur_len;
246 } else {
247 edges[edge_ix].pos = pix_round(edge.opos);
248 }
249 edges[edge_ix].flags |= Edge::DONE;
250 align_linked_edge(axis, metrics, group, scale, edge_ix, edge2_ix);
251 anchor_ix = Some(edge_ix);
252 }
253 } else {
254 if edge2_ix < edge_ix {
257 last_stem_pos = Some(edge.pos);
258 edges[edge_ix].flags |= Edge::DONE;
259 align_linked_edge(axis, metrics, group, scale, edge2_ix, edge_ix);
260 continue;
261 }
262 if axis.dim != Axis::VERTICAL && anchor_ix.is_none() {
263 delta = hint_normal_stem_cjk(axis, metrics, group, scale, edge_ix, edge2_ix, delta);
264 } else {
265 hint_normal_stem_cjk(axis, metrics, group, scale, edge_ix, edge2_ix, delta);
266 }
267 anchor_ix = Some(edge_ix);
268 axis.edges[edge_ix].flags |= Edge::DONE;
269 let edge2 = &mut axis.edges[edge2_ix];
270 edge2.flags |= Edge::DONE;
271 last_stem_pos = Some(edge2.pos);
272 }
273 }
274 (serif_count, anchor_ix)
275}
276
277fn hint_lowercase_m(edges: &mut [Edge], group: ScriptGroup) {
281 let (edge1_ix, edge2_ix, edge3_ix) = if edges.len() == 6 {
282 (0, 2, 4)
283 } else {
284 (1, 5, 9)
285 };
286 let edge1 = &edges[edge1_ix];
287 let edge2 = &edges[edge2_ix];
288 let edge3 = &edges[edge3_ix];
289 let dist1 = edge2.opos - edge1.opos;
290 let dist2 = edge3.opos - edge2.opos;
291 let span = (dist1 - dist2).abs();
292 if group != ScriptGroup::Default {
293 for (edge, ix) in [(edge1, edge1_ix), (edge2, edge2_ix), (edge3, edge3_ix)] {
296 if edge.link_ix != Some((ix + 1) as u16) {
297 return;
298 }
299 }
300 }
301 if span < 8 {
302 let delta = edge3.pos - (2 * edge2.pos - edge1.pos);
303 let link_ix = edge3.link_ix.map(|ix| ix as usize);
304 let edge3 = &mut edges[edge3_ix];
305 edge3.pos -= delta;
306 edge3.flags |= Edge::DONE;
307 if let Some(link_ix) = link_ix {
308 let link = &mut edges[link_ix];
309 link.pos -= delta;
310 link.flags |= Edge::DONE;
311 }
312 if edges.len() == 12 {
314 edges[8].pos -= delta;
315 edges[11].pos -= delta;
316 }
317 }
318}
319
320fn align_remaining_edges(
322 axis: &mut Axis,
323 group: ScriptGroup,
324 top_to_bottom_hinting: bool,
325 mut serif_count: usize,
326 mut anchor_ix: Option<usize>,
327) {
328 if group == ScriptGroup::Default {
329 for edge_ix in 0..axis.edges.len() {
331 let edges = &mut axis.edges;
332 let edge = &edges[edge_ix];
333 if edge.flags & Edge::DONE != 0 {
334 continue;
335 }
336 let mut delta = 1000;
337 if let Some(serif) = edge.serif(edges) {
338 delta = (serif.opos - edge.opos).abs();
339 }
340 if delta < 64 + 16 {
341 let serif_ix = edge.serif_ix.unwrap() as usize;
343 align_serif_edge(axis, serif_ix, edge_ix)
344 } else if let Some(anchor_ix) = anchor_ix {
345 let [before_ix, after_ix] = find_bounding_completed_edges(edges, edge_ix);
346 if let Some((before_ix, after_ix)) = before_ix.zip(after_ix) {
347 let before = &edges[before_ix];
348 let after = &edges[after_ix];
349 let new_pos = if after.opos == before.opos {
350 before.pos
351 } else {
352 before.pos
353 + fixed_mul_div(
354 edge.opos - before.opos,
355 after.pos - before.pos,
356 after.opos - before.opos,
357 )
358 };
359 edges[edge_ix].pos = new_pos;
360 } else {
361 let anchor = &edges[anchor_ix];
362 let new_pos = anchor.pos + ((edge.opos - anchor.opos + 16) & !31);
363 edges[edge_ix].pos = new_pos;
364 }
365 } else {
366 anchor_ix = Some(edge_ix);
367 let new_pos = pix_round(edge.opos);
368 edges[edge_ix].pos = new_pos;
369 }
370 let edges = &mut axis.edges;
371 edges[edge_ix].flags |= Edge::DONE;
372 adjust_link(edges, edge_ix, LinkDir::Prev, top_to_bottom_hinting);
373 adjust_link(edges, edge_ix, LinkDir::Next, top_to_bottom_hinting);
374 }
375 } else {
376 for edge_ix in 0..axis.edges.len() {
378 let edge = &mut axis.edges[edge_ix];
379 if edge.flags & Edge::DONE != 0 {
380 continue;
381 }
382 if let Some(serif_ix) = edge.serif_ix.map(|ix| ix as usize) {
383 edge.flags |= Edge::DONE;
384 align_serif_edge(axis, serif_ix, edge_ix);
385 serif_count = serif_count.saturating_sub(1);
386 }
387 }
388 if serif_count == 0 {
389 return;
390 }
391 for edge_ix in 0..axis.edges.len() {
392 let edges = axis.edges.as_mut_slice();
393 let edge = &edges[edge_ix];
394 if edge.flags & Edge::DONE != 0 {
395 continue;
396 }
397 let [before_ix, after_ix] = find_bounding_completed_edges(edges, edge_ix);
398 match (before_ix, after_ix) {
399 (Some(before_ix), None) => {
400 align_serif_edge(axis, before_ix, edge_ix);
401 }
402 (None, Some(after_ix)) => {
403 align_serif_edge(axis, after_ix, edge_ix);
404 }
405 (Some(before_ix), Some(after_ix)) => {
406 let before = edges[before_ix];
407 let after = edges[after_ix];
408 if after.fpos == before.fpos {
409 edges[edge_ix].pos = before.pos;
410 } else {
411 edges[edge_ix].pos = before.pos
412 + fixed_mul_div(
413 edge.fpos as i32 - before.fpos as i32,
414 after.pos - before.pos,
415 after.fpos as i32 - before.fpos as i32,
416 );
417 }
418 }
419 _ => {}
420 }
421 }
422 }
423}
424
425#[derive(Copy, Clone, PartialEq)]
426enum LinkDir {
427 Prev,
428 Next,
429}
430
431fn adjust_link(
435 edges: &mut [Edge],
436 edge_ix: usize,
437 link_dir: LinkDir,
438 top_to_bottom_hinting: bool,
439) -> Option<()> {
440 let edge = &edges[edge_ix];
441 let (edge2, prev_edge) = if link_dir == LinkDir::Next {
442 let edge2 = edges.get(edge_ix + 1)?;
443 if edge2.flags & Edge::DONE == 0 {
445 return None;
446 }
447 (edge2, edges.get(edge_ix.checked_sub(1)?)?)
448 } else {
449 let edge = edges.get(edge_ix.checked_sub(1)?)?;
450 (edge, edge)
451 };
452 let pos1 = edge.pos;
453 let pos2 = edge2.pos;
454 let order_check = match (link_dir, top_to_bottom_hinting) {
455 (LinkDir::Prev, true) | (LinkDir::Next, false) => pos1 > pos2,
456 (LinkDir::Prev, false) | (LinkDir::Next, true) => pos1 < pos2,
457 };
458 if !order_check {
459 return None;
460 }
461 let link = edge.link(edges)?;
462 if (link.pos - prev_edge.pos).abs() > 16 {
463 let new_pos = edge2.pos;
464 edges[edge_ix].pos = new_pos;
465 }
466 Some(())
467}
468
469fn find_bounding_completed_edges(edges: &[Edge], ix: usize) -> [Option<usize>; 2] {
472 let before_ix = edges
473 .get(..ix)
474 .unwrap_or_default()
475 .iter()
476 .enumerate()
477 .rev()
478 .filter_map(|(ix, edge)| (edge.flags & Edge::DONE != 0).then_some(ix))
479 .next();
480 let after_ix = edges
481 .iter()
482 .enumerate()
483 .skip(ix + 1)
484 .filter_map(|(ix, edge)| (edge.flags & Edge::DONE != 0).then_some(ix))
485 .next();
486 [before_ix, after_ix]
487}
488
489fn snap_width(widths: &[ScaledWidth], width: i32) -> i32 {
493 let (_, ref_width) =
494 widths
495 .iter()
496 .fold((64 + 32 + 2, width), |(best_dist, ref_width), candidate| {
497 let dist = (width - candidate.scaled).abs();
498 if dist < best_dist {
499 (dist, candidate.scaled)
500 } else {
501 (best_dist, ref_width)
502 }
503 });
504 let scaled = pix_round(ref_width);
505 if width >= ref_width {
506 if width < scaled + 48 {
507 ref_width
508 } else {
509 width
510 }
511 } else if width > scaled - 48 {
512 ref_width
513 } else {
514 width
515 }
516}
517
518fn stem_width(
522 metrics: &ScaledAxisMetrics,
523 group: ScriptGroup,
524 scale: &Scale,
525 width: i32,
526 base_delta: i32,
527 base_flags: u8,
528 stem_flags: u8,
529) -> i32 {
530 if scale.flags & Scale::STEM_ADJUST == 0
531 || (group == ScriptGroup::Default && metrics.width_metrics.is_extra_light)
532 {
533 return width;
534 }
535 let is_vertical = metrics.dim == Axis::VERTICAL;
536 let sign = if width < 0 { -1 } else { 1 };
537 let mut dist = width.abs();
538 if (is_vertical && scale.flags & Scale::VERTICAL_SNAP == 0)
539 || (!is_vertical && scale.flags & Scale::HORIZONTAL_SNAP == 0)
540 {
541 if group == ScriptGroup::Default {
543 if (stem_flags & Edge::SERIF != 0) && is_vertical && (dist < 3 * 64) {
544 return dist * sign;
546 } else if base_flags & Edge::ROUND != 0 {
547 if dist < 80 {
548 dist = 64;
549 }
550 } else if dist < 56 {
551 dist = 56;
552 }
553 }
554 if !metrics.widths.is_empty() {
555 let min_width = metrics.widths[0].scaled;
557 let delta = (dist - min_width).abs();
558 if delta < 40 {
559 dist = min_width.max(48);
560 return dist * sign;
561 }
562 if group == ScriptGroup::Default {
563 if dist < 3 * 64 {
566 let delta = dist & 63;
567 dist &= -64;
568 if delta < 10 {
569 dist += delta;
570 } else if delta < 32 {
571 dist += 10;
572 } else if delta < 54 {
573 dist += 54;
574 } else {
575 dist += delta;
576 }
577 } else {
578 let mut new_base_delta = 0;
579 if (width > 0 && base_delta > 0) || (width < 0 && base_delta < 0) {
580 if scale.size < 10.0 {
581 new_base_delta = base_delta;
582 } else if scale.size < 30.0 {
583 new_base_delta = (base_delta * (30.0 - scale.size) as i32) / 20;
584 }
585 }
586 dist = (dist - new_base_delta.abs() + 32) & !63;
587 }
588 }
589 }
590 if group != ScriptGroup::Default {
591 if dist < 54 {
594 dist += (54 - dist) / 2;
595 } else if dist < 3 * 64 {
596 let delta = dist & 63;
597 dist &= -64;
598 if delta < 10 {
599 dist += delta;
600 } else if delta < 22 {
601 dist += 10;
602 } else if delta < 42 {
603 dist += delta;
604 } else if delta < 54 {
605 dist += 54;
606 } else {
607 dist += delta;
608 }
609 }
610 }
611 } else {
612 let original_dist = dist;
614 dist = snap_width(&metrics.widths, dist);
615 if is_vertical {
616 if dist >= 64 {
618 dist = (dist + 16) & !63;
619 } else {
620 dist = 64;
621 }
622 } else if scale.flags & Scale::MONO != 0 {
623 if dist < 64 {
626 dist = 64;
627 } else {
628 dist = (dist + 32) & !63;
629 }
630 } else {
631 if dist < 48 {
634 dist = (dist + 64) >> 1;
635 } else if dist < 128 {
636 dist = (dist + 22) & !63;
639 if group == ScriptGroup::Default {
640 let delta = (dist - original_dist).abs();
642 if delta >= 16 {
643 dist = original_dist;
644 if dist < 48 {
645 dist = (dist + 64) >> 1;
646 }
647 }
648 }
649 } else {
650 dist = (dist + 32) & !63;
652 }
653 }
654 }
655 dist * sign
656}
657
658fn align_linked_edge(
662 axis: &mut Axis,
663 metrics: &ScaledAxisMetrics,
664 group: ScriptGroup,
665 scale: &Scale,
666 base_edge_ix: usize,
667 stem_edge_ix: usize,
668) {
669 let edges = axis.edges.as_mut_slice();
670 let base_edge = &edges[base_edge_ix];
671 let stem_edge = &edges[stem_edge_ix];
672 let width = stem_edge.opos - base_edge.opos;
673 let base_delta = base_edge.pos - base_edge.opos;
674 let fitted_width = stem_width(
675 metrics,
676 group,
677 scale,
678 width,
679 base_delta,
680 base_edge.flags,
681 stem_edge.flags,
682 );
683 edges[stem_edge_ix].pos = base_edge.pos + fitted_width;
684}
685
686fn align_serif_edge(axis: &mut Axis, base_edge_ix: usize, serif_edge_ix: usize) {
690 let edges = axis.edges.as_mut_slice();
691 let base_edge = &edges[base_edge_ix];
692 let serif_edge = &edges[serif_edge_ix];
693 edges[serif_edge_ix].pos = base_edge.pos + (serif_edge.opos - base_edge.opos);
694}
695
696fn hint_normal_stem_cjk(
700 axis: &mut Axis,
701 metrics: &ScaledAxisMetrics,
702 group: ScriptGroup,
703 scale: &Scale,
704 edge_ix: usize,
705 edge2_ix: usize,
706 anchor: i32,
707) -> i32 {
708 const MAX_HORIZONTAL_GAP: i32 = 9;
709 const MAX_VERTICAL_GAP: i32 = 15;
710 const MAX_DELTA_ABS: i32 = 14;
711 let edge = axis.edges[edge_ix];
712 let edge2 = axis.edges[edge2_ix];
713 let do_stem_adjust = scale.flags & Scale::STEM_ADJUST != 0;
714 let threshold_delta = if do_stem_adjust {
715 0
716 } else {
717 let delta = if axis.dim == Axis::VERTICAL {
718 MAX_HORIZONTAL_GAP
719 } else {
720 MAX_VERTICAL_GAP
721 };
722 if edge.flags & Edge::ROUND != 0 && edge2.flags & Edge::ROUND != 0 {
723 delta
724 } else {
725 delta / 3
726 }
727 };
728 let threshold = 64 - threshold_delta;
729 let original_len = edge2.opos - edge.opos;
730 let cur_len = stem_width(
731 metrics,
732 group,
733 scale,
734 original_len,
735 0,
736 edge.flags,
737 edge2.flags,
738 );
739 let original_center = (edge.opos + edge2.opos) / 2 + anchor;
740 let cur_pos1 = original_center - cur_len / 2;
741 let cur_pos2 = cur_pos1 + cur_len;
742 let mut finish = |mut delta: i32| {
743 if !do_stem_adjust {
744 delta = delta.clamp(-MAX_DELTA_ABS, MAX_DELTA_ABS);
745 }
746 let adjustment = cur_pos1 + delta;
747 if edge.opos < edge2.opos {
748 axis.edges[edge_ix].pos = adjustment;
749 axis.edges[edge2_ix].pos = adjustment + cur_len;
750 } else {
751 axis.edges[edge2_ix].pos = adjustment;
752 axis.edges[edge_ix].pos = adjustment + cur_len;
753 }
754 delta
755 };
756 let mut d_off1 = cur_pos1 - pix_floor(cur_pos1);
757 let mut d_off2 = cur_pos2 - pix_floor(cur_pos2);
758 let mut delta = 0;
759 if d_off1 == 0 || d_off2 == 0 {
760 return finish(delta);
761 }
762 let mut u_off1 = 64 - d_off1;
763 let mut u_off2 = 64 - d_off2;
764 if cur_len <= threshold {
765 if d_off2 < cur_len {
766 delta = if u_off1 <= d_off2 { u_off1 } else { -d_off2 };
767 }
768 return finish(delta);
769 }
770 if threshold < 64
771 && (d_off1 >= threshold
772 || u_off1 >= threshold
773 || d_off2 >= threshold
774 || u_off2 >= threshold)
775 {
776 return finish(delta);
777 }
778 let mut offset = cur_len & 63;
779 if offset < 32 {
780 if u_off1 <= offset || d_off2 <= offset {
781 return finish(delta);
782 }
783 } else {
784 offset = 64 - threshold;
785 }
786 d_off1 = threshold - u_off1;
787 u_off1 -= offset;
788 u_off2 = threshold - d_off2;
789 d_off2 -= offset;
790 if d_off1 <= u_off1 {
791 u_off1 = -d_off1;
792 }
793 if d_off2 <= u_off2 {
794 u_off2 = -d_off2;
795 }
796 if u_off1.abs() <= u_off2.abs() {
797 delta = u_off1;
798 } else {
799 delta = u_off2;
800 }
801 finish(delta)
802}
803
804#[cfg(test)]
805mod tests {
806 use super::{
807 super::super::{
808 metrics,
809 outline::Outline,
810 shape::{Shaper, ShaperMode},
811 style, topo,
812 },
813 *,
814 };
815 use crate::{attribute::Style, MetadataProvider};
816 use raw::{types::GlyphId, FontRef, TableProvider};
817
818 #[test]
819 fn edge_hinting_default() {
820 let expected_h_edges = [
821 (0, Edge::DONE | Edge::ROUND),
822 (133, Edge::DONE),
823 (187, Edge::DONE),
824 (192, Edge::DONE | Edge::ROUND),
825 ];
826 let expected_v_edges = [
827 (-256, Edge::DONE),
828 (463, Edge::DONE),
829 (576, Edge::DONE | Edge::ROUND | Edge::SERIF),
830 (633, Edge::DONE),
831 ];
832 check_edges(
833 font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS,
834 GlyphId::new(9),
835 style::StyleClass::HEBR,
836 &expected_h_edges,
837 &expected_v_edges,
838 );
839 }
840
841 #[test]
842 fn edge_hinting_cjk() {
843 let expected_h_edges = [
844 (128, Edge::DONE),
845 (193, Edge::DONE),
846 (473, 0),
847 (594, 0),
848 (704, Edge::DONE),
849 (673, Edge::DONE),
850 (767, Edge::DONE),
851 (832, Edge::DONE),
852 (896, Edge::DONE),
853 ];
854 let expected_v_edges = [
855 (-64, Edge::DONE | Edge::ROUND),
856 (15, Edge::ROUND),
857 (142, Edge::ROUND),
858 (546, Edge::DONE),
859 (624, Edge::DONE),
860 (576, Edge::DONE),
861 (720, Edge::DONE),
862 (768, Edge::DONE),
863 (799, Edge::ROUND),
864 ];
865 check_edges(
866 font_test_data::NOTOSERIFTC_AUTOHINT_METRICS,
867 GlyphId::new(9),
868 style::StyleClass::HANI,
869 &expected_h_edges,
870 &expected_v_edges,
871 );
872 }
873
874 fn check_edges(
875 font_data: &[u8],
876 glyph_id: GlyphId,
877 class: usize,
878 expected_h_edges: &[(i32, u8)],
879 expected_v_edges: &[(i32, u8)],
880 ) {
881 let font = FontRef::new(font_data).unwrap();
882 let shaper = Shaper::new(&font, ShaperMode::Nominal);
883 let class = &style::STYLE_CLASSES[class];
884 let unscaled_metrics =
885 metrics::compute_unscaled_style_metrics(&shaper, Default::default(), class);
886 let scale = metrics::Scale::new(
887 16.0,
888 font.head().unwrap().units_per_em() as i32,
889 Style::Normal,
890 Default::default(),
891 class.script.group,
892 );
893 let scaled_metrics = metrics::scale_style_metrics(&unscaled_metrics, scale);
894 let glyphs = font.outline_glyphs();
895 let glyph = glyphs.get(glyph_id).unwrap();
896 let mut outline = Outline::default();
897 outline.fill(&glyph, Default::default()).unwrap();
898 let mut axes = [
899 Axis::new(Axis::HORIZONTAL, outline.orientation),
900 Axis::new(Axis::VERTICAL, outline.orientation),
901 ];
902 for (dim, axis) in axes.iter_mut().enumerate() {
903 topo::compute_segments(&mut outline, axis, class.script.group);
904 topo::link_segments(
905 &outline,
906 axis,
907 scaled_metrics.axes[dim].scale,
908 class.script.group,
909 unscaled_metrics.axes[dim].max_width(),
910 );
911 topo::compute_edges(
912 axis,
913 &scaled_metrics.axes[0],
914 class.script.hint_top_to_bottom,
915 scaled_metrics.axes[1].scale,
916 class.script.group,
917 );
918 if dim == Axis::VERTICAL {
919 topo::compute_blue_edges(
920 axis,
921 &scale,
922 &unscaled_metrics.axes[dim].blues,
923 &scaled_metrics.axes[dim].blues,
924 class.script.group,
925 );
926 }
927 hint_edges(
928 axis,
929 &scaled_metrics.axes[dim],
930 class.script.group,
931 &scale,
932 class.script.hint_top_to_bottom,
933 );
934 }
935 let h_edges = axes[Axis::HORIZONTAL]
937 .edges
938 .iter()
939 .map(|edge| (edge.pos, edge.flags))
940 .collect::<Vec<_>>();
941 let v_edges = axes[Axis::VERTICAL]
942 .edges
943 .iter()
944 .map(|edge| (edge.pos, edge.flags))
945 .collect::<Vec<_>>();
946 assert_eq!(h_edges, expected_h_edges);
947 assert_eq!(v_edges, expected_v_edges);
948 }
949}