1use crate::{
4 from_obj::{FromObjRef, FromTableRef, ToOwnedTable},
5 util::{self, MultiZip, WrappingGet},
6 FontWrite, OtRound,
7};
8
9use kurbo::BezPath;
10use read_fonts::{tables::glyf::SimpleGlyphFlags, FontRead, ReadArgs};
11
12pub use read_fonts::tables::glyf::CurvePoint;
13
14use super::Bbox;
15
16#[derive(Clone, Debug, Default, PartialEq, Eq)]
18pub struct SimpleGlyph {
19 pub bbox: Bbox,
20 pub contours: Vec<Contour>,
21 pub instructions: Vec<u8>,
22 pub overlaps: bool,
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq)]
30pub struct Contour(Vec<CurvePoint>);
31
32#[derive(Clone, Debug)]
34#[non_exhaustive]
35pub enum MalformedPath {
36 HasCubic,
37 TooSmall,
38 MissingMove,
39 UnequalNumberOfElements(Vec<usize>),
40 InconsistentPathElements(usize, Vec<&'static str>),
41}
42
43impl SimpleGlyph {
44 pub fn from_bezpath(path: &BezPath) -> Result<Self, MalformedPath> {
64 Self::interpolatable_glyphs_from_bezpaths(std::slice::from_ref(path))
65 .map(|mut x| x.pop().unwrap())
66 }
67
68 pub fn interpolatable_glyphs_from_bezpaths(
81 paths: &[BezPath],
82 ) -> Result<Vec<Self>, MalformedPath> {
83 simple_glyphs_from_kurbo(paths)
84 }
85
86 fn compute_point_deltas(
94 &self,
95 ) -> impl Iterator<Item = (SimpleGlyphFlags, CoordDelta, CoordDelta)> + '_ {
96 fn flag_and_delta(
98 value: i16,
99 short_flag: SimpleGlyphFlags,
100 same_or_pos: SimpleGlyphFlags,
101 ) -> (SimpleGlyphFlags, CoordDelta) {
102 const SHORT_MAX: i16 = u8::MAX as i16;
103 const SHORT_MIN: i16 = -SHORT_MAX;
104 match value {
105 0 => (same_or_pos, CoordDelta::Skip),
106 SHORT_MIN..=-1 => (short_flag, CoordDelta::Short(value.unsigned_abs() as u8)),
107 1..=SHORT_MAX => (short_flag | same_or_pos, CoordDelta::Short(value as _)),
108 _other => (SimpleGlyphFlags::empty(), CoordDelta::Long(value)),
109 }
110 }
111
112 let (mut last_x, mut last_y) = (0, 0);
113 let mut iter = self.contours.iter().flat_map(|c| c.iter());
114 std::iter::from_fn(move || {
115 let point = iter.next()?;
116 let mut flag = SimpleGlyphFlags::empty();
117 let d_x = point.x - last_x;
118 let d_y = point.y - last_y;
119 last_x = point.x;
120 last_y = point.y;
121
122 if point.on_curve {
123 flag |= SimpleGlyphFlags::ON_CURVE_POINT;
124 }
125 if point.cubic {
126 flag |= SimpleGlyphFlags::CUBIC;
127 }
128 let (x_flag, x_data) = flag_and_delta(
129 d_x,
130 SimpleGlyphFlags::X_SHORT_VECTOR,
131 SimpleGlyphFlags::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
132 );
133 let (y_flag, y_data) = flag_and_delta(
134 d_y,
135 SimpleGlyphFlags::Y_SHORT_VECTOR,
136 SimpleGlyphFlags::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
137 );
138
139 flag |= x_flag | y_flag;
140 Some((flag, x_data, y_data))
141 })
142 }
143
144 pub fn recompute_bounding_box(&mut self) {
146 let mut points = self
147 .contours
148 .iter()
149 .flat_map(|c| c.iter())
150 .map(|p| (p.x, p.y));
151
152 if let Some((mut x_min, mut y_min)) = points.next() {
153 let mut x_max = x_min;
154 let mut y_max = y_min;
155 for (x, y) in points {
156 x_min = x_min.min(x);
157 y_min = y_min.min(y);
158 x_max = x_max.max(x);
159 y_max = y_max.max(y);
160 }
161 self.bbox = Bbox {
162 x_min,
163 y_min,
164 x_max,
165 y_max,
166 };
167 }
168 }
169}
170
171impl Contour {
172 pub fn len(&self) -> usize {
174 self.0.len()
175 }
176
177 pub fn is_empty(&self) -> bool {
179 self.0.is_empty()
180 }
181
182 pub fn iter(&self) -> impl Iterator<Item = &CurvePoint> {
183 self.0.iter()
184 }
185}
186
187impl From<Vec<CurvePoint>> for Contour {
188 fn from(points: Vec<CurvePoint>) -> Self {
189 Self(points)
190 }
191}
192
193impl From<Contour> for Vec<CurvePoint> {
194 fn from(contour: Contour) -> Self {
195 contour.0
196 }
197}
198
199impl MalformedPath {
200 fn inconsistent_path_els(idx: usize, elements: &[kurbo::PathEl]) -> Self {
201 fn el_types(elements: &[kurbo::PathEl]) -> Vec<&'static str> {
202 elements
203 .iter()
204 .map(|el| match el {
205 kurbo::PathEl::MoveTo(_) => "M",
206 kurbo::PathEl::LineTo(_) => "L",
207 kurbo::PathEl::QuadTo(_, _) => "Q",
208 kurbo::PathEl::CurveTo(_, _, _) => "C",
209 kurbo::PathEl::ClosePath => "Z",
210 })
211 .collect()
212 }
213
214 MalformedPath::InconsistentPathElements(idx, el_types(elements))
215 }
216}
217
218#[derive(Clone, Copy, Debug)]
220enum CoordDelta {
221 Skip,
223 Short(u8),
224 Long(i16),
225}
226
227impl FontWrite for CoordDelta {
228 fn write_into(&self, writer: &mut crate::TableWriter) {
229 match self {
230 CoordDelta::Skip => (),
231 CoordDelta::Short(val) => val.write_into(writer),
232 CoordDelta::Long(val) => val.write_into(writer),
233 }
234 }
235}
236
237impl FromObjRef<read_fonts::tables::glyf::SimpleGlyph<'_>> for SimpleGlyph {
238 fn from_obj_ref(
239 from: &read_fonts::tables::glyf::SimpleGlyph,
240 _data: read_fonts::FontData,
241 ) -> Self {
242 let bbox = Bbox {
243 x_min: from.x_min(),
244 y_min: from.y_min(),
245 x_max: from.x_max(),
246 y_max: from.y_max(),
247 };
248 let mut points = from.points();
249 let mut last_end = 0;
250 let mut contours = vec![];
251 for end_pt in from.end_pts_of_contours() {
252 let end = end_pt.get() as usize + 1;
253 let count = end - last_end;
254 last_end = end;
255 contours.push(Contour(points.by_ref().take(count).collect()));
256 }
257 Self {
258 bbox,
259 contours,
260 instructions: from.instructions().to_owned(),
261 overlaps: from.has_overlapping_contours(),
262 }
263 }
264}
265
266impl FromTableRef<read_fonts::tables::glyf::SimpleGlyph<'_>> for SimpleGlyph {}
267
268impl ReadArgs for SimpleGlyph {
269 type Args = ();
270}
271
272impl<'a> FontRead<'a> for SimpleGlyph {
273 fn read_with_args(
274 data: read_fonts::FontData<'a>,
275 _: (),
276 ) -> Result<Self, read_fonts::ReadError> {
277 read_fonts::tables::glyf::SimpleGlyph::read(data).map(|g| g.to_owned_table())
278 }
279}
280
281impl FontWrite for SimpleGlyph {
282 fn write_into(&self, writer: &mut crate::TableWriter) {
283 assert!(self.contours.len() < i16::MAX as usize);
284 assert!(self.instructions.len() < u16::MAX as usize);
285 let n_contours = self.contours.len() as i16;
286 if n_contours == 0 {
287 return;
289 }
290 n_contours.write_into(writer);
291 self.bbox.write_into(writer);
292 let mut cur = 0;
294 for contour in &self.contours {
295 cur += contour.len();
296 (cur as u16 - 1).write_into(writer);
297 }
298 (self.instructions.len() as u16).write_into(writer);
299 self.instructions.write_into(writer);
300
301 let mut deltas = self.compute_point_deltas().collect::<Vec<_>>();
302 if self.overlaps {
303 if let Some((flags, _, _)) = deltas.first_mut() {
304 flags.insert(SimpleGlyphFlags::OVERLAP_SIMPLE);
305 }
306 }
307 RepeatableFlag::iter_from_flags(deltas.iter().map(|(flag, _, _)| *flag))
308 .for_each(|flag| flag.write_into(writer));
309 deltas.iter().for_each(|(_, x, _)| x.write_into(writer));
310 deltas.iter().for_each(|(_, _, y)| y.write_into(writer));
311 writer.pad_to_2byte_aligned();
312 }
313}
314
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
317struct RepeatableFlag {
318 flag: SimpleGlyphFlags,
319 repeat: u8,
320}
321
322impl FontWrite for RepeatableFlag {
323 fn write_into(&self, writer: &mut crate::TableWriter) {
324 debug_assert_eq!(
325 self.flag.contains(SimpleGlyphFlags::REPEAT_FLAG),
326 self.repeat > 0
327 );
328
329 self.flag.bits().write_into(writer);
330 if self.flag.contains(SimpleGlyphFlags::REPEAT_FLAG) {
331 self.repeat.write_into(writer);
332 }
333 }
334}
335
336impl RepeatableFlag {
337 fn iter_from_flags(
341 flags: impl IntoIterator<Item = SimpleGlyphFlags>,
342 ) -> impl Iterator<Item = RepeatableFlag> {
343 let mut iter = flags.into_iter();
344 let mut prev = None;
345 let mut decompose_single_repeat = None;
353
354 std::iter::from_fn(move || loop {
355 if let Some(repeat) = decompose_single_repeat.take() {
356 return Some(repeat);
357 }
358
359 match (iter.next(), prev.take()) {
360 (None, Some(RepeatableFlag { flag, repeat: 1 })) => {
361 let flag = flag & !SimpleGlyphFlags::REPEAT_FLAG;
362 decompose_single_repeat = Some(RepeatableFlag { flag, repeat: 0 });
363 return decompose_single_repeat;
364 }
365 (None, prev) => return prev,
366 (Some(flag), None) => prev = Some(RepeatableFlag { flag, repeat: 0 }),
367 (Some(flag), Some(mut last)) => {
368 if (last.flag & !SimpleGlyphFlags::REPEAT_FLAG) == flag && last.repeat < u8::MAX
369 {
370 last.repeat += 1;
371 last.flag |= SimpleGlyphFlags::REPEAT_FLAG;
372 prev = Some(last);
373 } else {
374 if last.repeat == 1 {
376 last.flag &= !SimpleGlyphFlags::REPEAT_FLAG;
377 last.repeat = 0;
378 decompose_single_repeat = Some(last);
381 }
382 prev = Some(RepeatableFlag { flag, repeat: 0 });
383 return Some(last);
384 }
385 }
386 }
387 })
388 }
389}
390
391impl crate::validate::Validate for SimpleGlyph {
392 fn validate_impl(&self, ctx: &mut crate::codegen_prelude::ValidationCtx) {
393 if self.instructions.len() > u16::MAX as usize {
394 ctx.report("instructions len overflows");
395 }
396 }
397}
398
399#[derive(Clone, Copy, Debug, PartialEq)]
404struct ContourPoint {
405 point: kurbo::Point,
406 on_curve: bool,
407}
408
409impl ContourPoint {
410 fn new(point: kurbo::Point, on_curve: bool) -> Self {
411 Self { point, on_curve }
412 }
413
414 fn on_curve(point: kurbo::Point) -> Self {
415 Self::new(point, true)
416 }
417
418 fn off_curve(point: kurbo::Point) -> Self {
419 Self::new(point, false)
420 }
421}
422
423impl From<ContourPoint> for CurvePoint {
424 fn from(pt: ContourPoint) -> Self {
425 let (x, y) = pt.point.ot_round();
426 CurvePoint::new(x, y, pt.on_curve)
427 }
428}
429#[derive(Clone, Debug, PartialEq)]
433struct InterpolatableContourBuilder(Vec<Vec<ContourPoint>>);
434
435impl InterpolatableContourBuilder {
436 fn new(move_pts: &[kurbo::Point]) -> Self {
438 assert!(!move_pts.is_empty());
439 Self(
440 move_pts
441 .iter()
442 .map(|pt| vec![ContourPoint::on_curve(*pt)])
443 .collect(),
444 )
445 }
446
447 fn len(&self) -> usize {
449 self.0.len()
450 }
451
452 fn line_to(&mut self, pts: &[kurbo::Point]) {
454 assert_eq!(pts.len(), self.len());
455 for (i, pt) in pts.iter().enumerate() {
456 self.0[i].push(ContourPoint::on_curve(*pt));
457 }
458 }
459
460 fn quad_to(&mut self, pts: &[(kurbo::Point, kurbo::Point)]) {
462 for (i, (p0, p1)) in pts.iter().enumerate() {
463 self.0[i].push(ContourPoint::off_curve(*p0));
464 self.0[i].push(ContourPoint::on_curve(*p1));
465 }
466 }
467
468 fn num_points(&self) -> usize {
470 let n = self.0[0].len();
471 assert!(self.0.iter().all(|c| c.len() == n));
472 n
473 }
474
475 fn first(&self) -> impl Iterator<Item = &ContourPoint> {
477 self.0.iter().map(|v| v.first().unwrap())
478 }
479
480 fn last(&self) -> impl Iterator<Item = &ContourPoint> {
482 self.0.iter().map(|v| v.last().unwrap())
483 }
484
485 fn remove_last(&mut self) {
487 self.0.iter_mut().for_each(|c| {
488 c.pop().unwrap();
489 });
490 }
491
492 fn is_implicit_on_curve(&self, idx: usize) -> bool {
493 self.0
494 .iter()
495 .all(|points| is_implicit_on_curve(points, idx))
496 }
497
498 fn build(self) -> Vec<Contour> {
500 let num_contours = self.len();
501 let num_points = self.num_points();
502 let mut contours = vec![Contour::default(); num_contours];
503 contours.iter_mut().for_each(|c| c.0.reserve(num_points));
504 for point_idx in (0..num_points).filter(|point_idx| !self.is_implicit_on_curve(*point_idx))
505 {
506 for (contour_idx, contour) in contours.iter_mut().enumerate() {
507 contour
508 .0
509 .push(CurvePoint::from(self.0[contour_idx][point_idx]));
510 }
511 }
512 contours
513 }
514}
515
516#[inline]
521fn is_mid_point(p0: kurbo::Point, p1: kurbo::Point, p2: kurbo::Point) -> bool {
522 let mid = p0.midpoint(p2);
523 (util::isclose(mid.x, p1.x) && util::isclose(mid.y, p1.y))
524 || p0.to_vec2().ot_round() + p2.to_vec2().ot_round() == p1.to_vec2().ot_round() * 2.0
525}
526
527fn is_implicit_on_curve(points: &[ContourPoint], idx: usize) -> bool {
528 let p1 = &points[idx]; if !p1.on_curve {
530 return false;
531 }
532 let p0 = points.wrapping_prev(idx);
533 let p2 = points.wrapping_next(idx);
534 if p0.on_curve || p0.on_curve != p2.on_curve {
535 return false;
536 }
537 is_mid_point(p0.point, p1.point, p2.point)
539}
540
541fn simple_glyphs_from_kurbo(paths: &[BezPath]) -> Result<Vec<SimpleGlyph>, MalformedPath> {
543 let num_elements: Vec<usize> = paths.iter().map(|path| path.elements().len()).collect();
545 if num_elements.iter().any(|n| *n != num_elements[0]) {
546 return Err(MalformedPath::UnequalNumberOfElements(num_elements));
547 }
548 let path_iters = MultiZip::new(paths.iter().map(|path| path.iter()).collect());
549 let mut contours: Vec<InterpolatableContourBuilder> = Vec::new();
550 let mut current: Option<InterpolatableContourBuilder> = None;
551 let num_glyphs = paths.len();
552 let mut pts = Vec::with_capacity(num_glyphs);
553 let mut quad_pts = Vec::with_capacity(num_glyphs);
554 for (i, elements) in path_iters.enumerate() {
555 let first_el = elements.first().unwrap();
558 match first_el {
559 kurbo::PathEl::MoveTo(_) => {
560 if let Some(prev) = current.take() {
562 contours.push(prev);
563 }
564 pts.clear();
565 for el in &elements {
566 match el {
567 &kurbo::PathEl::MoveTo(pt) => {
568 pts.push(pt);
569 }
570 _ => return Err(MalformedPath::inconsistent_path_els(i, &elements)),
571 }
572 }
573 current = Some(InterpolatableContourBuilder::new(&pts));
574 }
575 kurbo::PathEl::LineTo(_) => {
576 pts.clear();
577 for el in &elements {
578 match el {
579 &kurbo::PathEl::LineTo(pt) => {
580 pts.push(pt);
581 }
582 _ => return Err(MalformedPath::inconsistent_path_els(i, &elements)),
583 }
584 }
585 current
586 .as_mut()
587 .ok_or(MalformedPath::MissingMove)?
588 .line_to(&pts)
589 }
590 kurbo::PathEl::QuadTo(_, _) => {
591 quad_pts.clear();
592 for el in &elements {
593 match el {
594 &kurbo::PathEl::QuadTo(p0, p1) => {
595 quad_pts.push((p0, p1));
596 }
597 _ => return Err(MalformedPath::inconsistent_path_els(i, &elements)),
598 }
599 }
600 current
601 .as_mut()
602 .ok_or(MalformedPath::MissingMove)?
603 .quad_to(&quad_pts)
604 }
605 kurbo::PathEl::CurveTo(_, _, _) => return Err(MalformedPath::HasCubic),
606 kurbo::PathEl::ClosePath => {
607 let contour = current.as_mut().ok_or(MalformedPath::MissingMove)?;
608 if contour.num_points() > 1 && contour.last().eq(contour.first()) {
612 contour.remove_last();
613 }
614 }
615 }
616 }
617 contours.extend(current);
618
619 let mut glyph_contours = vec![Vec::new(); num_glyphs];
620 for builder in contours {
621 assert_eq!(builder.len(), num_glyphs);
622 for (i, contour) in builder.build().into_iter().enumerate() {
623 glyph_contours[i].push(contour);
624 }
625 }
626
627 let mut glyphs = Vec::new();
628 for (contours, path) in glyph_contours.into_iter().zip(paths.iter()) {
629 glyphs.push(SimpleGlyph {
632 bbox: path.control_box().into(),
633 contours,
634 instructions: Default::default(),
635 overlaps: false,
636 })
637 }
638
639 Ok(glyphs)
640}
641
642#[cfg(test)]
643mod tests {
644 use font_types::GlyphId;
645 use kurbo::Affine;
646 use read_fonts::{tables::glyf as read_glyf, FontRef, TableProvider};
647
648 use super::*;
649
650 #[test]
651 fn roundtrip_cubic_flags_in_extended_simple_glyph() {
652 let bytes = font_test_data::extended::simple_glyph(&[
653 (0, 0, 0x81),
654 (20, 80, 0x80),
655 (60, 80, 0x80),
656 (80, 0, 0x81),
657 ]);
658 let read = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
659 let points: Vec<_> = read.points().collect();
660 assert!(points.iter().all(|point| point.cubic));
661 assert!(points[0].on_curve);
662 assert!(!points[1].on_curve);
663 let owned: SimpleGlyph = read.to_owned_table();
664 let compiled = crate::dump_table(&owned).unwrap();
665 let read = read_glyf::SimpleGlyph::read(compiled.as_slice().into()).unwrap();
666 assert_eq!(read.points().collect::<Vec<_>>(), points);
667 let mut fast_points = vec![read_fonts::types::Point::<i32>::default(); points.len()];
668 let mut flags = vec![read_glyf::PointFlags::default(); points.len()];
669 read.read_points_fast(&mut fast_points, &mut flags).unwrap();
670 assert_eq!(
671 flags.iter().map(|flag| flag.to_bits()).collect::<Vec<_>>(),
672 [0x81, 0x80, 0x80, 0x81]
673 );
674 }
675
676 fn pad_for_loca_format(loca: &read_fonts::tables::loca::Loca, mut bytes: Vec<u8>) -> Vec<u8> {
679 if matches!(loca, read_fonts::tables::loca::Loca::Short(_)) && bytes.len() & 1 != 0 {
680 bytes.push(0);
681 }
682 bytes
683 }
684
685 #[test]
686 fn bad_path_input() {
687 let mut path = BezPath::new();
688 path.move_to((0., 0.));
689 path.curve_to((10., 10.), (20., 20.), (30., 30.));
690 path.line_to((50., 50.));
691 path.line_to((10., 10.));
692 let err = SimpleGlyph::from_bezpath(&path).unwrap_err();
693 assert!(matches!(err, MalformedPath::HasCubic));
694 }
695
696 #[test]
697 fn read_write_simple() {
698 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
699 let loca = font.loca(None).unwrap();
700 let glyf = font.glyf().unwrap();
701 let read_glyf::Glyph::Simple(orig) = loca
702 .get(GlyphId::new(0), &glyf)
703 .and_then(|g| g.into_glyph())
704 .unwrap()
705 else {
706 panic!("not a simple glyph")
707 };
708 let orig_bytes = orig.offset_data();
709
710 let ours = SimpleGlyph::from_table_ref(&orig);
711 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
712 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
713
714 let our_points = ours.points().collect::<Vec<_>>();
715 let their_points = orig.points().collect::<Vec<_>>();
716 assert_eq!(our_points, their_points);
717 assert_eq!(orig_bytes.as_ref(), bytes);
718 assert_eq!(orig.glyph_data(), ours.glyph_data());
719 assert_eq!(orig_bytes.len(), bytes.len());
720 }
721
722 #[test]
723 fn round_trip_simple() {
724 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
725 let loca = font.loca(None).unwrap();
726 let glyf = font.glyf().unwrap();
727 let read_glyf::Glyph::Simple(orig) = loca
728 .get(GlyphId::new(2), &glyf)
729 .and_then(|g| g.into_glyph())
730 .unwrap()
731 else {
732 panic!("not a simple glyph")
733 };
734 let orig_bytes = orig.offset_data();
735
736 let bezpath = BezPath::from_svg("M278,710 L278,470 L998,470 L998,710 Z").unwrap();
737
738 let ours = SimpleGlyph::from_bezpath(&bezpath).unwrap();
739 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
740 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
741
742 let our_points = ours.points().collect::<Vec<_>>();
743 let their_points = orig.points().collect::<Vec<_>>();
744 assert_eq!(our_points, their_points);
745 assert_eq!(orig_bytes.as_ref(), bytes);
746 assert_eq!(orig.glyph_data(), ours.glyph_data());
747 assert_eq!(orig_bytes.len(), bytes.len());
748 }
749
750 #[test]
751 fn round_trip_multi_contour() {
752 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
753 let loca = font.loca(None).unwrap();
754 let glyf = font.glyf().unwrap();
755 let read_glyf::Glyph::Simple(orig) = loca
756 .get(GlyphId::new(1), &glyf)
757 .and_then(|g| g.into_glyph())
758 .unwrap()
759 else {
760 panic!("not a simple glyph")
761 };
762 let orig_bytes = orig.offset_data();
763
764 let bezpath = BezPath::from_svg("M708,1327 L226,0 L29,0 L584,1456 L711,1456 Z M1112,0 L629,1327 L626,1456 L753,1456 L1310,0 Z M1087,539 L1087,381 L269,381 L269,539 Z").unwrap();
765
766 let ours = SimpleGlyph::from_bezpath(&bezpath).unwrap();
767 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
768 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
769
770 let our_points = ours.points().collect::<Vec<_>>();
771 let their_points = orig.points().collect::<Vec<_>>();
772 dbg!(
773 SimpleGlyphFlags::from_bits(1),
774 SimpleGlyphFlags::from_bits(9)
775 );
776 assert_eq!(our_points, their_points);
777 assert_eq!(orig.glyph_data(), ours.glyph_data());
778 assert_eq!(orig_bytes.len(), bytes.len());
779 assert_eq!(orig_bytes.as_ref(), bytes);
780 }
781
782 #[test]
783 fn simple_glyph_open_path() {
784 let mut path = BezPath::new();
785 path.move_to((20., -100.));
786 path.quad_to((1337., 1338.), (-50., -69.0));
787 path.quad_to((13., 255.), (-255., 256.));
788 path.line_to((20., -100.));
791
792 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
793 let bytes = crate::dump_table(&glyph).unwrap();
794 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
795 assert_eq!(read.number_of_contours(), 1);
796 assert_eq!(read.num_points(), 6);
797 assert_eq!(read.end_pts_of_contours(), &[5]);
798 let points = read.points().collect::<Vec<_>>();
799 assert_eq!(points[0].x, 20);
800 assert_eq!(points[0].y, -100);
801 assert!(points[0].on_curve);
802 assert_eq!(points[1].x, 1337);
803 assert_eq!(points[1].y, 1338);
804 assert!(!points[1].on_curve);
805 assert_eq!(points[4].x, -255);
806 assert_eq!(points[4].y, 256);
807 assert!(points[4].on_curve);
808 assert_eq!(points[5].x, 20);
809 assert_eq!(points[5].y, -100);
810 assert!(points[5].on_curve);
811 }
812
813 #[test]
814 fn simple_glyph_closed_path_implicit_vs_explicit_closing_line() {
815 let mut path1 = BezPath::new();
816 path1.move_to((20., -100.));
817 path1.quad_to((1337., 1338.), (-50., -69.0));
818 path1.quad_to((13., 255.), (-255., 256.));
819 path1.close_path();
820
821 let mut path2 = BezPath::new();
822 path2.move_to((20., -100.));
823 path2.quad_to((1337., 1338.), (-50., -69.0));
824 path2.quad_to((13., 255.), (-255., 256.));
825 path2.line_to((20., -100.));
828 path2.close_path();
829
830 for path in &[path1, path2] {
831 let glyph = SimpleGlyph::from_bezpath(path).unwrap();
832 let bytes = crate::dump_table(&glyph).unwrap();
833 let read =
834 read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
835 assert_eq!(read.number_of_contours(), 1);
836 assert_eq!(read.num_points(), 5);
837 assert_eq!(read.end_pts_of_contours(), &[4]);
838 let points = read.points().collect::<Vec<_>>();
839 assert_eq!(points[0].x, 20);
840 assert_eq!(points[0].y, -100);
841 assert!(points[0].on_curve);
842 assert_eq!(points[1].x, 1337);
843 assert_eq!(points[1].y, 1338);
844 assert!(!points[1].on_curve);
845 assert_eq!(points[4].x, -255);
846 assert_eq!(points[4].y, 256);
847 assert!(points[4].on_curve);
848 }
849 }
850
851 #[test]
852 fn keep_single_point_contours() {
853 let mut path = BezPath::new();
855 path.move_to((0.0, 0.0));
856 path.move_to((1.0, 2.0));
858 path.close_path();
859
860 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
861 let bytes = crate::dump_table(&glyph).unwrap();
862 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
863 assert_eq!(read.number_of_contours(), 2);
864 assert_eq!(read.num_points(), 2);
865 assert_eq!(read.end_pts_of_contours(), &[0, 1]);
866 let points = read.points().collect::<Vec<_>>();
867 assert_eq!(points[0].x, 0);
868 assert_eq!(points[0].y, 0);
869 assert!(points[0].on_curve);
870 assert_eq!(points[1].x, 1);
871 assert_eq!(points[1].y, 2);
872 assert!(points[0].on_curve);
873 }
874
875 #[test]
876 fn compile_repeatable_flags() {
877 let mut path = BezPath::new();
878 path.move_to((20., -100.));
879 path.line_to((25., -90.));
880 path.line_to((50., -69.));
881 path.line_to((80., -20.));
882
883 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
884 let flags = glyph
885 .compute_point_deltas()
886 .map(|x| x.0)
887 .collect::<Vec<_>>();
888 let r_flags = RepeatableFlag::iter_from_flags(flags.iter().copied()).collect::<Vec<_>>();
889
890 assert_eq!(r_flags.len(), 2, "{r_flags:?}");
891 let bytes = crate::dump_table(&glyph).unwrap();
892 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
893 assert_eq!(read.number_of_contours(), 1);
894 assert_eq!(read.num_points(), 4);
895 assert_eq!(read.end_pts_of_contours(), &[3]);
896 let points = read.points().collect::<Vec<_>>();
897 assert_eq!(points[0].x, 20);
898 assert_eq!(points[0].y, -100);
899 assert_eq!(points[1].x, 25);
900 assert_eq!(points[1].y, -90);
901 assert_eq!(points[2].x, 50);
902 assert_eq!(points[2].y, -69);
903 assert_eq!(points[3].x, 80);
904 assert_eq!(points[3].y, -20);
905 }
906
907 #[test]
908 fn simple_glyphs_from_kurbo_unequal_number_of_elements() {
909 let mut path1 = BezPath::new();
910 path1.move_to((0., 0.));
911 path1.line_to((1., 1.));
912 path1.line_to((2., 2.));
913 path1.line_to((0., 0.));
914 path1.close_path();
915 assert_eq!(path1.elements().len(), 5);
916
917 let mut path2 = BezPath::new();
918 path2.move_to((3., 3.));
919 path2.line_to((4., 4.));
920 path2.line_to((5., 5.));
921 path2.line_to((6., 6.));
922 path2.line_to((3., 3.));
923 path2.close_path();
924 assert_eq!(path2.elements().len(), 6);
925
926 let err = simple_glyphs_from_kurbo(&[path1, path2]).unwrap_err();
927 assert!(matches!(err, MalformedPath::UnequalNumberOfElements(_)));
928 assert_eq!(format!("{:?}", err), "UnequalNumberOfElements([5, 6])");
929 }
930
931 #[test]
932 fn simple_glyphs_from_kurbo_inconsistent_path_elements() {
933 let mut path1 = BezPath::new();
934 path1.move_to((0., 0.));
935 path1.line_to((1., 1.));
936 path1.quad_to((2., 2.), (0., 0.));
937 path1.close_path();
938 let mut path2 = BezPath::new();
939 path2.move_to((3., 3.));
940 path2.quad_to((4., 4.), (5., 5.)); path2.line_to((3., 3.));
942 path2.close_path();
943
944 let err = simple_glyphs_from_kurbo(&[path1, path2]).unwrap_err();
945 assert!(matches!(err, MalformedPath::InconsistentPathElements(1, _)));
946 assert_eq!(
947 format!("{:?}", err),
948 "InconsistentPathElements(1, [\"L\", \"Q\"])"
949 );
950 }
951
952 fn make_interpolatable_paths(
973 num_paths: usize,
974 el_types: &str,
975 last_pt_equal_move: bool,
976 ) -> Vec<BezPath> {
977 let mut paths = Vec::new();
978 let mut start = 0.0;
981 let mut points = std::iter::from_fn(move || {
982 let value = start;
983 start += 1.0;
984 Some((value, value))
985 });
986 let el_types = el_types.chars().collect::<Vec<_>>();
987 assert!(!el_types.is_empty());
988 for _ in 0..num_paths {
989 let mut path = BezPath::new();
990 let mut start_pt = None;
991 let mut el_types_iter = el_types.iter().peekable();
993 while let Some(&el_type) = el_types_iter.next() {
994 let next_el_type = el_types_iter.peek().map(|x| **x).unwrap_or('M');
995 match el_type {
996 'M' => {
997 start_pt = points.next();
998 path.move_to(start_pt.unwrap());
999 }
1000 'L' => {
1001 if matches!(next_el_type, 'Z' | 'M') && last_pt_equal_move {
1002 path.line_to(start_pt.unwrap());
1003 } else {
1004 path.line_to(points.next().unwrap());
1005 }
1006 }
1007 'Q' => {
1008 let p1 = points.next().unwrap();
1009 let p2 = if matches!(next_el_type, 'Z' | 'M') && last_pt_equal_move {
1010 start_pt.unwrap()
1011 } else {
1012 points.next().unwrap()
1013 };
1014 path.quad_to(p1, p2);
1015 }
1016 'Z' => {
1017 path.close_path();
1018 start_pt = None;
1019 }
1020 _ => panic!("Unsupported element type {:?}", el_type),
1021 }
1022 }
1023 paths.push(path);
1024 }
1025 assert_eq!(paths.len(), num_paths);
1026 paths
1027 }
1028
1029 fn assert_contour_points(glyph: &SimpleGlyph, all_points: Vec<Vec<CurvePoint>>) {
1030 let expected_num_contours = all_points.len();
1031 assert_eq!(glyph.contours.len(), expected_num_contours);
1032 for (contour, expected_points) in glyph.contours.iter().zip(all_points.iter()) {
1033 let points = contour.iter().copied().collect::<Vec<_>>();
1034 assert_eq!(points, *expected_points);
1035 }
1036 }
1037
1038 #[test]
1039 fn simple_glyphs_from_kurbo_3_lines_closed() {
1040 let paths = make_interpolatable_paths(2, "MLLLZ", true);
1042 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1043
1044 assert_contour_points(
1045 &glyphs[0],
1046 vec![vec![
1047 CurvePoint::on_curve(0, 0),
1048 CurvePoint::on_curve(1, 1),
1049 CurvePoint::on_curve(2, 2),
1050 ]],
1051 );
1052 assert_contour_points(
1053 &glyphs[1],
1054 vec![vec![
1055 CurvePoint::on_curve(3, 3),
1056 CurvePoint::on_curve(4, 4),
1057 CurvePoint::on_curve(5, 5),
1058 ]],
1059 );
1060 }
1061
1062 #[test]
1063 fn simple_glyphs_from_kurbo_3_lines_implicitly_closed() {
1064 let paths = make_interpolatable_paths(2, "MLLZ", false);
1066 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1067
1068 assert_contour_points(
1069 &glyphs[0],
1070 vec![vec![
1071 CurvePoint::on_curve(0, 0),
1072 CurvePoint::on_curve(1, 1),
1073 CurvePoint::on_curve(2, 2),
1074 ]],
1075 );
1076 assert_contour_points(
1077 &glyphs[1],
1078 vec![vec![
1079 CurvePoint::on_curve(3, 3),
1080 CurvePoint::on_curve(4, 4),
1081 CurvePoint::on_curve(5, 5),
1082 ]],
1083 );
1084 }
1085
1086 #[test]
1087 fn simple_glyphs_from_kurbo_2_quads_closed() {
1088 let paths = make_interpolatable_paths(2, "MQQZ", true);
1093 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1094
1095 assert_contour_points(
1096 &glyphs[0],
1097 vec![vec![
1098 CurvePoint::on_curve(0, 0),
1099 CurvePoint::off_curve(1, 1),
1100 CurvePoint::off_curve(3, 3),
1102 ]],
1103 );
1104 assert_contour_points(
1105 &glyphs[1],
1106 vec![vec![
1107 CurvePoint::on_curve(4, 4),
1108 CurvePoint::off_curve(5, 5),
1109 CurvePoint::off_curve(7, 7),
1111 ]],
1112 );
1113 }
1114
1115 #[test]
1116 fn simple_glyphs_from_kurbo_2_quads_1_line_implicitly_closed() {
1117 let paths = make_interpolatable_paths(2, "MQQZ", false);
1121 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1122
1123 assert_contour_points(
1124 &glyphs[0],
1125 vec![vec![
1126 CurvePoint::on_curve(0, 0),
1127 CurvePoint::off_curve(1, 1),
1128 CurvePoint::off_curve(3, 3),
1130 CurvePoint::on_curve(4, 4),
1131 ]],
1132 );
1133 assert_contour_points(
1134 &glyphs[1],
1135 vec![vec![
1136 CurvePoint::on_curve(5, 5),
1137 CurvePoint::off_curve(6, 6),
1138 CurvePoint::off_curve(8, 8),
1140 CurvePoint::on_curve(9, 9),
1141 ]],
1142 );
1143 }
1144
1145 #[test]
1146 fn simple_glyphs_from_kurbo_multiple_contours_mixed_segments() {
1147 let paths = make_interpolatable_paths(4, "MLQQZMQLQLZ", true);
1149 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1150
1151 assert_contour_points(
1152 &glyphs[0],
1153 vec![
1154 vec![
1155 CurvePoint::on_curve(0, 0),
1156 CurvePoint::on_curve(1, 1),
1157 CurvePoint::off_curve(2, 2),
1158 CurvePoint::off_curve(4, 4),
1160 ],
1161 vec![
1162 CurvePoint::on_curve(5, 5),
1163 CurvePoint::off_curve(6, 6),
1164 CurvePoint::on_curve(7, 7),
1165 CurvePoint::on_curve(8, 8),
1166 CurvePoint::off_curve(9, 9),
1167 CurvePoint::on_curve(10, 10),
1168 ],
1169 ],
1170 );
1171 }
1172
1173 #[test]
1174 fn simple_glyphs_from_kurbo_all_quad_off_curves() {
1175 let mut path1 = BezPath::new();
1178 path1.move_to((0.0, 1.0));
1179 path1.quad_to((1.0, 1.0), (1.0, 0.0));
1180 path1.quad_to((1.0, -1.0), (0.0, -1.0));
1181 path1.quad_to((-1.0, -1.0), (-1.0, 0.0));
1182 path1.quad_to((-1.0, 1.0), (0.0, 1.0));
1183 path1.close_path();
1184
1185 let mut path2 = path1.clone();
1186 path2.apply_affine(Affine::scale(2.0));
1187
1188 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1189
1190 assert_contour_points(
1191 &glyphs[0],
1192 vec![vec![
1193 CurvePoint::off_curve(1, 1),
1194 CurvePoint::off_curve(1, -1),
1195 CurvePoint::off_curve(-1, -1),
1196 CurvePoint::off_curve(-1, 1),
1197 ]],
1198 );
1199 assert_contour_points(
1200 &glyphs[1],
1201 vec![vec![
1202 CurvePoint::off_curve(2, 2),
1203 CurvePoint::off_curve(2, -2),
1204 CurvePoint::off_curve(-2, -2),
1205 CurvePoint::off_curve(-2, 2),
1206 ]],
1207 );
1208 }
1209
1210 #[test]
1211 fn simple_glyphs_from_kurbo_keep_on_curve_unless_impliable_for_all() {
1212 let mut path1 = BezPath::new();
1213 path1.move_to((0.0, 0.0));
1214 path1.quad_to((0.0, 1.0), (1.0, 1.0)); path1.quad_to((2.0, 1.0), (2.0, 0.0));
1216 path1.line_to((0.0, 0.0));
1217 path1.close_path();
1218
1219 assert_contour_points(
1222 &SimpleGlyph::from_bezpath(&path1).unwrap(),
1223 vec![vec![
1224 CurvePoint::on_curve(0, 0),
1225 CurvePoint::off_curve(0, 1),
1226 CurvePoint::off_curve(2, 1),
1228 CurvePoint::on_curve(2, 0),
1229 ]],
1230 );
1231
1232 let mut path2 = BezPath::new();
1233 path2.move_to((0.0, 0.0));
1234 path2.quad_to((0.0, 2.0), (2.0, 2.0)); path2.quad_to((3.0, 2.0), (3.0, 0.0));
1236 path2.line_to((0.0, 0.0));
1237 path2.close_path();
1238
1239 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1240
1241 assert_contour_points(
1244 &glyphs[0],
1245 vec![vec![
1246 CurvePoint::on_curve(0, 0),
1247 CurvePoint::off_curve(0, 1),
1248 CurvePoint::on_curve(1, 1), CurvePoint::off_curve(2, 1),
1250 CurvePoint::on_curve(2, 0),
1251 ]],
1252 );
1253 assert_contour_points(
1254 &glyphs[1],
1255 vec![vec![
1256 CurvePoint::on_curve(0, 0),
1257 CurvePoint::off_curve(0, 2),
1258 CurvePoint::on_curve(2, 2), CurvePoint::off_curve(3, 2),
1260 CurvePoint::on_curve(3, 0),
1261 ]],
1262 );
1263 }
1264
1265 #[test]
1266 fn simple_glyphs_from_kurbo_2_lines_open() {
1267 let paths = make_interpolatable_paths(2, "MLL", false);
1270 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1271
1272 assert_contour_points(
1273 &glyphs[0],
1274 vec![vec![
1275 CurvePoint::on_curve(0, 0),
1276 CurvePoint::on_curve(1, 1),
1277 CurvePoint::on_curve(2, 2),
1278 ]],
1279 );
1280 assert_contour_points(
1281 &glyphs[1],
1282 vec![vec![
1283 CurvePoint::on_curve(3, 3),
1284 CurvePoint::on_curve(4, 4),
1285 CurvePoint::on_curve(5, 5),
1286 ]],
1287 );
1288 }
1289
1290 #[test]
1291 fn simple_glyphs_from_kurbo_3_lines_open_duplicate_last_pt() {
1292 let paths = make_interpolatable_paths(2, "MLLL", true);
1297 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1298
1299 assert_contour_points(
1300 &glyphs[0],
1301 vec![vec![
1302 CurvePoint::on_curve(0, 0),
1303 CurvePoint::on_curve(1, 1),
1304 CurvePoint::on_curve(2, 2),
1305 CurvePoint::on_curve(0, 0),
1306 ]],
1307 );
1308 assert_contour_points(
1309 &glyphs[1],
1310 vec![vec![
1311 CurvePoint::on_curve(3, 3),
1312 CurvePoint::on_curve(4, 4),
1313 CurvePoint::on_curve(5, 5),
1314 CurvePoint::on_curve(3, 3),
1315 ]],
1316 );
1317 }
1318
1319 #[test]
1320 fn simple_glyphs_from_kurbo_4_lines_closed_duplicate_last_pt() {
1321 for implicit_closing_line in &[true, false] {
1322 let mut path1 = BezPath::new();
1326 path1.move_to((0.0, 0.0));
1327 path1.line_to((0.0, 1.0));
1328 path1.line_to((1.0, 1.0));
1329 path1.line_to((0.0, 0.0));
1330 if !*implicit_closing_line {
1331 path1.line_to((0.0, 0.0));
1332 }
1333 path1.close_path();
1334
1335 let mut path2 = BezPath::new();
1336 path2.move_to((0.0, 0.0));
1337 path2.line_to((0.0, 2.0));
1338 path2.line_to((2.0, 2.0));
1339 path2.line_to((2.0, 0.0));
1340 if !*implicit_closing_line {
1341 path2.line_to((0.0, 0.0));
1342 }
1343 path2.close_path();
1344
1345 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1346
1347 assert_contour_points(
1348 &glyphs[0],
1349 vec![vec![
1350 CurvePoint::on_curve(0, 0),
1351 CurvePoint::on_curve(0, 1),
1352 CurvePoint::on_curve(1, 1),
1353 CurvePoint::on_curve(0, 0), ]],
1355 );
1356 assert_contour_points(
1357 &glyphs[1],
1358 vec![vec![
1359 CurvePoint::on_curve(0, 0),
1360 CurvePoint::on_curve(0, 2),
1361 CurvePoint::on_curve(2, 2),
1362 CurvePoint::on_curve(2, 0),
1363 ]],
1364 );
1365 }
1366 }
1367
1368 #[test]
1369 fn simple_glyphs_from_kurbo_2_quads_1_line_closed_duplicate_last_pt() {
1370 for implicit_closing_line in &[true, false] {
1371 let mut path1 = BezPath::new();
1374 path1.move_to((0.0, 0.0));
1375 path1.quad_to((0.0, 1.0), (1.0, 1.0));
1376 path1.quad_to((1.0, 0.0), (0.0, 0.0));
1377 if !*implicit_closing_line {
1378 path1.line_to((0.0, 0.0));
1379 }
1380 path1.close_path();
1381
1382 let mut path2 = BezPath::new();
1383 path2.move_to((0.0, 0.0));
1384 path2.quad_to((0.0, 2.0), (2.0, 2.0));
1385 path2.quad_to((2.0, 1.0), (1.0, 0.0));
1386 if !*implicit_closing_line {
1387 path2.line_to((0.0, 0.0));
1388 }
1389 path2.close_path();
1390
1391 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1392
1393 assert_contour_points(
1394 &glyphs[0],
1395 vec![vec![
1396 CurvePoint::on_curve(0, 0),
1397 CurvePoint::off_curve(0, 1),
1398 CurvePoint::on_curve(1, 1),
1399 CurvePoint::off_curve(1, 0),
1400 CurvePoint::on_curve(0, 0), ]],
1402 );
1403 assert_contour_points(
1404 &glyphs[1],
1405 vec![vec![
1406 CurvePoint::on_curve(0, 0),
1407 CurvePoint::off_curve(0, 2),
1408 CurvePoint::on_curve(2, 2),
1409 CurvePoint::off_curve(2, 1),
1410 CurvePoint::on_curve(1, 0),
1411 ]],
1412 );
1413 }
1414 }
1415
1416 #[test]
1417 fn simple_glyph_from_kurbo_equidistant_but_not_collinear_points() {
1418 let mut path = BezPath::new();
1419 path.move_to((0.0, 0.0));
1420 path.quad_to((2.0, 2.0), (4.0, 3.0));
1421 path.quad_to((6.0, 2.0), (8.0, 0.0));
1422 path.close_path();
1423
1424 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
1425
1426 assert_contour_points(
1427 &glyph,
1428 vec![vec![
1429 CurvePoint::on_curve(0, 0),
1430 CurvePoint::off_curve(2, 2),
1431 CurvePoint::on_curve(4, 3),
1435 CurvePoint::off_curve(6, 2),
1436 CurvePoint::on_curve(8, 0),
1437 ]],
1438 );
1439 }
1440
1441 #[test]
1442 fn repeatable_flags_basic() {
1443 let flags = [
1444 SimpleGlyphFlags::ON_CURVE_POINT,
1445 SimpleGlyphFlags::X_SHORT_VECTOR,
1446 SimpleGlyphFlags::X_SHORT_VECTOR,
1447 ];
1448 let repeatable = RepeatableFlag::iter_from_flags(flags).collect::<Vec<_>>();
1449 let expected = flags
1450 .into_iter()
1451 .map(|flag| RepeatableFlag { flag, repeat: 0 })
1452 .collect::<Vec<_>>();
1453
1454 assert_eq!(repeatable, expected);
1457 }
1458
1459 #[test]
1460 fn repeatable_flags_repeats() {
1461 let some_dupes = std::iter::repeat_n(SimpleGlyphFlags::ON_CURVE_POINT, 4);
1462 let many_dupes = std::iter::repeat_n(SimpleGlyphFlags::Y_SHORT_VECTOR, 257);
1463 let repeatable =
1464 RepeatableFlag::iter_from_flags(some_dupes.chain(many_dupes)).collect::<Vec<_>>();
1465 assert_eq!(repeatable.len(), 3);
1466 assert_eq!(
1467 repeatable[0],
1468 RepeatableFlag {
1469 flag: SimpleGlyphFlags::ON_CURVE_POINT | SimpleGlyphFlags::REPEAT_FLAG,
1470 repeat: 3
1471 }
1472 );
1473 assert_eq!(
1474 repeatable[1],
1475 RepeatableFlag {
1476 flag: SimpleGlyphFlags::Y_SHORT_VECTOR | SimpleGlyphFlags::REPEAT_FLAG,
1477 repeat: u8::MAX,
1478 }
1479 );
1480
1481 assert_eq!(
1482 repeatable[2],
1483 RepeatableFlag {
1484 flag: SimpleGlyphFlags::Y_SHORT_VECTOR,
1485 repeat: 0,
1486 }
1487 )
1488 }
1489
1490 #[test]
1491 fn mid_points() {
1492 assert!(is_mid_point(
1494 kurbo::Point::new(0.0, 0.0),
1495 kurbo::Point::new(1.0, 1.0),
1496 kurbo::Point::new(2.0, 2.0)
1497 ));
1498 assert!(is_mid_point(
1500 kurbo::Point::new(0.5, 0.5),
1501 kurbo::Point::new(3.0, 3.0),
1502 kurbo::Point::new(5.5, 5.5)
1503 ));
1504 assert!(is_mid_point(
1506 kurbo::Point::new(0.0, 0.0),
1507 kurbo::Point::new(1.00001, 0.99999),
1508 kurbo::Point::new(2.0, 2.0)
1509 ));
1510 assert!(is_mid_point(
1512 kurbo::Point::new(0.0, 0.0),
1513 kurbo::Point::new(-1.499999, 0.500001),
1514 kurbo::Point::new(-2.0, 2.0)
1515 ));
1516 assert!(!is_mid_point(
1518 kurbo::Point::new(0.0, 0.0),
1519 kurbo::Point::new(1.0, 1.5),
1520 kurbo::Point::new(2.0, 2.0)
1521 ));
1522 }
1523}