1use super::super::{
10 derived_constant,
11 metrics::fixed_div,
12 outline::Outline,
13 style::ScriptGroup,
14 topo::{Axis, Dimension, Segment},
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 == Axis::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 if prev_segment_ix.is_none()
357 || axis.segments[segment_ix].first_ix
358 != axis.segments[prev_segment_ix.unwrap()].last_ix
359 {
360 let segment = &mut axis.segments[segment_ix];
363 segment.last_ix = point_ix as u16;
364 state.apply_to_segment(segment, flat_threshold);
365 prev_segment_ix = Some(segment_ix);
366 prev_state = state;
367 } else {
368 let prev_segment = &mut axis.segments[prev_segment_ix.unwrap()];
370 if prev_segment.last_point(points).in_dir == point.in_dir {
371 state.min_pos = prev_state.min_pos.min(state.min_pos);
374 state.max_pos = prev_state.max_pos.max(state.max_pos);
375 if prev_state.min_coord < state.min_coord {
376 state.min_coord = prev_state.min_coord;
377 state.min_flags = prev_state.min_flags;
378 }
379 if prev_state.max_coord > state.max_coord {
380 state.max_coord = prev_state.max_coord;
381 state.max_flags = prev_state.max_flags;
382 }
383 state.min_on_coord = prev_state.min_on_coord.min(state.min_on_coord);
384 state.max_on_coord = prev_state.max_on_coord.max(state.max_on_coord);
385 prev_segment.last_ix = point_ix as u16;
386 state.apply_to_segment(prev_segment, flat_threshold);
387 } else {
388 if (prev_state.max_coord - prev_state.min_coord).abs()
391 > (state.max_coord - state.min_coord).abs()
392 {
393 prev_state.min_pos = prev_state.min_pos.min(state.min_pos);
395 prev_state.max_pos = prev_state.max_pos.max(state.max_pos);
396 prev_segment.last_ix = point_ix as u16;
397 prev_segment.pos =
398 ((prev_state.min_pos + prev_state.max_pos) >> 1) as i16;
399 prev_segment.delta =
400 ((prev_state.max_pos - prev_state.min_pos) >> 1) as i16;
401 } else {
402 state.min_pos = state.min_pos.min(prev_state.min_pos);
404 state.max_pos = state.max_pos.max(prev_state.max_pos);
405 let mut segment = axis.segments[segment_ix];
406 segment.last_ix = point_ix as u16;
407 state.apply_to_segment(&mut segment, flat_threshold);
408 axis.segments[prev_segment_ix.unwrap()] = segment;
409 prev_state = state;
410 }
411 }
412 axis.segments.pop();
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 |= Segment::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 fn adjust_height(segment: &mut Segment, v1: i32, v2: i32) {
480 segment.height = (segment.height as i32 + ((v1 - v2) >> 1)) as i16;
481 }
482 let prev = &points[first.prev()];
483 let next = &points[last.next()];
484 if first.v < last.v {
485 if prev.v < first.v {
486 adjust_height(segment, first.v, prev.v);
487 }
488 if next.v > last.v {
489 adjust_height(segment, next.v, last.v);
490 }
491 } else {
492 if prev.v > first.v {
493 adjust_height(segment, prev.v, first.v);
494 }
495 if next.v < last.v {
496 adjust_height(segment, last.v, next.v);
497 }
498 }
499 }
500}
501
502fn _detect_round_segments_cjk(outline: &mut Outline, axis: &mut Axis) {
507 let points = outline.points.as_slice();
508 for segment in &mut axis.segments {
511 segment.flags &= !Segment::ROUND;
512 let mut point_ix = segment.first();
513 let last_ix = segment.last();
514 let first_point = &points[point_ix];
515 let mut is_prev_on_curve = first_point.is_on_curve();
516 point_ix = first_point.next();
517 loop {
518 let point = &points[point_ix];
519 let is_on_curve = point.is_on_curve();
520 if is_prev_on_curve && is_on_curve {
521 break;
523 }
524 is_prev_on_curve = is_on_curve;
525 point_ix = point.next();
526 if point_ix == last_ix {
527 segment.flags |= Segment::ROUND;
530 break;
531 }
532 }
533 }
534}
535
536#[derive(Copy, Clone)]
541struct State {
542 min_pos: i32,
543 max_pos: i32,
544 min_coord: i32,
545 max_coord: i32,
546 min_flags: PointFlags,
547 max_flags: PointFlags,
548 min_on_coord: i32,
549 max_on_coord: i32,
550}
551
552impl Default for State {
553 fn default() -> Self {
554 Self {
556 min_pos: MAX_SCORE,
557 max_pos: MIN_SCORE,
558 min_coord: MAX_SCORE,
559 max_coord: MIN_SCORE,
560 min_flags: PointFlags::default(),
561 max_flags: PointFlags::default(),
562 min_on_coord: MAX_SCORE,
563 max_on_coord: MIN_SCORE,
564 }
565 }
566}
567
568impl State {
569 fn apply_to_segment(&self, segment: &mut Segment, flat_threshold: i32) {
570 segment.pos = ((self.min_pos + self.max_pos) >> 1) as i16;
571 segment.delta = ((self.max_pos - self.min_pos) >> 1) as i16;
572 if (!self.min_flags.is_on_curve() || !self.max_flags.is_on_curve())
576 && (self.max_on_coord - self.min_on_coord) < flat_threshold
577 {
578 segment.flags |= Segment::ROUND;
579 }
580 segment.min_coord = self.min_coord as i16;
581 segment.max_coord = self.max_coord as i16;
582 segment.height = segment.max_coord - segment.min_coord;
583 }
584}
585
586#[cfg(test)]
587mod tests {
588 use super::{super::super::outline::Direction, *};
589 use crate::MetadataProvider;
590 use raw::{types::GlyphId, FontRef};
591
592 #[test]
593 fn horizontal_segments() {
594 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
595 let glyphs = font.outline_glyphs();
596 let glyph = glyphs.get(GlyphId::new(8)).unwrap();
597 let mut outline = Outline::default();
598 outline.fill(&glyph, Default::default()).unwrap();
599 let mut axis = Axis::new(Axis::HORIZONTAL, outline.orientation);
600 compute_segments(&mut outline, &mut axis, ScriptGroup::Default);
601 link_segments(&outline, &mut axis, 0, ScriptGroup::Default, None);
602 let segments = retain_segment_test_fields(&axis.segments);
603 let expected = [
604 Segment {
605 flags: 0,
606 dir: Direction::Up,
607 pos: 55,
608 delta: 0,
609 min_coord: 26,
610 max_coord: 360,
611 height: 372,
612 link_ix: Some(3),
613 serif_ix: None,
614 ..Default::default()
615 },
616 Segment {
617 flags: 0,
618 dir: Direction::Up,
619 pos: 112,
620 delta: 0,
621 min_coord: 481,
622 max_coord: 504,
623 height: 34,
624 link_ix: Some(2),
625 serif_ix: None,
626 ..Default::default()
627 },
628 Segment {
629 flags: 0,
630 dir: Direction::Down,
631 pos: 168,
632 delta: 0,
633 min_coord: 483,
634 max_coord: 504,
635 height: 26,
636 link_ix: Some(1),
637 serif_ix: None,
638 ..Default::default()
639 },
640 Segment {
641 flags: 0,
642 dir: Direction::Down,
643 pos: 109,
644 delta: 0,
645 min_coord: 109,
646 max_coord: 366,
647 height: 288,
648 link_ix: Some(0),
649 serif_ix: None,
650 ..Default::default()
651 },
652 Segment {
653 flags: 0,
654 dir: Direction::Up,
655 pos: 453,
656 delta: 0,
657 min_coord: 169,
658 max_coord: 432,
659 height: 304,
660 link_ix: Some(7),
661 serif_ix: None,
662 ..Default::default()
663 },
664 Segment {
665 flags: 1,
666 dir: Direction::Up,
667 pos: 62,
668 delta: 0,
669 min_coord: 517,
670 max_coord: 566,
671 height: 76,
672 link_ix: None,
673 serif_ix: None,
674 ..Default::default()
675 },
676 Segment {
677 flags: 1,
678 dir: Direction::Down,
679 pos: 103,
680 delta: 0,
681 min_coord: 619,
682 max_coord: 647,
683 height: 41,
684 link_ix: None,
685 serif_ix: None,
686 ..Default::default()
687 },
688 Segment {
689 flags: 0,
690 dir: Direction::Down,
691 pos: 507,
692 delta: 0,
693 min_coord: 40,
694 max_coord: 485,
695 height: 498,
696 link_ix: Some(4),
697 serif_ix: None,
698 ..Default::default()
699 },
700 ];
701 assert_eq!(segments, &expected);
702 }
703
704 #[test]
705 fn vertical_segments() {
706 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
707 let glyphs = font.outline_glyphs();
708 let glyph = glyphs.get(GlyphId::new(8)).unwrap();
709 let mut outline = Outline::default();
710 outline.fill(&glyph, Default::default()).unwrap();
711 let mut axis = Axis::new(Axis::VERTICAL, outline.orientation);
712 compute_segments(&mut outline, &mut axis, ScriptGroup::Default);
713 link_segments(&outline, &mut axis, 0, ScriptGroup::Default, None);
714 let segments = retain_segment_test_fields(&axis.segments);
715 let expected = [
716 Segment {
717 flags: 0,
718 dir: Direction::Left,
719 pos: 0,
720 delta: 0,
721 min_coord: 85,
722 max_coord: 470,
723 height: 418,
724 link_ix: Some(2),
725 serif_ix: None,
726 ..Default::default()
727 },
728 Segment {
729 flags: 0,
730 dir: Direction::Right,
731 pos: 504,
732 delta: 0,
733 min_coord: 112,
734 max_coord: 168,
735 height: 56,
736 link_ix: Some(3),
737 serif_ix: None,
738 ..Default::default()
739 },
740 Segment {
741 flags: 0,
742 dir: Direction::Right,
743 pos: 109,
744 delta: 0,
745 min_coord: 109,
746 max_coord: 427,
747 height: 327,
748 link_ix: Some(0),
749 serif_ix: None,
750 ..Default::default()
751 },
752 Segment {
753 flags: 0,
754 dir: Direction::Left,
755 pos: 483,
756 delta: 0,
757 min_coord: 86,
758 max_coord: 400,
759 height: 352,
760 link_ix: Some(1),
761 serif_ix: None,
762 ..Default::default()
763 },
764 Segment {
765 flags: 0,
766 dir: Direction::Right,
767 pos: 647,
768 delta: 0,
769 min_coord: 76,
770 max_coord: 103,
771 height: 29,
772 link_ix: None,
773 serif_ix: Some(1),
774 ..Default::default()
775 },
776 Segment {
777 flags: 0,
778 dir: Direction::Right,
779 pos: 592,
780 delta: 0,
781 min_coord: 131,
782 max_coord: 437,
783 height: 346,
784 link_ix: None,
785 serif_ix: Some(1),
786 ..Default::default()
787 },
788 ];
789 assert_eq!(segments, &expected);
790 }
791
792 #[test]
793 fn cjk_horizontal_segments() {
794 let font = FontRef::new(font_test_data::NOTOSERIFTC_AUTOHINT_METRICS).unwrap();
795 let glyphs = font.outline_glyphs();
796 let glyph = glyphs.get(GlyphId::new(9)).unwrap();
797 let mut outline = Outline::default();
798 outline.fill(&glyph, Default::default()).unwrap();
799 let mut axis = Axis::new(Axis::HORIZONTAL, outline.orientation);
800 compute_segments(&mut outline, &mut axis, ScriptGroup::Cjk);
801 link_segments(&outline, &mut axis, 67109, ScriptGroup::Cjk, None);
802 let segments = retain_segment_test_fields(&axis.segments);
803 let expected = [
804 Segment {
805 flags: 0,
806 dir: Direction::Down,
807 pos: 731,
808 delta: 0,
809 min_coord: 155,
810 max_coord: 676,
811 height: 524,
812 link_ix: Some(1),
813 serif_ix: None,
814 ..Default::default()
815 },
816 Segment {
817 flags: 0,
818 dir: Direction::Up,
819 pos: 670,
820 delta: 0,
821 min_coord: 133,
822 max_coord: 712,
823 height: 579,
824 link_ix: Some(0),
825 serif_ix: None,
826 ..Default::default()
827 },
828 Segment {
829 flags: 0,
830 dir: Direction::Down,
831 pos: 458,
832 delta: 0,
833 min_coord: 741,
834 max_coord: 757,
835 height: 88,
836 link_ix: None,
837 serif_ix: None,
838 ..Default::default()
839 },
840 Segment {
841 flags: 0,
842 dir: Direction::Down,
843 pos: 911,
844 delta: 0,
845 min_coord: -9,
846 max_coord: 791,
847 height: 821,
848 link_ix: Some(5),
849 serif_ix: None,
850 ..Default::default()
851 },
852 Segment {
853 flags: 0,
854 dir: Direction::Up,
855 pos: 693,
856 delta: 0,
857 min_coord: -7,
858 max_coord: 9,
859 height: 18,
860 link_ix: None,
861 serif_ix: Some(5),
862 ..Default::default()
863 },
864 Segment {
865 flags: 0,
866 dir: Direction::Up,
867 pos: 849,
868 delta: 0,
869 min_coord: 11,
870 max_coord: 829,
871 height: 823,
872 link_ix: Some(3),
873 serif_ix: None,
874 ..Default::default()
875 },
876 Segment {
877 flags: 0,
878 dir: Direction::Down,
879 pos: 569,
880 delta: 0,
881 min_coord: 547,
882 max_coord: 576,
883 height: 29,
884 link_ix: None,
885 serif_ix: None,
886 ..Default::default()
887 },
888 Segment {
889 flags: 0,
890 dir: Direction::Down,
891 pos: 201,
892 delta: 0,
893 min_coord: -57,
894 max_coord: 540,
895 height: 599,
896 link_ix: Some(8),
897 serif_ix: None,
898 ..Default::default()
899 },
900 Segment {
901 flags: 0,
902 dir: Direction::Up,
903 pos: 138,
904 delta: 0,
905 min_coord: -78,
906 max_coord: 543,
907 height: 640,
908 link_ix: Some(7),
909 serif_ix: None,
910 ..Default::default()
911 },
912 ];
913 assert_eq!(segments, &expected);
914 }
915
916 #[test]
917 fn cjk_vertical_segments() {
918 let font = FontRef::new(font_test_data::NOTOSERIFTC_AUTOHINT_METRICS).unwrap();
919 let glyphs = font.outline_glyphs();
920 let glyph = glyphs.get(GlyphId::new(9)).unwrap();
921 let mut outline = Outline::default();
922 outline.fill(&glyph, Default::default()).unwrap();
923 let mut axis = Axis::new(Axis::VERTICAL, outline.orientation);
924 compute_segments(&mut outline, &mut axis, ScriptGroup::Cjk);
925 link_segments(&outline, &mut axis, 67109, ScriptGroup::Cjk, None);
926 let segments = retain_segment_test_fields(&axis.segments);
927 let expected = [
928 Segment {
929 flags: 0,
930 dir: Direction::Right,
931 pos: 758,
932 delta: 0,
933 min_coord: 280,
934 max_coord: 545,
935 height: 288,
936 link_ix: Some(1),
937 serif_ix: None,
938 ..Default::default()
939 },
940 Segment {
941 flags: 0,
942 dir: Direction::Left,
943 pos: 729,
944 delta: 0,
945 min_coord: 288,
946 max_coord: 674,
947 height: 391,
948 link_ix: Some(0),
949 serif_ix: None,
950 ..Default::default()
951 },
952 Segment {
953 flags: 1,
954 dir: Direction::Left,
955 pos: 133,
956 delta: 0,
957 min_coord: 670,
958 max_coord: 693,
959 height: 34,
960 link_ix: None,
961 serif_ix: None,
962 ..Default::default()
963 },
964 Segment {
965 flags: 0,
966 dir: Direction::Right,
967 pos: 757,
968 delta: 0,
969 min_coord: 393,
970 max_coord: 458,
971 height: 70,
972 link_ix: None,
973 serif_ix: Some(0),
974 ..Default::default()
975 },
976 Segment {
977 flags: 1,
978 dir: Direction::Right,
979 pos: 3,
980 delta: 2,
981 min_coord: 727,
982 max_coord: 838,
983 height: 133,
984 link_ix: None,
985 serif_ix: None,
986 ..Default::default()
987 },
988 Segment {
989 flags: 0,
990 dir: Direction::Right,
991 pos: 576,
992 delta: 0,
993 min_coord: 397,
994 max_coord: 569,
995 height: 177,
996 link_ix: Some(7),
997 serif_ix: None,
998 ..Default::default()
999 },
1000 Segment {
1001 flags: 0,
1002 dir: Direction::Left,
1003 pos: 547,
1004 delta: 0,
1005 min_coord: 387,
1006 max_coord: 569,
1007 height: 182,
1008 link_ix: None,
1009 serif_ix: Some(7),
1010 ..Default::default()
1011 },
1012 Segment {
1013 flags: 0,
1014 dir: Direction::Left,
1015 pos: 576,
1016 delta: 0,
1017 min_coord: 536,
1018 max_coord: 546,
1019 height: 10,
1020 link_ix: Some(5),
1021 serif_ix: None,
1022 ..Default::default()
1023 },
1024 Segment {
1025 flags: 1,
1026 dir: Direction::Left,
1027 pos: -78,
1028 delta: 0,
1029 min_coord: 138,
1030 max_coord: 161,
1031 height: 34,
1032 link_ix: None,
1033 serif_ix: None,
1034 ..Default::default()
1035 },
1036 Segment {
1037 flags: 1,
1038 dir: Direction::Left,
1039 pos: 788,
1040 delta: 0,
1041 min_coord: 262,
1042 max_coord: 294,
1043 height: 46,
1044 link_ix: None,
1045 serif_ix: None,
1046 ..Default::default()
1047 },
1048 ];
1049 assert_eq!(segments, &expected);
1050 }
1051
1052 fn retain_segment_test_fields(segments: &[Segment]) -> Vec<Segment> {
1055 segments
1056 .iter()
1057 .map(|segment| Segment {
1058 flags: segment.flags,
1059 dir: segment.dir,
1060 pos: segment.pos,
1061 delta: segment.delta,
1062 min_coord: segment.min_coord,
1063 max_coord: segment.max_coord,
1064 height: segment.height,
1065 link_ix: segment.link_ix,
1066 serif_ix: segment.serif_ix,
1067 ..Default::default()
1068 })
1069 .collect()
1070 }
1071}