1use super::super::{
10 derived_constant,
11 metrics::fixed_div,
12 outline::Outline,
13 style::ScriptGroup,
14 topo::{Axis, Dimension, Segment, TopoFlags},
15};
16use raw::tables::glyf::PointFlags;
17
18const MAX_SCORE: i32 = 32000;
21const MIN_SCORE: i32 = -32000;
22
23pub(crate) fn compute_segments(
27 outline: &mut Outline,
28 axis: &mut Axis,
29 _group: ScriptGroup,
30) -> bool {
31 assign_point_uvs(outline, axis.dim);
32 if !build_segments(outline, axis) {
33 return false;
34 }
35 adjust_segment_heights(outline, axis);
36 true
42}
43
44pub(crate) fn link_segments(
50 outline: &Outline,
51 axis: &mut Axis,
52 scale: i32,
53 group: ScriptGroup,
54 max_width: Option<i32>,
55) {
56 if group == ScriptGroup::Default {
57 link_segments_default(outline, axis, max_width);
58 } else {
59 link_segments_cjk(outline, axis, scale)
60 }
61}
62
63fn link_segments_default(outline: &Outline, axis: &mut Axis, max_width: Option<i32>) {
69 let max_width = max_width.unwrap_or_default();
70 let len_threshold = derived_constant(outline.units_per_em, 8).max(1);
72 let len_score = derived_constant(outline.units_per_em, 6000);
74 let dist_score = 3000;
77 let segments = axis.segments.as_mut_slice();
79 for ix1 in 0..segments.len() {
80 let seg1 = segments[ix1];
81 if seg1.dir != axis.major_dir {
82 continue;
83 }
84 let pos1 = seg1.pos as i32;
85 for ix2 in 0..segments.len() {
88 let seg1 = segments[ix1];
89 let seg2 = segments[ix2];
90 let pos2 = seg2.pos as i32;
91 if seg1.dir.is_opposite(seg2.dir) && pos2 > pos1 {
92 let min = seg1.min_coord.max(seg2.min_coord) as i32;
95 let max = seg1.max_coord.min(seg2.max_coord) as i32;
96 let len = max - min;
99 if len >= len_threshold {
100 let dist = pos2 - pos1;
114 let dist_demerit = if max_width != 0 {
115 let delta = (dist << 10) / max_width - (1 << 10);
117 if delta > 10_000 {
118 MAX_SCORE
119 } else if delta > 0 {
120 delta * delta / dist_score
121 } else {
122 0
123 }
124 } else {
125 dist
126 };
127 let score = dist_demerit + len_score / len;
128 if score < seg1.score {
129 let seg1 = &mut segments[ix1];
130 seg1.score = score;
131 seg1.link_ix = Some(ix2 as u16);
132 }
133 if score < seg2.score {
134 let seg2 = &mut segments[ix2];
135 seg2.score = score;
136 seg2.link_ix = Some(ix1 as u16);
137 }
138 }
139 }
140 }
141 }
142 for ix1 in 0..segments.len() {
145 let Some(ix2) = segments[ix1].link_ix else {
146 continue;
147 };
148 let seg2_link = segments[ix2 as usize].link_ix;
149 if seg2_link != Some(ix1 as u16) {
150 let seg1 = &mut segments[ix1];
151 seg1.link_ix = None;
152 seg1.serif_ix = seg2_link;
153 }
154 }
155}
156
157fn link_segments_cjk(outline: &Outline, axis: &mut Axis, scale: i32) {
161 let len_threshold = derived_constant(outline.units_per_em, 8);
163 let dist_threshold = fixed_div(64 * 3, scale);
164 let segments = axis.segments.as_mut_slice();
166 for ix1 in 0..segments.len() {
167 let seg1 = segments[ix1];
168 if seg1.dir != axis.major_dir {
169 continue;
170 }
171 let pos1 = seg1.pos as i32;
172 for ix2 in 0..segments.len() {
175 let seg1 = segments[ix1];
176 let seg2 = segments[ix2];
177 if ix1 == ix2 || !seg1.dir.is_opposite(seg2.dir) {
178 continue;
179 }
180 let pos2 = seg2.pos as i32;
181 let dist = pos2 - pos1;
182 if dist < 0 {
183 continue;
184 }
185 let min = seg1.min_coord.max(seg2.min_coord) as i32;
188 let max = seg1.max_coord.min(seg2.max_coord) as i32;
189 let len = max - min;
192 if len >= len_threshold {
193 let check_seg = |seg: &Segment| {
194 (dist * 8 < seg.score * 9) && (dist * 8 < seg.score * 7 || seg.len < len)
197 };
198 if check_seg(&seg1) {
199 let seg = &mut segments[ix1];
200 seg.score = dist;
201 seg.len = len;
202 seg.link_ix = Some(ix2 as _);
203 }
204 if check_seg(&seg2) {
205 let seg = &mut segments[ix2];
206 seg.score = dist;
207 seg.len = len;
208 seg.link_ix = Some(ix1 as _);
209 }
210 }
211 }
212 }
213 for ix1 in 0..segments.len() {
216 let seg1 = segments[ix1];
217 if seg1.score >= dist_threshold {
218 continue;
219 }
220 let Some(link1) = seg1.link(segments).copied() else {
221 continue;
222 };
223 let link1_ix = seg1.link_ix.unwrap() as usize;
225 if link1.link_ix != Some(ix1 as u16) || link1.pos <= seg1.pos {
226 continue;
227 }
228 for ix2 in 0..segments.len() {
229 let seg2 = segments[ix2];
230 if seg2.pos > seg1.pos || ix1 == ix2 {
231 continue;
232 }
233 let Some(link2) = seg2.link(segments).copied() else {
234 continue;
235 };
236 if link2.link_ix != Some(ix2 as u16) || link2.pos < link1.pos {
237 continue;
238 }
239 if seg1.pos == seg2.pos && link1.pos == link2.pos {
240 continue;
241 }
242 if seg2.score <= seg1.score || seg1.score * 4 <= seg2.score {
243 continue;
244 }
245 if seg1.len >= seg2.len * 3 {
246 let link2_ix = seg2.link_ix.unwrap() as usize;
248 for seg in segments.iter_mut() {
249 let link_ix = seg.link_ix;
250 if link_ix == Some(ix2 as u16) {
251 seg.link_ix = None;
252 seg.serif_ix = Some(link1_ix as u16);
253 } else if link_ix == Some(link2_ix as u16) {
254 seg.link_ix = None;
255 seg.serif_ix = Some(ix1 as u16);
256 }
257 }
258 } else {
259 segments[ix1].link_ix = None;
260 segments[link1_ix].link_ix = None;
261 break;
262 }
263 }
264 }
265 for ix1 in 0..segments.len() {
266 let seg1 = segments[ix1];
267 let Some(seg2) = seg1.link(segments).copied() else {
268 continue;
269 };
270 if seg2.link_ix != Some(ix1 as u16) {
271 segments[ix1].link_ix = None;
272 if seg2.score < dist_threshold || seg1.score < seg2.score * 4 {
273 segments[ix1].serif_ix = seg2.link_ix;
274 }
275 }
276 }
277}
278
279fn assign_point_uvs(outline: &mut Outline, dim: Dimension) {
284 if dim == Dimension::Horizontal {
285 for point in &mut outline.points {
286 point.u = point.fx;
287 point.v = point.fy;
288 }
289 } else {
290 for point in &mut outline.points {
291 point.u = point.fy;
292 point.v = point.fx;
293 }
294 }
295}
296
297fn build_segments(outline: &mut Outline, axis: &mut Axis) -> bool {
301 let flat_threshold = outline.units_per_em / 14;
302 axis.segments.clear();
303 let major_dir = axis.major_dir.normalize();
304 let mut segment_dir = major_dir;
305 let points = outline.points.as_mut_slice();
306 for contour in &outline.contours {
307 let is_single_point_contour = contour.range().len() == 1;
308 let mut point_ix = contour.first();
309 let mut last_ix = contour.prev(point_ix);
310 let mut state = State::default();
311 let mut prev_state = state;
312 let mut prev_segment_ix: Option<usize> = None;
313 let mut segment_ix = 0;
314 if points[point_ix].out_dir.is_same_axis(major_dir)
317 && points[last_ix].out_dir.is_same_axis(major_dir)
318 {
319 last_ix = point_ix;
320 loop {
321 point_ix = contour.prev(point_ix);
322 if !points[point_ix].out_dir.is_same_axis(major_dir) {
323 point_ix = contour.next(point_ix);
324 break;
325 }
326 if point_ix == last_ix {
327 break;
328 }
329 }
330 }
331 last_ix = point_ix;
332 let mut on_edge = false;
333 let mut passed = false;
334 loop {
335 if on_edge {
336 let point = points[point_ix];
338 state.min_pos = state.min_pos.min(point.u);
339 state.max_pos = state.max_pos.max(point.u);
340 let v = point.v;
342 if v < state.min_coord {
343 state.min_coord = v;
344 state.min_flags = point.flags;
345 }
346 if v > state.max_coord {
347 state.max_coord = v;
348 state.max_flags = point.flags;
349 }
350 if point.is_on_curve() {
352 state.min_on_coord = state.min_on_coord.min(point.v);
353 state.max_on_coord = state.max_on_coord.max(point.v);
354 }
355 if point.out_dir != segment_dir || point_ix == last_ix {
356 prev_segment_ix.take_if(|idx| {
357 axis.segments[segment_ix].first_ix != axis.segments[*idx].last_ix
358 });
359 if let Some(prev_segment_ix) = prev_segment_ix {
360 let prev_segment = &mut axis.segments[prev_segment_ix];
362 if prev_segment.last_point(points).in_dir == point.in_dir {
363 state.min_pos = prev_state.min_pos.min(state.min_pos);
366 state.max_pos = prev_state.max_pos.max(state.max_pos);
367 if prev_state.min_coord < state.min_coord {
368 state.min_coord = prev_state.min_coord;
369 state.min_flags = prev_state.min_flags;
370 }
371 if prev_state.max_coord > state.max_coord {
372 state.max_coord = prev_state.max_coord;
373 state.max_flags = prev_state.max_flags;
374 }
375 state.min_on_coord = prev_state.min_on_coord.min(state.min_on_coord);
376 state.max_on_coord = prev_state.max_on_coord.max(state.max_on_coord);
377 prev_segment.last_ix = point_ix as u16;
378 state.apply_to_segment(prev_segment, flat_threshold);
379 } else {
380 if (prev_state.max_coord - prev_state.min_coord).abs()
383 > (state.max_coord - state.min_coord).abs()
384 {
385 prev_state.min_pos = prev_state.min_pos.min(state.min_pos);
387 prev_state.max_pos = prev_state.max_pos.max(state.max_pos);
388 prev_segment.last_ix = point_ix as u16;
389 prev_segment.pos =
390 compute_mid_pos(prev_state.min_pos, prev_state.max_pos);
391 prev_segment.delta =
392 compute_mid_delta(prev_state.min_pos, prev_state.max_pos);
393 } else {
394 state.min_pos = state.min_pos.min(prev_state.min_pos);
396 state.max_pos = state.max_pos.max(prev_state.max_pos);
397 let mut segment = axis.segments[segment_ix];
398 segment.last_ix = point_ix as u16;
399 state.apply_to_segment(&mut segment, flat_threshold);
400 axis.segments[prev_segment_ix] = segment;
401 prev_state = state;
402 }
403 }
404 axis.segments.pop();
405 } else {
406 let segment = &mut axis.segments[segment_ix];
409 segment.last_ix = point_ix as u16;
410 state.apply_to_segment(segment, flat_threshold);
411 prev_segment_ix = Some(segment_ix);
412 prev_state = state;
413 };
414 on_edge = false;
415 }
416 }
417 if point_ix == last_ix {
418 if passed {
419 break;
420 }
421 passed = true;
422 }
423 let point = points[point_ix];
424 if !on_edge && (point.out_dir.is_same_axis(major_dir) || is_single_point_contour) {
425 if axis.segments.len() > 1000 {
426 axis.segments.clear();
427 return false;
428 }
429 segment_ix = axis.segments.len();
430 segment_dir = point.out_dir;
431 let mut segment = Segment {
432 dir: segment_dir,
433 first_ix: point_ix as u16,
434 last_ix: point_ix as u16,
435 score: MAX_SCORE,
436 ..Default::default()
437 };
438 state.min_pos = point.u;
439 state.max_pos = point.u;
440 state.min_coord = point.v;
441 state.max_coord = point.v;
442 state.min_flags = point.flags;
443 state.max_flags = point.flags;
444 if !point.is_on_curve() {
445 state.min_on_coord = MAX_SCORE;
446 state.max_on_coord = MIN_SCORE;
447 } else {
448 state.min_on_coord = point.v;
449 state.max_on_coord = point.v;
450 }
451 on_edge = true;
452 if is_single_point_contour {
453 segment.pos = state.min_pos as i16;
454 if !point.is_on_curve() {
455 segment.flags |= TopoFlags::ROUND;
456 }
457 segment.min_coord = point.v as i16;
458 segment.max_coord = point.v as i16;
459 segment.height = 0;
460 on_edge = false;
461 }
462 axis.segments.push(segment);
463 }
464 point_ix = contour.next(point_ix);
465 }
466 }
467 true
468}
469
470fn adjust_segment_heights(outline: &mut Outline, axis: &mut Axis) {
475 let points = outline.points.as_slice();
476 for segment in &mut axis.segments {
477 let first = segment.first_point(points);
478 let last = segment.last_point(points);
479 let prev = &points[first.prev()];
480 let next = &points[last.next()];
481 if first.v < last.v {
482 if prev.v < first.v {
483 segment.adjust_height(first.v, prev.v);
484 }
485 if next.v > last.v {
486 segment.adjust_height(next.v, last.v);
487 }
488 } else {
489 if prev.v > first.v {
490 segment.adjust_height(prev.v, first.v);
491 }
492 if next.v < last.v {
493 segment.adjust_height(last.v, next.v);
494 }
495 }
496 }
497}
498
499fn _detect_round_segments_cjk(outline: &mut Outline, axis: &mut Axis) {
504 let points = outline.points.as_slice();
505 for segment in &mut axis.segments {
508 segment.flags &= !TopoFlags::ROUND;
509 let mut point_ix = segment.first();
510 let last_ix = segment.last();
511 let first_point = &points[point_ix];
512 let mut is_prev_on_curve = first_point.is_on_curve();
513 point_ix = first_point.next();
514 loop {
515 let point = &points[point_ix];
516 let is_on_curve = point.is_on_curve();
517 if is_prev_on_curve && is_on_curve {
518 break;
520 }
521 is_prev_on_curve = is_on_curve;
522 point_ix = point.next();
523 if point_ix == last_ix {
524 segment.flags |= TopoFlags::ROUND;
527 break;
528 }
529 }
530 }
531}
532
533#[derive(Copy, Clone)]
538struct State {
539 min_pos: i32,
540 max_pos: i32,
541 min_coord: i32,
542 max_coord: i32,
543 min_flags: PointFlags,
544 max_flags: PointFlags,
545 min_on_coord: i32,
546 max_on_coord: i32,
547}
548
549impl Default for State {
550 fn default() -> Self {
551 Self {
553 min_pos: MAX_SCORE,
554 max_pos: MIN_SCORE,
555 min_coord: MAX_SCORE,
556 max_coord: MIN_SCORE,
557 min_flags: PointFlags::default(),
558 max_flags: PointFlags::default(),
559 min_on_coord: MAX_SCORE,
560 max_on_coord: MIN_SCORE,
561 }
562 }
563}
564
565impl State {
566 fn apply_to_segment(&self, segment: &mut Segment, flat_threshold: i32) {
567 segment.pos = compute_mid_pos(self.min_pos, self.max_pos);
568 segment.delta = compute_mid_delta(self.min_pos, self.max_pos);
569 if (!self.min_flags.is_on_curve() || !self.max_flags.is_on_curve())
573 && (self.max_on_coord.wrapping_sub(self.min_on_coord)) < flat_threshold
574 {
575 segment.flags |= TopoFlags::ROUND;
576 }
577 segment.min_coord = self.min_coord as i16;
578 segment.max_coord = self.max_coord as i16;
579 segment.height = self.max_coord.wrapping_sub(self.min_coord) as i16;
580 }
581}
582
583fn compute_mid_pos(min: i32, max: i32) -> i16 {
585 ((min.wrapping_add(max)) >> 1) as i16
586}
587
588fn compute_mid_delta(min: i32, max: i32) -> i16 {
590 ((max.wrapping_sub(min)) >> 1) as i16
591}
592
593#[cfg(test)]
594mod tests {
595 use super::{super::super::outline::Direction, *};
596 use crate::MetadataProvider;
597 use raw::{types::GlyphId, FontRef};
598
599 #[test]
600 fn horizontal_segments() {
601 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
602 let glyphs = font.outline_glyphs();
603 let glyph = glyphs.get(GlyphId::new(8)).unwrap();
604 let mut outline = Outline::default();
605 outline.fill(&glyph, &[], Default::default()).unwrap();
606 let mut axis = Axis::new(Dimension::Horizontal, outline.orientation);
607 compute_segments(&mut outline, &mut axis, ScriptGroup::Default);
608 link_segments(&outline, &mut axis, 0, ScriptGroup::Default, None);
609 let segments = retain_segment_test_fields(&axis.segments);
610 let expected = [
611 Segment {
612 flags: TopoFlags::NORMAL,
613 dir: Direction::Up,
614 pos: 55,
615 delta: 0,
616 min_coord: 26,
617 max_coord: 360,
618 height: 372,
619 link_ix: Some(3),
620 serif_ix: None,
621 ..Default::default()
622 },
623 Segment {
624 flags: TopoFlags::NORMAL,
625 dir: Direction::Up,
626 pos: 112,
627 delta: 0,
628 min_coord: 481,
629 max_coord: 504,
630 height: 34,
631 link_ix: Some(2),
632 serif_ix: None,
633 ..Default::default()
634 },
635 Segment {
636 flags: TopoFlags::NORMAL,
637 dir: Direction::Down,
638 pos: 168,
639 delta: 0,
640 min_coord: 483,
641 max_coord: 504,
642 height: 26,
643 link_ix: Some(1),
644 serif_ix: None,
645 ..Default::default()
646 },
647 Segment {
648 flags: TopoFlags::NORMAL,
649 dir: Direction::Down,
650 pos: 109,
651 delta: 0,
652 min_coord: 109,
653 max_coord: 366,
654 height: 288,
655 link_ix: Some(0),
656 serif_ix: None,
657 ..Default::default()
658 },
659 Segment {
660 flags: TopoFlags::NORMAL,
661 dir: Direction::Up,
662 pos: 453,
663 delta: 0,
664 min_coord: 169,
665 max_coord: 432,
666 height: 304,
667 link_ix: Some(7),
668 serif_ix: None,
669 ..Default::default()
670 },
671 Segment {
672 flags: TopoFlags::ROUND,
673 dir: Direction::Up,
674 pos: 62,
675 delta: 0,
676 min_coord: 517,
677 max_coord: 566,
678 height: 76,
679 link_ix: None,
680 serif_ix: None,
681 ..Default::default()
682 },
683 Segment {
684 flags: TopoFlags::ROUND,
685 dir: Direction::Down,
686 pos: 103,
687 delta: 0,
688 min_coord: 619,
689 max_coord: 647,
690 height: 41,
691 link_ix: None,
692 serif_ix: None,
693 ..Default::default()
694 },
695 Segment {
696 flags: TopoFlags::NORMAL,
697 dir: Direction::Down,
698 pos: 507,
699 delta: 0,
700 min_coord: 40,
701 max_coord: 485,
702 height: 498,
703 link_ix: Some(4),
704 serif_ix: None,
705 ..Default::default()
706 },
707 ];
708 assert_eq!(segments, &expected);
709 }
710
711 #[test]
712 fn vertical_segments() {
713 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
714 let glyphs = font.outline_glyphs();
715 let glyph = glyphs.get(GlyphId::new(8)).unwrap();
716 let mut outline = Outline::default();
717 outline.fill(&glyph, &[], Default::default()).unwrap();
718 let mut axis = Axis::new(Dimension::Vertical, outline.orientation);
719 compute_segments(&mut outline, &mut axis, ScriptGroup::Default);
720 link_segments(&outline, &mut axis, 0, ScriptGroup::Default, None);
721 let segments = retain_segment_test_fields(&axis.segments);
722 let expected = [
723 Segment {
724 flags: TopoFlags::NORMAL,
725 dir: Direction::Left,
726 pos: 0,
727 delta: 0,
728 min_coord: 85,
729 max_coord: 470,
730 height: 418,
731 link_ix: Some(2),
732 serif_ix: None,
733 ..Default::default()
734 },
735 Segment {
736 flags: TopoFlags::NORMAL,
737 dir: Direction::Right,
738 pos: 504,
739 delta: 0,
740 min_coord: 112,
741 max_coord: 168,
742 height: 56,
743 link_ix: Some(3),
744 serif_ix: None,
745 ..Default::default()
746 },
747 Segment {
748 flags: TopoFlags::NORMAL,
749 dir: Direction::Right,
750 pos: 109,
751 delta: 0,
752 min_coord: 109,
753 max_coord: 427,
754 height: 327,
755 link_ix: Some(0),
756 serif_ix: None,
757 ..Default::default()
758 },
759 Segment {
760 flags: TopoFlags::NORMAL,
761 dir: Direction::Left,
762 pos: 483,
763 delta: 0,
764 min_coord: 86,
765 max_coord: 400,
766 height: 352,
767 link_ix: Some(1),
768 serif_ix: None,
769 ..Default::default()
770 },
771 Segment {
772 flags: TopoFlags::NORMAL,
773 dir: Direction::Right,
774 pos: 647,
775 delta: 0,
776 min_coord: 76,
777 max_coord: 103,
778 height: 29,
779 link_ix: None,
780 serif_ix: Some(1),
781 ..Default::default()
782 },
783 Segment {
784 flags: TopoFlags::NORMAL,
785 dir: Direction::Right,
786 pos: 592,
787 delta: 0,
788 min_coord: 131,
789 max_coord: 437,
790 height: 346,
791 link_ix: None,
792 serif_ix: Some(1),
793 ..Default::default()
794 },
795 ];
796 assert_eq!(segments, &expected);
797 }
798
799 #[test]
800 fn cjk_horizontal_segments() {
801 let font = FontRef::new(font_test_data::NOTOSERIFTC_AUTOHINT_METRICS).unwrap();
802 let glyphs = font.outline_glyphs();
803 let glyph = glyphs.get(GlyphId::new(9)).unwrap();
804 let mut outline = Outline::default();
805 outline.fill(&glyph, &[], Default::default()).unwrap();
806 let mut axis = Axis::new(Dimension::Horizontal, outline.orientation);
807 compute_segments(&mut outline, &mut axis, ScriptGroup::Cjk);
808 link_segments(&outline, &mut axis, 67109, ScriptGroup::Cjk, None);
809 let segments = retain_segment_test_fields(&axis.segments);
810 let expected = [
811 Segment {
812 flags: TopoFlags::NORMAL,
813 dir: Direction::Down,
814 pos: 731,
815 delta: 0,
816 min_coord: 155,
817 max_coord: 676,
818 height: 524,
819 link_ix: Some(1),
820 serif_ix: None,
821 ..Default::default()
822 },
823 Segment {
824 flags: TopoFlags::NORMAL,
825 dir: Direction::Up,
826 pos: 670,
827 delta: 0,
828 min_coord: 133,
829 max_coord: 712,
830 height: 579,
831 link_ix: Some(0),
832 serif_ix: None,
833 ..Default::default()
834 },
835 Segment {
836 flags: TopoFlags::NORMAL,
837 dir: Direction::Down,
838 pos: 458,
839 delta: 0,
840 min_coord: 741,
841 max_coord: 757,
842 height: 88,
843 link_ix: None,
844 serif_ix: None,
845 ..Default::default()
846 },
847 Segment {
848 flags: TopoFlags::NORMAL,
849 dir: Direction::Down,
850 pos: 911,
851 delta: 0,
852 min_coord: -9,
853 max_coord: 791,
854 height: 821,
855 link_ix: Some(5),
856 serif_ix: None,
857 ..Default::default()
858 },
859 Segment {
860 flags: TopoFlags::NORMAL,
861 dir: Direction::Up,
862 pos: 693,
863 delta: 0,
864 min_coord: -7,
865 max_coord: 9,
866 height: 18,
867 link_ix: None,
868 serif_ix: Some(5),
869 ..Default::default()
870 },
871 Segment {
872 flags: TopoFlags::NORMAL,
873 dir: Direction::Up,
874 pos: 849,
875 delta: 0,
876 min_coord: 11,
877 max_coord: 829,
878 height: 823,
879 link_ix: Some(3),
880 serif_ix: None,
881 ..Default::default()
882 },
883 Segment {
884 flags: TopoFlags::NORMAL,
885 dir: Direction::Down,
886 pos: 569,
887 delta: 0,
888 min_coord: 547,
889 max_coord: 576,
890 height: 29,
891 link_ix: None,
892 serif_ix: None,
893 ..Default::default()
894 },
895 Segment {
896 flags: TopoFlags::NORMAL,
897 dir: Direction::Down,
898 pos: 201,
899 delta: 0,
900 min_coord: -57,
901 max_coord: 540,
902 height: 599,
903 link_ix: Some(8),
904 serif_ix: None,
905 ..Default::default()
906 },
907 Segment {
908 flags: TopoFlags::NORMAL,
909 dir: Direction::Up,
910 pos: 138,
911 delta: 0,
912 min_coord: -78,
913 max_coord: 543,
914 height: 640,
915 link_ix: Some(7),
916 serif_ix: None,
917 ..Default::default()
918 },
919 ];
920 assert_eq!(segments, &expected);
921 }
922
923 #[test]
924 fn cjk_vertical_segments() {
925 let font = FontRef::new(font_test_data::NOTOSERIFTC_AUTOHINT_METRICS).unwrap();
926 let glyphs = font.outline_glyphs();
927 let glyph = glyphs.get(GlyphId::new(9)).unwrap();
928 let mut outline = Outline::default();
929 outline.fill(&glyph, &[], Default::default()).unwrap();
930 let mut axis = Axis::new(Dimension::Vertical, outline.orientation);
931 compute_segments(&mut outline, &mut axis, ScriptGroup::Cjk);
932 link_segments(&outline, &mut axis, 67109, ScriptGroup::Cjk, None);
933 let segments = retain_segment_test_fields(&axis.segments);
934 let expected = [
935 Segment {
936 flags: TopoFlags::NORMAL,
937 dir: Direction::Right,
938 pos: 758,
939 delta: 0,
940 min_coord: 280,
941 max_coord: 545,
942 height: 288,
943 link_ix: Some(1),
944 serif_ix: None,
945 ..Default::default()
946 },
947 Segment {
948 flags: TopoFlags::NORMAL,
949 dir: Direction::Left,
950 pos: 729,
951 delta: 0,
952 min_coord: 288,
953 max_coord: 674,
954 height: 391,
955 link_ix: Some(0),
956 serif_ix: None,
957 ..Default::default()
958 },
959 Segment {
960 flags: TopoFlags::ROUND,
961 dir: Direction::Left,
962 pos: 133,
963 delta: 0,
964 min_coord: 670,
965 max_coord: 693,
966 height: 34,
967 link_ix: None,
968 serif_ix: None,
969 ..Default::default()
970 },
971 Segment {
972 flags: TopoFlags::NORMAL,
973 dir: Direction::Right,
974 pos: 757,
975 delta: 0,
976 min_coord: 393,
977 max_coord: 458,
978 height: 70,
979 link_ix: None,
980 serif_ix: Some(0),
981 ..Default::default()
982 },
983 Segment {
984 flags: TopoFlags::ROUND,
985 dir: Direction::Right,
986 pos: 3,
987 delta: 2,
988 min_coord: 727,
989 max_coord: 838,
990 height: 133,
991 link_ix: None,
992 serif_ix: None,
993 ..Default::default()
994 },
995 Segment {
996 flags: TopoFlags::NORMAL,
997 dir: Direction::Right,
998 pos: 576,
999 delta: 0,
1000 min_coord: 397,
1001 max_coord: 569,
1002 height: 177,
1003 link_ix: Some(7),
1004 serif_ix: None,
1005 ..Default::default()
1006 },
1007 Segment {
1008 flags: TopoFlags::NORMAL,
1009 dir: Direction::Left,
1010 pos: 547,
1011 delta: 0,
1012 min_coord: 387,
1013 max_coord: 569,
1014 height: 182,
1015 link_ix: None,
1016 serif_ix: Some(7),
1017 ..Default::default()
1018 },
1019 Segment {
1020 flags: TopoFlags::NORMAL,
1021 dir: Direction::Left,
1022 pos: 576,
1023 delta: 0,
1024 min_coord: 536,
1025 max_coord: 546,
1026 height: 10,
1027 link_ix: Some(5),
1028 serif_ix: None,
1029 ..Default::default()
1030 },
1031 Segment {
1032 flags: TopoFlags::ROUND,
1033 dir: Direction::Left,
1034 pos: -78,
1035 delta: 0,
1036 min_coord: 138,
1037 max_coord: 161,
1038 height: 34,
1039 link_ix: None,
1040 serif_ix: None,
1041 ..Default::default()
1042 },
1043 Segment {
1044 flags: TopoFlags::ROUND,
1045 dir: Direction::Left,
1046 pos: 788,
1047 delta: 0,
1048 min_coord: 262,
1049 max_coord: 294,
1050 height: 46,
1051 link_ix: None,
1052 serif_ix: None,
1053 ..Default::default()
1054 },
1055 ];
1056 assert_eq!(segments, &expected);
1057 }
1058
1059 fn retain_segment_test_fields(segments: &[Segment]) -> Vec<Segment> {
1062 segments
1063 .iter()
1064 .map(|segment| Segment {
1065 flags: segment.flags,
1066 dir: segment.dir,
1067 pos: segment.pos,
1068 delta: segment.delta,
1069 min_coord: segment.min_coord,
1070 max_coord: segment.max_coord,
1071 height: segment.height,
1072 link_ix: segment.link_ix,
1073 serif_ix: segment.serif_ix,
1074 ..Default::default()
1075 })
1076 .collect()
1077 }
1078
1079 #[test]
1083 fn state_apply_overflow() {
1084 let mut segment = Segment::default();
1085 let state = State {
1086 min_coord: MAX_SCORE,
1087 max_coord: MIN_SCORE,
1088 ..Default::default()
1089 };
1090 state.apply_to_segment(&mut segment, 0);
1092 }
1093
1094 #[test]
1095 fn mid_extreme_values_do_not_panic() {
1096 let _ = compute_mid_pos(i32::MIN, i32::MAX);
1098 let _ = compute_mid_pos(i32::MAX, i32::MIN);
1099 let _ = compute_mid_delta(i32::MIN, i32::MAX);
1100 let _ = compute_mid_delta(i32::MAX, i32::MIN);
1101 }
1102}