1use super::super::{
8 metrics::{fixed_mul_div, pix_floor, pix_round, Scale, ScaledAxisMetrics, ScaledWidth},
9 recorder::{EdgeAction, HintsRecorder},
10 style::ScriptGroup,
11 topo::{Axis, Dimension, Edge, TopoFlags},
12 ScaleFlags,
13};
14
15pub(crate) fn hint_edges(
21 axis: &mut Axis,
22 metrics: &ScaledAxisMetrics,
23 group: ScriptGroup,
24 scale: &Scale,
25 mut top_to_bottom_hinting: bool,
26 mut recorder: Option<&mut HintsRecorder>,
27) {
28 if axis.dim != Dimension::Vertical {
29 top_to_bottom_hinting = false;
30 }
31 let anchor_ix = align_edges_to_blues(axis, metrics, group, scale, recorder.as_deref_mut());
33 let (serif_count, anchor_ix) = align_stem_edges(
35 axis,
36 metrics,
37 group,
38 scale,
39 top_to_bottom_hinting,
40 anchor_ix,
41 recorder.as_deref_mut(),
42 );
43 let edges = axis.edges.as_mut_slice();
44 if axis.dim == Dimension::Horizontal && (edges.len() == 6 || edges.len() == 12) {
46 hint_lowercase_m(edges, group);
47 }
48 if serif_count > 0 || anchor_ix.is_none() {
50 align_remaining_edges(
51 axis,
52 group,
53 top_to_bottom_hinting,
54 serif_count,
55 anchor_ix,
56 recorder,
57 );
58 }
59}
60
61fn align_edges_to_blues(
65 axis: &mut Axis,
66 metrics: &ScaledAxisMetrics,
67 group: ScriptGroup,
68 scale: &Scale,
69 mut recorder: Option<&mut HintsRecorder>,
70) -> Option<usize> {
71 let mut anchor_ix = None;
72 if group == ScriptGroup::Default && axis.dim != Dimension::Vertical {
74 return anchor_ix;
75 }
76 for edge_ix in 0..axis.edges.len() {
77 let mut linked_edge_to_align = None;
78 {
79 let edges = axis.edges.as_mut_slice();
80 let edge = &edges[edge_ix];
81 if edge.flags.contains(TopoFlags::DONE) {
82 continue;
83 }
84 let edge2_ix = edge.link_ix.map(|x| x as usize);
85 let edge2 = edge2_ix.map(|ix| &edges[ix]);
86 if let (true, Some(edge2)) = (edge.blue_edge.is_some(), edge2) {
88 if edge2.blue_edge.is_some() {
89 let skip_ix = if edge2.flags.contains(TopoFlags::NEUTRAL) {
90 edge2_ix
91 } else if edge.flags.contains(TopoFlags::NEUTRAL) {
92 Some(edge_ix)
93 } else {
94 None
95 };
96 if let Some(skip_ix) = skip_ix {
97 let skip_edge = &mut edges[skip_ix];
98 skip_edge.blue_edge = None;
99 skip_edge.flags &= !TopoFlags::NEUTRAL;
100 }
101 }
102 }
103 let blue = edges[edge_ix].blue_edge;
105 let (blue, edge1_ix, edge2_ix) = if let Some(blue) = blue {
106 (blue, Some(edge_ix), edge2_ix)
107 } else if let Some(edge2_blue) = edge2_ix.and_then(|ix| edges[ix].blue_edge) {
108 (edge2_blue, edge2_ix, Some(edge_ix))
109 } else {
110 (Default::default(), None, None)
111 };
112 let Some(edge1_ix) = edge1_ix else {
113 continue;
114 };
115 if edges[edge1_ix].flags.contains(TopoFlags::DONE) {
119 continue;
120 }
121 let edge1 = &mut edges[edge1_ix];
122 edge1.pos = blue.fitted;
123 edge1.flags |= TopoFlags::DONE;
124 if let Some(recorder) = recorder.as_mut() {
125 let action = if anchor_ix.is_none() {
126 EdgeAction::BlueAnchor
127 } else {
128 EdgeAction::Blue
129 };
130 recorder.record_edge(
131 axis.dim,
132 action,
133 edge1_ix,
134 anchor_ix.is_none().then_some(edge_ix),
135 None,
136 None,
137 None,
138 edges[edge1_ix].blue_provenance,
139 );
140 }
141 if let Some(edge2_ix) = edge2_ix {
142 if edges[edge2_ix].blue_edge.is_none() {
143 edges[edge2_ix].flags |= TopoFlags::DONE;
144 linked_edge_to_align = Some((edge1_ix, edge2_ix));
145 }
146 }
147 }
148 if let Some((edge1_ix, edge2_ix)) = linked_edge_to_align {
149 align_linked_edge(
150 axis,
151 metrics,
152 group,
153 scale,
154 edge1_ix,
155 edge2_ix,
156 recorder.as_deref_mut(),
157 );
158 }
159 if anchor_ix.is_none() {
160 anchor_ix = Some(edge_ix);
161 }
162 }
163 anchor_ix
164}
165
166fn align_stem_edges(
170 axis: &mut Axis,
171 metrics: &ScaledAxisMetrics,
172 group: ScriptGroup,
173 scale: &Scale,
174 top_to_bottom_hinting: bool,
175 mut anchor_ix: Option<usize>,
176 mut recorder: Option<&mut HintsRecorder>,
177) -> (usize, Option<usize>) {
178 let mut serif_count = 0;
179 let mut last_stem_pos = None;
180 let mut delta = 0;
181 for edge_ix in 0..axis.edges.len() {
184 let (edge_flags, edge_link_ix, edge_pos, edge2_pos_for_cjk, edge2_has_blue) = {
187 let edges = axis.edges.as_slice();
188 let edge = &edges[edge_ix];
189 let link_ix = edge.link_ix.map(|ix| ix as usize);
190 let (edge2_pos, edge2_has_blue) = link_ix
191 .map(|ix| (edges[ix].pos, edges[ix].blue_edge.is_some()))
192 .unwrap_or((0, false));
193 (edge.flags, link_ix, edge.pos, edge2_pos, edge2_has_blue)
194 };
195 if edge_flags.contains(TopoFlags::DONE) {
196 continue;
197 }
198 let Some(edge2_ix) = edge_link_ix else {
200 serif_count += 1;
201 continue;
202 };
203 if group != ScriptGroup::Default {
206 if let Some(last_pos) = last_stem_pos {
207 if edge_pos < last_pos + 64 || edge2_pos_for_cjk < last_pos + 64 {
208 serif_count += 1;
209 continue;
210 }
211 }
212 }
213 if edge2_has_blue {
215 align_linked_edge(
216 axis,
217 metrics,
218 group,
219 scale,
220 edge2_ix,
221 edge_ix,
222 recorder.as_deref_mut(),
223 );
224 axis.edges[edge_ix].flags |= TopoFlags::DONE;
225 continue;
226 }
227 let edges = axis.edges.as_mut_slice();
228 if group == ScriptGroup::Default {
229 if let Some(anchor_ix) = anchor_ix {
233 let anchor = &edges[anchor_ix];
234 let edge = edges[edge_ix];
235 let edge2 = edges[edge2_ix];
236 let original_pos = anchor.pos + (edge.opos - anchor.opos);
237 let original_len = edge2.opos - edge.opos;
238 let original_center = original_pos + (original_len >> 1);
239 let cur_len = stem_width(
240 metrics,
241 group,
242 scale,
243 original_len,
244 0,
245 edge.flags,
246 edge2.flags,
247 );
248 if edge2.flags.contains(TopoFlags::DONE) {
249 let new_pos = edge2.pos - cur_len;
250 edges[edge_ix].pos = new_pos;
251 if let Some(recorder) = recorder.as_mut() {
252 recorder.record_edge(
253 axis.dim,
254 EdgeAction::Adjust,
255 edge_ix,
256 Some(edge2_ix),
257 None,
258 edge_ix.checked_sub(1),
259 None,
260 None,
261 );
262 }
263 } else if cur_len < 96 {
264 let cur_pos1 = pix_round(original_center);
265 let (u_off, d_off) = if cur_len <= 64 { (32, 32) } else { (38, 26) };
266 let delta1 = (original_center - (cur_pos1 - u_off)).abs();
267 let delta2 = (original_center - (cur_pos1 + d_off)).abs();
268 let cur_pos1 = if delta1 < delta2 {
269 cur_pos1 - u_off
270 } else {
271 cur_pos1 + d_off
272 };
273 edges[edge_ix].pos = cur_pos1 - cur_len / 2;
274 edges[edge2_ix].pos = cur_pos1 + cur_len / 2;
275 if let Some(recorder) = recorder.as_mut() {
276 recorder.record_edge(
277 axis.dim,
278 EdgeAction::Stem,
279 edge_ix,
280 Some(edge2_ix),
281 None,
282 edge_ix.checked_sub(1),
283 None,
284 None,
285 );
286 }
287 } else {
288 let cur_pos1 = pix_round(original_pos);
289 let delta1 = (cur_pos1 + (cur_len >> 1) - original_center).abs();
290 let cur_pos2 = pix_round(original_pos + original_len) - cur_len;
291 let delta2 = (cur_pos2 + (cur_len >> 1) - original_center).abs();
292 let new_pos = if delta1 < delta2 { cur_pos1 } else { cur_pos2 };
293 let new_pos2 = new_pos + cur_len;
294 edges[edge_ix].pos = new_pos;
295 edges[edge2_ix].pos = new_pos2;
296 if let Some(recorder) = recorder.as_mut() {
297 recorder.record_edge(
298 axis.dim,
299 EdgeAction::Stem,
300 edge_ix,
301 Some(edge2_ix),
302 None,
303 edge_ix.checked_sub(1),
304 None,
305 None,
306 );
307 }
308 }
309 edges[edge_ix].flags |= TopoFlags::DONE;
310 edges[edge2_ix].flags |= TopoFlags::DONE;
311 if edge_ix > 0 {
312 adjust_link(
313 edges,
314 axis.dim,
315 edge_ix,
316 LinkDir::Prev,
317 top_to_bottom_hinting,
318 recorder.as_deref_mut(),
319 );
320 }
321 } else {
322 let edge = edges[edge_ix];
324 let edge2 = edges[edge2_ix];
325 let original_len = edge2.opos - edge.opos;
326 let cur_len = stem_width(
327 metrics,
328 group,
329 scale,
330 original_len,
331 0,
332 edge.flags,
333 edge2.flags,
334 );
335 let (u_off, d_off) = if cur_len <= 64 {
337 (32, 32)
339 } else {
340 (38, 26)
342 };
343 if cur_len < 96 {
344 let original_center = edge.opos + (original_len >> 1);
345 let mut cur_pos1 = pix_round(original_center);
346 let error1 = (original_center - (cur_pos1 - u_off)).abs();
347 let error2 = (original_center - (cur_pos1 + d_off)).abs();
348 if error1 < error2 {
349 cur_pos1 -= u_off;
350 } else {
351 cur_pos1 += d_off;
352 }
353 let edge_pos = cur_pos1 - cur_len / 2;
354 edges[edge_ix].pos = edge_pos;
355 edges[edge2_ix].pos = edge_pos + cur_len;
356 } else {
357 edges[edge_ix].pos = pix_round(edge.opos);
358 }
359 edges[edge_ix].flags |= TopoFlags::DONE;
360 if let Some(recorder) = recorder.as_mut() {
361 recorder.record_edge(
362 axis.dim,
363 EdgeAction::Anchor,
364 edge_ix,
365 Some(edge2_ix),
366 None,
367 None,
368 None,
369 None,
370 );
371 }
372 align_linked_edge(
373 axis,
374 metrics,
375 group,
376 scale,
377 edge_ix,
378 edge2_ix,
379 recorder.as_deref_mut(),
380 );
381 anchor_ix = Some(edge_ix);
382 }
383 } else {
384 if edge2_ix < edge_ix {
387 last_stem_pos = Some(edge_pos);
388 edges[edge_ix].flags |= TopoFlags::DONE;
389 align_linked_edge(
390 axis,
391 metrics,
392 group,
393 scale,
394 edge2_ix,
395 edge_ix,
396 recorder.as_deref_mut(),
397 );
398 continue;
399 }
400 if axis.dim != Dimension::Vertical && anchor_ix.is_none() {
401 delta = hint_normal_stem_cjk(axis, metrics, group, scale, edge_ix, edge2_ix, delta);
402 } else {
403 hint_normal_stem_cjk(axis, metrics, group, scale, edge_ix, edge2_ix, delta);
404 }
405 anchor_ix = Some(edge_ix);
406 axis.edges[edge_ix].flags |= TopoFlags::DONE;
407 let edge2 = &mut axis.edges[edge2_ix];
408 edge2.flags |= TopoFlags::DONE;
409 last_stem_pos = Some(edge2.pos);
410 }
411 }
412 (serif_count, anchor_ix)
413}
414
415fn hint_lowercase_m(edges: &mut [Edge], group: ScriptGroup) {
419 let (edge1_ix, edge2_ix, edge3_ix) = if edges.len() == 6 {
420 (0, 2, 4)
421 } else {
422 (1, 5, 9)
423 };
424 let edge1 = &edges[edge1_ix];
425 let edge2 = &edges[edge2_ix];
426 let edge3 = &edges[edge3_ix];
427 let dist1 = edge2.opos - edge1.opos;
428 let dist2 = edge3.opos - edge2.opos;
429 let span = (dist1 - dist2).abs();
430 if group != ScriptGroup::Default {
431 for (edge, ix) in [(edge1, edge1_ix), (edge2, edge2_ix), (edge3, edge3_ix)] {
434 if edge.link_ix != Some((ix + 1) as u16) {
435 return;
436 }
437 }
438 }
439 if span < 8 {
440 let delta = edge3.pos - (2 * edge2.pos - edge1.pos);
441 let link_ix = edge3.link_ix.map(|ix| ix as usize);
442 let edge3 = &mut edges[edge3_ix];
443 edge3.pos -= delta;
444 edge3.flags |= TopoFlags::DONE;
445 if let Some(link_ix) = link_ix {
446 let link = &mut edges[link_ix];
447 link.pos -= delta;
448 link.flags |= TopoFlags::DONE;
449 }
450 if edges.len() == 12 {
452 edges[8].pos -= delta;
453 edges[11].pos -= delta;
454 }
455 }
456}
457
458fn align_remaining_edges(
460 axis: &mut Axis,
461 group: ScriptGroup,
462 top_to_bottom_hinting: bool,
463 mut serif_count: usize,
464 mut anchor_ix: Option<usize>,
465 mut recorder: Option<&mut HintsRecorder>,
466) {
467 if group == ScriptGroup::Default {
468 for edge_ix in 0..axis.edges.len() {
470 let (edge_flags, edge_opos, edge_serif_ix, delta) = {
471 let edges = axis.edges.as_slice();
472 let edge = &edges[edge_ix];
473 let delta = edge
474 .serif(edges)
475 .map(|serif| (serif.opos - edge.opos).abs())
476 .unwrap_or(1000);
477 (edge.flags, edge.opos, edge.serif_ix, delta)
478 };
479 if edge_flags.contains(TopoFlags::DONE) {
480 continue;
481 }
482 if delta < 64 + 16 {
483 let serif_ix = edge_serif_ix.unwrap() as usize;
485 align_serif_edge(axis, serif_ix, edge_ix);
486 if let Some(recorder) = recorder.as_mut() {
487 let edges = axis.edges.as_slice();
488 let [lower_bound_ix, upper_bound_ix] = latin_remaining_bounds(edges, edge_ix);
489 recorder.record_edge(
490 axis.dim,
491 EdgeAction::Serif,
492 edge_ix,
493 Some(serif_ix),
494 None,
495 lower_bound_ix,
496 upper_bound_ix,
497 None,
498 );
499 }
500 } else if let Some(anchor_ix) = anchor_ix {
501 let edges = axis.edges.as_mut_slice();
502 let [before_ix, after_ix] = find_bounding_completed_edges(edges, edge_ix);
503 if let Some((before_ix, after_ix)) = before_ix.zip(after_ix) {
504 let before = &edges[before_ix];
505 let after = &edges[after_ix];
506 let new_pos = if after.opos == before.opos {
507 before.pos
508 } else {
509 before.pos
510 + fixed_mul_div(
511 edge_opos - before.opos,
512 after.pos - before.pos,
513 after.opos - before.opos,
514 )
515 };
516 edges[edge_ix].pos = new_pos;
517 if let Some(recorder) = recorder.as_mut() {
518 let [lower_bound_ix, upper_bound_ix] =
519 latin_remaining_bounds(edges, edge_ix);
520 recorder.record_edge(
521 axis.dim,
522 EdgeAction::SerifLink1,
523 edge_ix,
524 Some(before_ix),
525 Some(after_ix),
526 lower_bound_ix,
527 upper_bound_ix,
528 None,
529 );
530 }
531 } else {
532 let anchor = &edges[anchor_ix];
533 let new_pos = anchor.pos + ((edge_opos - anchor.opos + 16) & !31);
534 edges[edge_ix].pos = new_pos;
535 if let Some(recorder) = recorder.as_mut() {
536 let [lower_bound_ix, upper_bound_ix] =
537 latin_remaining_bounds(edges, edge_ix);
538 recorder.record_edge(
539 axis.dim,
540 EdgeAction::SerifLink2,
541 edge_ix,
542 None,
543 None,
544 lower_bound_ix,
545 upper_bound_ix,
546 None,
547 );
548 }
549 }
550 } else {
551 anchor_ix = Some(edge_ix);
552 let edges = axis.edges.as_mut_slice();
553 let new_pos = pix_round(edge_opos);
554 edges[edge_ix].pos = new_pos;
555 if let Some(recorder) = recorder.as_mut() {
556 let [lower_bound_ix, upper_bound_ix] = latin_remaining_bounds(edges, edge_ix);
557 recorder.record_edge(
558 axis.dim,
559 EdgeAction::SerifAnchor,
560 edge_ix,
561 None,
562 None,
563 lower_bound_ix,
564 upper_bound_ix,
565 None,
566 );
567 }
568 }
569 let edges = &mut axis.edges;
570 edges[edge_ix].flags |= TopoFlags::DONE;
571 adjust_link(
572 edges,
573 axis.dim,
574 edge_ix,
575 LinkDir::Prev,
576 top_to_bottom_hinting,
577 recorder.as_deref_mut(),
578 );
579 adjust_link(
580 edges,
581 axis.dim,
582 edge_ix,
583 LinkDir::Next,
584 top_to_bottom_hinting,
585 recorder.as_deref_mut(),
586 );
587 }
588 } else {
589 for edge_ix in 0..axis.edges.len() {
591 let edge = &mut axis.edges[edge_ix];
592 if edge.flags.contains(TopoFlags::DONE) {
593 continue;
594 }
595 if let Some(serif_ix) = edge.serif_ix.map(|ix| ix as usize) {
596 edge.flags |= TopoFlags::DONE;
597 align_serif_edge(axis, serif_ix, edge_ix);
598 serif_count = serif_count.saturating_sub(1);
599 }
600 }
601 if serif_count == 0 {
602 return;
603 }
604 for edge_ix in 0..axis.edges.len() {
605 let edges = axis.edges.as_mut_slice();
606 let edge = &edges[edge_ix];
607 if edge.flags.contains(TopoFlags::DONE) {
608 continue;
609 }
610 let [before_ix, after_ix] = find_bounding_completed_edges(edges, edge_ix);
611 match (before_ix, after_ix) {
612 (Some(before_ix), None) => {
613 align_serif_edge(axis, before_ix, edge_ix);
614 }
615 (None, Some(after_ix)) => {
616 align_serif_edge(axis, after_ix, edge_ix);
617 }
618 (Some(before_ix), Some(after_ix)) => {
619 let before = edges[before_ix];
620 let after = edges[after_ix];
621 if after.fpos == before.fpos {
622 edges[edge_ix].pos = before.pos;
623 } else {
624 edges[edge_ix].pos = before.pos
625 + fixed_mul_div(
626 edge.fpos as i32 - before.fpos as i32,
627 after.pos - before.pos,
628 after.fpos as i32 - before.fpos as i32,
629 );
630 }
631 }
632 _ => {}
633 }
634 }
635 }
636}
637
638#[derive(Copy, Clone, PartialEq)]
639enum LinkDir {
640 Prev,
641 Next,
642}
643
644fn adjust_link(
648 edges: &mut [Edge],
649 dim: Dimension,
650 edge_ix: usize,
651 link_dir: LinkDir,
652 top_to_bottom_hinting: bool,
653 mut recorder: Option<&mut HintsRecorder>,
654) -> Option<()> {
655 let edge = &edges[edge_ix];
656 let (edge2, prev_edge) = if link_dir == LinkDir::Next {
657 let edge2 = edges.get(edge_ix + 1)?;
658 if !edge2.flags.contains(TopoFlags::DONE) {
660 return None;
661 }
662 (edge2, edges.get(edge_ix.checked_sub(1)?)?)
663 } else {
664 let edge = edges.get(edge_ix.checked_sub(1)?)?;
665 (edge, edge)
666 };
667 let pos1 = edge.pos;
668 let pos2 = edge2.pos;
669 let order_check = match (link_dir, top_to_bottom_hinting) {
670 (LinkDir::Prev, true) | (LinkDir::Next, false) => pos1 > pos2,
671 (LinkDir::Prev, false) | (LinkDir::Next, true) => pos1 < pos2,
672 };
673 if !order_check {
674 return None;
675 }
676 let link = edge.link(edges)?;
677 if (link.pos - prev_edge.pos).abs() > 16 {
678 let new_pos = edge2.pos;
679 edges[edge_ix].pos = new_pos;
680 if let Some(recorder) = recorder.as_mut() {
681 recorder.record_edge(
682 dim,
683 EdgeAction::Bound,
684 edge_ix,
685 None,
686 None,
687 None,
688 None,
689 None,
690 );
691 }
692 }
693 Some(())
694}
695
696fn latin_remaining_bounds(edges: &[Edge], edge_ix: usize) -> [Option<usize>; 2] {
697 let lower_bound_ix = edge_ix.checked_sub(1);
698 let upper_bound_ix = (edge_ix + 1 < edges.len()
699 && edges[edge_ix + 1].flags.contains(TopoFlags::DONE))
700 .then_some(edge_ix + 1);
701 [lower_bound_ix, upper_bound_ix]
702}
703
704fn find_bounding_completed_edges(edges: &[Edge], ix: usize) -> [Option<usize>; 2] {
707 let before_ix = edges
708 .get(..ix)
709 .unwrap_or_default()
710 .iter()
711 .enumerate()
712 .rev()
713 .filter_map(|(ix, edge)| edge.flags.contains(TopoFlags::DONE).then_some(ix))
714 .next();
715 let after_ix = edges
716 .iter()
717 .enumerate()
718 .skip(ix + 1)
719 .filter_map(|(ix, edge)| edge.flags.contains(TopoFlags::DONE).then_some(ix))
720 .next();
721 [before_ix, after_ix]
722}
723
724fn snap_width(widths: &[ScaledWidth], width: i32) -> i32 {
728 let (_, ref_width) =
729 widths
730 .iter()
731 .fold((64 + 32 + 2, width), |(best_dist, ref_width), candidate| {
732 let dist = (width - candidate.scaled).abs();
733 if dist < best_dist {
734 (dist, candidate.scaled)
735 } else {
736 (best_dist, ref_width)
737 }
738 });
739 let scaled = pix_round(ref_width);
740 if width >= ref_width {
741 if width < scaled + 48 {
742 ref_width
743 } else {
744 width
745 }
746 } else if width > scaled - 48 {
747 ref_width
748 } else {
749 width
750 }
751}
752
753fn stem_width(
757 metrics: &ScaledAxisMetrics,
758 group: ScriptGroup,
759 scale: &Scale,
760 width: i32,
761 base_delta: i32,
762 base_flags: TopoFlags,
763 stem_flags: TopoFlags,
764) -> i32 {
765 if !scale.flags.contains(ScaleFlags::STEM_ADJUST)
766 || (group == ScriptGroup::Default && metrics.width_metrics.is_extra_light)
767 {
768 return width;
769 }
770 let is_vertical = metrics.dim == Dimension::Vertical;
771 let sign = if width < 0 { -1 } else { 1 };
772 let mut dist = width.abs();
773 if (is_vertical && !scale.flags.contains(ScaleFlags::VERTICAL_SNAP))
774 || (!is_vertical && !scale.flags.contains(ScaleFlags::HORIZONTAL_SNAP))
775 {
776 if group == ScriptGroup::Default {
778 if stem_flags.contains(TopoFlags::SERIF) && is_vertical && (dist < 3 * 64) {
779 return dist * sign;
781 } else if base_flags.contains(TopoFlags::ROUND) {
782 if dist < 80 {
783 dist = 64;
784 }
785 } else if dist < 56 {
786 dist = 56;
787 }
788 }
789 if !metrics.widths.is_empty() {
790 let min_width = metrics.widths[0].scaled;
792 let delta = (dist - min_width).abs();
793 if delta < 40 {
794 dist = min_width.max(48);
795 return dist * sign;
796 }
797 if group == ScriptGroup::Default {
798 if dist < 3 * 64 {
801 let delta = dist & 63;
802 dist &= -64;
803 if delta < 10 {
804 dist += delta;
805 } else if delta < 32 {
806 dist += 10;
807 } else if delta < 54 {
808 dist += 54;
809 } else {
810 dist += delta;
811 }
812 } else {
813 let mut new_base_delta = 0;
814 if (width > 0 && base_delta > 0) || (width < 0 && base_delta < 0) {
815 if scale.size < 10.0 {
816 new_base_delta = base_delta;
817 } else if scale.size < 30.0 {
818 new_base_delta = (base_delta * (30.0 - scale.size) as i32) / 20;
819 }
820 }
821 dist = (dist - new_base_delta.abs() + 32) & !63;
822 }
823 }
824 }
825 if group != ScriptGroup::Default {
826 if dist < 54 {
829 dist += (54 - dist) / 2;
830 } else if dist < 3 * 64 {
831 let delta = dist & 63;
832 dist &= -64;
833 if delta < 10 {
834 dist += delta;
835 } else if delta < 22 {
836 dist += 10;
837 } else if delta < 42 {
838 dist += delta;
839 } else if delta < 54 {
840 dist += 54;
841 } else {
842 dist += delta;
843 }
844 }
845 }
846 } else {
847 let original_dist = dist;
849 dist = snap_width(&metrics.widths, dist);
850 if is_vertical {
851 if dist >= 64 {
853 dist = (dist + 16) & !63;
854 } else {
855 dist = 64;
856 }
857 } else if scale.flags.contains(ScaleFlags::MONO) {
858 if dist < 64 {
861 dist = 64;
862 } else {
863 dist = (dist + 32) & !63;
864 }
865 } else {
866 if dist < 48 {
869 dist = (dist + 64) >> 1;
870 } else if dist < 128 {
871 dist = (dist + 22) & !63;
874 if group == ScriptGroup::Default {
875 let delta = (dist - original_dist).abs();
877 if delta >= 16 {
878 dist = original_dist;
879 if dist < 48 {
880 dist = (dist + 64) >> 1;
881 }
882 }
883 }
884 } else {
885 dist = (dist + 32) & !63;
887 }
888 }
889 }
890 dist * sign
891}
892
893fn align_linked_edge(
897 axis: &mut Axis,
898 metrics: &ScaledAxisMetrics,
899 group: ScriptGroup,
900 scale: &Scale,
901 base_edge_ix: usize,
902 stem_edge_ix: usize,
903 mut recorder: Option<&mut HintsRecorder>,
904) {
905 let edges = axis.edges.as_mut_slice();
906 let base_edge = &edges[base_edge_ix];
907 let stem_edge = &edges[stem_edge_ix];
908 let width = stem_edge.opos - base_edge.opos;
909 let base_delta = base_edge.pos - base_edge.opos;
910 let fitted_width = stem_width(
911 metrics,
912 group,
913 scale,
914 width,
915 base_delta,
916 base_edge.flags,
917 stem_edge.flags,
918 );
919 edges[stem_edge_ix].pos = base_edge.pos + fitted_width;
920 if let Some(recorder) = recorder.as_mut() {
921 recorder.record_edge(
922 axis.dim,
923 EdgeAction::Link,
924 base_edge_ix,
925 Some(stem_edge_ix),
926 None,
927 None,
928 None,
929 None,
930 );
931 }
932}
933
934fn align_serif_edge(axis: &mut Axis, base_edge_ix: usize, serif_edge_ix: usize) {
938 let edges = axis.edges.as_mut_slice();
939 let base_edge = &edges[base_edge_ix];
940 let serif_edge = &edges[serif_edge_ix];
941 edges[serif_edge_ix].pos = base_edge.pos + (serif_edge.opos - base_edge.opos);
942}
943
944fn hint_normal_stem_cjk(
948 axis: &mut Axis,
949 metrics: &ScaledAxisMetrics,
950 group: ScriptGroup,
951 scale: &Scale,
952 edge_ix: usize,
953 edge2_ix: usize,
954 anchor: i32,
955) -> i32 {
956 const MAX_HORIZONTAL_GAP: i32 = 9;
957 const MAX_VERTICAL_GAP: i32 = 15;
958 const MAX_DELTA_ABS: i32 = 14;
959 let edge = axis.edges[edge_ix];
960 let edge2 = axis.edges[edge2_ix];
961 let do_stem_adjust = scale.flags.contains(ScaleFlags::STEM_ADJUST);
962 let threshold_delta = if do_stem_adjust {
963 0
964 } else {
965 let delta = if axis.dim == Dimension::Vertical {
966 MAX_HORIZONTAL_GAP
967 } else {
968 MAX_VERTICAL_GAP
969 };
970 if edge.flags.contains(TopoFlags::ROUND) && edge2.flags.contains(TopoFlags::ROUND) {
971 delta
972 } else {
973 delta / 3
974 }
975 };
976 let threshold = 64 - threshold_delta;
977 let original_len = edge2.opos - edge.opos;
978 let cur_len = stem_width(
979 metrics,
980 group,
981 scale,
982 original_len,
983 0,
984 edge.flags,
985 edge2.flags,
986 );
987 let original_center = (edge.opos + edge2.opos) / 2 + anchor;
988 let cur_pos1 = original_center - cur_len / 2;
989 let cur_pos2 = cur_pos1 + cur_len;
990 let mut finish = |mut delta: i32| {
991 if !do_stem_adjust {
992 delta = delta.clamp(-MAX_DELTA_ABS, MAX_DELTA_ABS);
993 }
994 let adjustment = cur_pos1 + delta;
995 if edge.opos < edge2.opos {
996 axis.edges[edge_ix].pos = adjustment;
997 axis.edges[edge2_ix].pos = adjustment + cur_len;
998 } else {
999 axis.edges[edge2_ix].pos = adjustment;
1000 axis.edges[edge_ix].pos = adjustment + cur_len;
1001 }
1002 delta
1003 };
1004 let mut d_off1 = cur_pos1 - pix_floor(cur_pos1);
1005 let mut d_off2 = cur_pos2 - pix_floor(cur_pos2);
1006 let mut delta = 0;
1007 if d_off1 == 0 || d_off2 == 0 {
1008 return finish(delta);
1009 }
1010 let mut u_off1 = 64 - d_off1;
1011 let mut u_off2 = 64 - d_off2;
1012 if cur_len <= threshold {
1013 if d_off2 < cur_len {
1014 delta = if u_off1 <= d_off2 { u_off1 } else { -d_off2 };
1015 }
1016 return finish(delta);
1017 }
1018 if threshold < 64
1019 && (d_off1 >= threshold
1020 || u_off1 >= threshold
1021 || d_off2 >= threshold
1022 || u_off2 >= threshold)
1023 {
1024 return finish(delta);
1025 }
1026 let mut offset = cur_len & 63;
1027 if offset < 32 {
1028 if u_off1 <= offset || d_off2 <= offset {
1029 return finish(delta);
1030 }
1031 } else {
1032 offset = 64 - threshold;
1033 }
1034 d_off1 = threshold - u_off1;
1035 u_off1 -= offset;
1036 u_off2 = threshold - d_off2;
1037 d_off2 -= offset;
1038 if d_off1 <= u_off1 {
1039 u_off1 = -d_off1;
1040 }
1041 if d_off2 <= u_off2 {
1042 u_off2 = -d_off2;
1043 }
1044 if u_off1.abs() <= u_off2.abs() {
1045 delta = u_off1;
1046 } else {
1047 delta = u_off2;
1048 }
1049 finish(delta)
1050}
1051
1052#[cfg(test)]
1053mod tests {
1054 use super::{
1055 super::super::{
1056 metrics,
1057 outline::Outline,
1058 shape::{Shaper, ShaperMode},
1059 style, topo,
1060 },
1061 *,
1062 };
1063 use crate::{attribute::Style, MetadataProvider};
1064 use raw::{types::GlyphId, FontRef, TableProvider};
1065
1066 #[test]
1067 fn edge_hinting_default() {
1068 let expected_h_edges = [
1069 (0, TopoFlags::DONE | TopoFlags::ROUND),
1070 (133, TopoFlags::DONE),
1071 (187, TopoFlags::DONE),
1072 (192, TopoFlags::DONE | TopoFlags::ROUND),
1073 ];
1074 let expected_v_edges = [
1075 (-256, TopoFlags::DONE),
1076 (463, TopoFlags::DONE),
1077 (576, TopoFlags::DONE | TopoFlags::ROUND | TopoFlags::SERIF),
1078 (633, TopoFlags::DONE),
1079 ];
1080 check_edges(
1081 font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS,
1082 GlyphId::new(9),
1083 style::StyleClass::HEBR,
1084 &expected_h_edges,
1085 &expected_v_edges,
1086 );
1087 }
1088
1089 #[test]
1090 fn edge_hinting_cjk() {
1091 let expected_h_edges = [
1092 (128, TopoFlags::DONE),
1093 (193, TopoFlags::DONE),
1094 (473, TopoFlags::NORMAL),
1095 (594, TopoFlags::NORMAL),
1096 (704, TopoFlags::DONE),
1097 (673, TopoFlags::DONE),
1098 (767, TopoFlags::DONE),
1099 (832, TopoFlags::DONE),
1100 (896, TopoFlags::DONE),
1101 ];
1102 let expected_v_edges = [
1103 (-64, TopoFlags::DONE | TopoFlags::ROUND),
1104 (15, TopoFlags::ROUND),
1105 (142, TopoFlags::ROUND),
1106 (546, TopoFlags::DONE),
1107 (624, TopoFlags::DONE),
1108 (576, TopoFlags::DONE),
1109 (720, TopoFlags::DONE),
1110 (768, TopoFlags::DONE),
1111 (799, TopoFlags::ROUND),
1112 ];
1113 check_edges(
1114 font_test_data::NOTOSERIFTC_AUTOHINT_METRICS,
1115 GlyphId::new(9),
1116 style::StyleClass::HANI,
1117 &expected_h_edges,
1118 &expected_v_edges,
1119 );
1120 }
1121
1122 fn check_edges(
1123 font_data: &[u8],
1124 glyph_id: GlyphId,
1125 class: usize,
1126 expected_h_edges: &[(i32, TopoFlags)],
1127 expected_v_edges: &[(i32, TopoFlags)],
1128 ) {
1129 let font = FontRef::new(font_data).unwrap();
1130 let shaper = Shaper::new(&font, ShaperMode::Nominal);
1131 let class = &style::STYLE_CLASSES[class];
1132 let unscaled_metrics =
1133 metrics::compute_unscaled_style_metrics(&shaper, &[], class, Default::default());
1134 let scale = metrics::Scale::new(
1135 16.0,
1136 font.head().unwrap().units_per_em() as i32,
1137 Style::Normal,
1138 Default::default(),
1139 class.script.group,
1140 );
1141 let scaled_metrics =
1142 metrics::scale_style_metrics(&unscaled_metrics, scale, Default::default());
1143 let glyphs = font.outline_glyphs();
1144 let glyph = glyphs.get(glyph_id).unwrap();
1145 let mut outline = Outline::default();
1146 outline.fill(&glyph, &[], Default::default()).unwrap();
1147 let mut axes = [
1148 Axis::new(Dimension::Horizontal, outline.orientation),
1149 Axis::new(Dimension::Vertical, outline.orientation),
1150 ];
1151 for axis in axes.iter_mut() {
1152 topo::compute_segments(&mut outline, axis, class.script.group);
1153 let dim = axis.dim;
1154 topo::link_segments(
1155 &outline,
1156 axis,
1157 scaled_metrics.axes[dim].scale,
1158 class.script.group,
1159 unscaled_metrics.axes[dim].max_width(),
1160 );
1161 topo::compute_edges(
1162 axis,
1163 &scaled_metrics.axes[0],
1164 class.script.hint_top_to_bottom,
1165 scaled_metrics.axes[1].scale,
1166 class.script.group,
1167 );
1168 if dim == Dimension::Vertical {
1169 topo::compute_blue_edges(
1170 axis,
1171 &scale,
1172 &unscaled_metrics.axes[dim].blues,
1173 &scaled_metrics.axes[dim].blues,
1174 class.script.group,
1175 );
1176 }
1177 hint_edges(
1178 axis,
1179 &scaled_metrics.axes[dim],
1180 class.script.group,
1181 &scale,
1182 class.script.hint_top_to_bottom,
1183 None,
1184 );
1185 }
1186 let h_edges = axes[Dimension::Horizontal]
1188 .edges
1189 .iter()
1190 .map(|edge| (edge.pos, edge.flags))
1191 .collect::<Vec<_>>();
1192 let v_edges = axes[Dimension::Vertical]
1193 .edges
1194 .iter()
1195 .map(|edge| (edge.pos, edge.flags))
1196 .collect::<Vec<_>>();
1197 assert_eq!(h_edges, expected_h_edges);
1198 assert_eq!(v_edges, expected_v_edges);
1199 }
1200}