1use std::fmt;
4use std::iter::Enumerate;
5use std::str;
6use std::str::Bytes;
7
8use crate::path_builder::*;
9
10#[derive(Debug, PartialEq, Copy, Clone)]
11pub enum Token {
12 Number(f64),
14 Flag(bool),
15 Command(u8),
16 Comma,
17}
18
19use crate::path_parser::Token::{Comma, Command, Flag, Number};
20
21#[derive(Debug)]
25pub struct Lexer<'a> {
26 input: &'a [u8],
28 ci: Enumerate<Bytes<'a>>,
29 current: Option<(usize, u8)>,
30 flags_required: u8,
31}
32
33#[derive(Debug, PartialEq, Copy, Clone)]
34pub enum LexError {
35 ParseFloatError,
37 UnexpectedByte(u8),
38 UnexpectedEof,
39}
40
41impl<'a> Lexer<'_> {
42 pub fn new(input: &'a str) -> Lexer<'a> {
43 let mut ci = input.bytes().enumerate();
44 let current = ci.next();
45 Lexer {
46 input: input.as_bytes(),
47 ci,
48 current,
49 flags_required: 0,
50 }
51 }
52
53 pub fn require_flags(&mut self) {
63 self.flags_required = 2;
64 }
65
66 fn current_pos(&mut self) -> usize {
67 match self.current {
68 None => self.input.len(),
69 Some((pos, _)) => pos,
70 }
71 }
72
73 fn advance(&mut self) {
74 self.current = self.ci.next();
75 }
76
77 fn advance_over_whitespace(&mut self) -> bool {
78 let mut found_some = false;
79 while self.current.is_some() && self.current.unwrap().1.is_ascii_whitespace() {
80 found_some = true;
81 self.current = self.ci.next();
82 }
83 found_some
84 }
85
86 fn advance_over_optional(&mut self, needle: u8) -> bool {
87 match self.current {
88 Some((_, c)) if c == needle => {
89 self.advance();
90 true
91 }
92 _ => false,
93 }
94 }
95
96 fn advance_over_digits(&mut self) -> bool {
97 let mut found_some = false;
98 while self.current.is_some() && self.current.unwrap().1.is_ascii_digit() {
99 found_some = true;
100 self.current = self.ci.next();
101 }
102 found_some
103 }
104
105 fn advance_over_simple_number(&mut self) -> bool {
106 let _ = self.advance_over_optional(b'-') || self.advance_over_optional(b'+');
107 let found_digit = self.advance_over_digits();
108 let _ = self.advance_over_optional(b'.');
109 self.advance_over_digits() || found_digit
110 }
111
112 fn match_number(&mut self) -> Result<Token, LexError> {
113 let (start_pos, _) = self.current.unwrap();
115 if !self.advance_over_simple_number() && start_pos != self.current_pos() {
116 match self.current {
117 None => return Err(LexError::UnexpectedEof),
118 Some((_pos, c)) => return Err(LexError::UnexpectedByte(c)),
119 }
120 }
121 if self.advance_over_optional(b'e') || self.advance_over_optional(b'E') {
122 let _ = self.advance_over_optional(b'-') || self.advance_over_optional(b'+');
123 let _ = self.advance_over_digits();
124 }
125 let end_pos = match self.current {
126 None => self.input.len(),
127 Some((i, _)) => i,
128 };
129
130 match std::str::from_utf8(&self.input[start_pos..end_pos])
137 .unwrap()
138 .parse::<f64>()
139 {
140 Ok(n) => Ok(Number(n)),
141 Err(_e) => Err(LexError::ParseFloatError),
142 }
143 }
144}
145
146impl Iterator for Lexer<'_> {
147 type Item = (usize, Result<Token, LexError>);
148
149 fn next(&mut self) -> Option<Self::Item> {
150 self.advance_over_whitespace();
152
153 match self.current {
154 Some((pos, b',')) => {
156 self.advance();
157 Some((pos, Ok(Comma)))
158 }
159
160 Some((pos, c)) if c.is_ascii_alphabetic() => {
162 let token = Command(c);
163 self.advance();
164 Some((pos, Ok(token)))
165 }
166
167 Some((pos, c)) if self.flags_required > 0 && c.is_ascii_digit() => match c {
168 b'0' => {
169 self.flags_required -= 1;
170 self.advance();
171 Some((pos, Ok(Flag(false))))
172 }
173 b'1' => {
174 self.flags_required -= 1;
175 self.advance();
176 Some((pos, Ok(Flag(true))))
177 }
178 _ => Some((pos, Err(LexError::UnexpectedByte(c)))),
179 },
180
181 Some((pos, c)) if c.is_ascii_digit() || c == b'-' || c == b'+' || c == b'.' => {
182 Some((pos, self.match_number()))
183 }
184
185 Some((pos, c)) => {
186 self.advance();
187 Some((pos, Err(LexError::UnexpectedByte(c))))
188 }
189
190 None => None,
191 }
192 }
193}
194
195pub struct PathParser<'b> {
196 tokens: Lexer<'b>,
197 current_pos_and_token: Option<(usize, Result<Token, LexError>)>,
198
199 builder: &'b mut PathBuilder,
200
201 current_x: f64,
203 current_y: f64,
204
205 cubic_reflection_x: f64,
208 cubic_reflection_y: f64,
209
210 quadratic_reflection_x: f64,
213 quadratic_reflection_y: f64,
214
215 subpath_start_x: f64,
218 subpath_start_y: f64,
219}
220
221impl<'b> PathParser<'b> {
245 pub fn new(builder: &'b mut PathBuilder, path_str: &'b str) -> PathParser<'b> {
246 let mut lexer = Lexer::new(path_str);
247 let pt = lexer.next();
248 PathParser {
249 tokens: lexer,
250 current_pos_and_token: pt,
251
252 builder,
253
254 current_x: 0.0,
255 current_y: 0.0,
256
257 cubic_reflection_x: 0.0,
258 cubic_reflection_y: 0.0,
259
260 quadratic_reflection_x: 0.0,
261 quadratic_reflection_y: 0.0,
262
263 subpath_start_x: 0.0,
264 subpath_start_y: 0.0,
265 }
266 }
267
268 fn match_command(&mut self) -> Result<u8, ParseError> {
276 let result = match &self.current_pos_and_token {
277 Some((_, Ok(Command(c)))) => Ok(*c),
278 Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
279 Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
280 None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
281 };
282 if result.is_ok() {
283 self.current_pos_and_token = self.tokens.next();
284 }
285 result
286 }
287
288 fn match_number(&mut self) -> Result<f64, ParseError> {
289 let result = match &self.current_pos_and_token {
290 Some((_, Ok(Number(n)))) => Ok(*n),
291 Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
292 Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
293 None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
294 };
295 if result.is_ok() {
296 self.current_pos_and_token = self.tokens.next();
297 }
298 result
299 }
300
301 fn match_number_and_flags(&mut self) -> Result<(f64, bool, bool), ParseError> {
302 let n = match &self.current_pos_and_token {
309 Some((_, Ok(Number(n)))) => Ok(*n),
310 Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
311 Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
312 None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
313 }?;
314
315 self.tokens.require_flags();
318 self.current_pos_and_token = self.tokens.next();
319
320 self.eat_optional_comma();
321 let f1 = self.match_flag()?;
322
323 self.eat_optional_comma();
324 let f2 = self.match_flag()?;
325
326 Ok((n, f1, f2))
327 }
328
329 fn match_comma(&mut self) -> Result<(), ParseError> {
330 let result = match &self.current_pos_and_token {
331 Some((_, Ok(Comma))) => Ok(()),
332 Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
333 Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
334 None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
335 };
336 if result.is_ok() {
337 self.current_pos_and_token = self.tokens.next();
338 }
339 result
340 }
341
342 fn eat_optional_comma(&mut self) {
343 let _ = self.match_comma();
344 }
345
346 fn match_comma_number(&mut self) -> Result<f64, ParseError> {
348 self.eat_optional_comma();
349 self.match_number()
350 }
351
352 fn match_flag(&mut self) -> Result<bool, ParseError> {
353 let result = match self.current_pos_and_token {
354 Some((_, Ok(Flag(f)))) => Ok(f),
355 Some((pos, Ok(t))) => Err(ParseError::new(pos, UnexpectedToken(t))),
356 Some((pos, Err(e))) => Err(ParseError::new(pos, LexError(e))),
357 None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
358 };
359 if result.is_ok() {
360 self.current_pos_and_token = self.tokens.next();
361 }
362 result
363 }
364
365 fn peek_command(&mut self) -> Option<u8> {
369 match &self.current_pos_and_token {
370 Some((_, Ok(Command(c)))) => Some(*c),
371 _ => None,
372 }
373 }
374
375 fn peek_number(&mut self) -> Option<f64> {
376 match &self.current_pos_and_token {
377 Some((_, Ok(Number(n)))) => Some(*n),
378 _ => None,
379 }
380 }
381
382 pub fn parse(&mut self) -> Result<(), ParseError> {
386 if self.current_pos_and_token.is_none() {
387 return Ok(());
388 }
389
390 self.moveto_drawto_command_groups()
391 }
392
393 fn error(&self, kind: ErrorKind) -> ParseError {
394 match self.current_pos_and_token {
395 Some((pos, _)) => ParseError {
396 position: pos,
397 kind,
398 },
399 None => ParseError { position: 0, kind }, }
401 }
402
403 fn coordinate_pair(&mut self) -> Result<(f64, f64), ParseError> {
404 Ok((self.match_number()?, self.match_comma_number()?))
405 }
406
407 fn set_current_point(&mut self, x: f64, y: f64) {
408 self.current_x = x;
409 self.current_y = y;
410
411 self.cubic_reflection_x = self.current_x;
412 self.cubic_reflection_y = self.current_y;
413
414 self.quadratic_reflection_x = self.current_x;
415 self.quadratic_reflection_y = self.current_y;
416 }
417
418 fn set_cubic_reflection_and_current_point(&mut self, x3: f64, y3: f64, x4: f64, y4: f64) {
419 self.cubic_reflection_x = x3;
420 self.cubic_reflection_y = y3;
421
422 self.current_x = x4;
423 self.current_y = y4;
424
425 self.quadratic_reflection_x = self.current_x;
426 self.quadratic_reflection_y = self.current_y;
427 }
428
429 fn set_quadratic_reflection_and_current_point(&mut self, a: f64, b: f64, c: f64, d: f64) {
430 self.quadratic_reflection_x = a;
431 self.quadratic_reflection_y = b;
432
433 self.current_x = c;
434 self.current_y = d;
435
436 self.cubic_reflection_x = self.current_x;
437 self.cubic_reflection_y = self.current_y;
438 }
439
440 fn emit_move_to(&mut self, x: f64, y: f64) {
441 self.set_current_point(x, y);
442
443 self.subpath_start_x = self.current_x;
444 self.subpath_start_y = self.current_y;
445
446 self.builder.move_to(self.current_x, self.current_y);
447 }
448
449 fn emit_line_to(&mut self, x: f64, y: f64) {
450 self.set_current_point(x, y);
451
452 self.builder.line_to(self.current_x, self.current_y);
453 }
454
455 fn emit_curve_to(&mut self, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) {
456 self.set_cubic_reflection_and_current_point(x3, y3, x4, y4);
457
458 self.builder.curve_to(x2, y2, x3, y3, x4, y4);
459 }
460
461 fn emit_quadratic_curve_to(&mut self, a: f64, b: f64, c: f64, d: f64) {
462 let x2 = (self.current_x + 2.0 * a) / 3.0;
464 let y2 = (self.current_y + 2.0 * b) / 3.0;
465 let x4 = c;
466 let y4 = d;
467 let x3 = (x4 + 2.0 * a) / 3.0;
468 let y3 = (y4 + 2.0 * b) / 3.0;
469
470 self.set_quadratic_reflection_and_current_point(a, b, c, d);
471
472 self.builder.curve_to(x2, y2, x3, y3, x4, y4);
473 }
474
475 fn emit_arc(
476 &mut self,
477 rx: f64,
478 ry: f64,
479 x_axis_rotation: f64,
480 large_arc: LargeArc,
481 sweep: Sweep,
482 x: f64,
483 y: f64,
484 ) {
485 let (start_x, start_y) = (self.current_x, self.current_y);
486
487 self.set_current_point(x, y);
488
489 self.builder.arc(
490 start_x,
491 start_y,
492 rx,
493 ry,
494 x_axis_rotation,
495 large_arc,
496 sweep,
497 self.current_x,
498 self.current_y,
499 );
500 }
501
502 fn moveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
503 let (mut x, mut y) = self.coordinate_pair()?;
504
505 if !absolute {
506 x += self.current_x;
507 y += self.current_y;
508 }
509
510 self.emit_move_to(x, y);
511
512 if self.match_comma().is_ok() || self.peek_number().is_some() {
513 self.lineto_argument_sequence(absolute)
514 } else {
515 Ok(())
516 }
517 }
518
519 fn moveto(&mut self) -> Result<(), ParseError> {
520 match self.match_command()? {
521 b'M' => self.moveto_argument_sequence(true),
522 b'm' => self.moveto_argument_sequence(false),
523 c => Err(self.error(ErrorKind::UnexpectedCommand(c))),
524 }
525 }
526
527 fn moveto_drawto_command_group(&mut self) -> Result<(), ParseError> {
528 self.moveto()?;
529 self.optional_drawto_commands().map(|_| ())
530 }
531
532 fn moveto_drawto_command_groups(&mut self) -> Result<(), ParseError> {
533 loop {
534 self.moveto_drawto_command_group()?;
535
536 if self.current_pos_and_token.is_none() {
537 break;
538 }
539 }
540
541 Ok(())
542 }
543
544 fn optional_drawto_commands(&mut self) -> Result<bool, ParseError> {
545 while self.drawto_command()? {
546 }
548
549 Ok(false)
550 }
551
552 fn match_if_drawto_command_with_absolute(&mut self) -> Option<(u8, bool)> {
555 let cmd = self.peek_command();
556 let result = match cmd {
557 Some(b'M') => None,
558 Some(b'm') => None,
559 Some(c) => {
560 let c_up = c.to_ascii_uppercase();
561 if c == c_up {
562 Some((c_up, true))
563 } else {
564 Some((c_up, false))
565 }
566 }
567 _ => None,
568 };
569 if result.is_some() {
570 let _ = self.match_command();
571 }
572 result
573 }
574
575 fn drawto_command(&mut self) -> Result<bool, ParseError> {
576 match self.match_if_drawto_command_with_absolute() {
577 Some((b'Z', _)) => {
578 self.emit_close_path();
579 Ok(true)
580 }
581 Some((b'L', abs)) => {
582 self.lineto_argument_sequence(abs)?;
583 Ok(true)
584 }
585 Some((b'H', abs)) => {
586 self.horizontal_lineto_argument_sequence(abs)?;
587 Ok(true)
588 }
589 Some((b'V', abs)) => {
590 self.vertical_lineto_argument_sequence(abs)?;
591 Ok(true)
592 }
593 Some((b'C', abs)) => {
594 self.curveto_argument_sequence(abs)?;
595 Ok(true)
596 }
597 Some((b'S', abs)) => {
598 self.smooth_curveto_argument_sequence(abs)?;
599 Ok(true)
600 }
601 Some((b'Q', abs)) => {
602 self.quadratic_curveto_argument_sequence(abs)?;
603 Ok(true)
604 }
605 Some((b'T', abs)) => {
606 self.smooth_quadratic_curveto_argument_sequence(abs)?;
607 Ok(true)
608 }
609 Some((b'A', abs)) => {
610 self.elliptical_arc_argument_sequence(abs)?;
611 Ok(true)
612 }
613 _ => Ok(false),
614 }
615 }
616
617 fn emit_close_path(&mut self) {
618 let (x, y) = (self.subpath_start_x, self.subpath_start_y);
619 self.set_current_point(x, y);
620
621 self.builder.close_path();
622 }
623
624 fn should_break_arg_sequence(&mut self) -> bool {
625 if self.match_comma().is_ok() {
626 false
629 } else {
630 self.peek_number().is_none()
632 }
633 }
634
635 fn lineto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
636 loop {
637 let (mut x, mut y) = self.coordinate_pair()?;
638
639 if !absolute {
640 x += self.current_x;
641 y += self.current_y;
642 }
643
644 self.emit_line_to(x, y);
645
646 if self.should_break_arg_sequence() {
647 break;
648 }
649 }
650
651 Ok(())
652 }
653
654 fn horizontal_lineto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
655 loop {
656 let mut x = self.match_number()?;
657
658 if !absolute {
659 x += self.current_x;
660 }
661
662 let y = self.current_y;
663
664 self.emit_line_to(x, y);
665
666 if self.should_break_arg_sequence() {
667 break;
668 }
669 }
670
671 Ok(())
672 }
673
674 fn vertical_lineto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
675 loop {
676 let mut y = self.match_number()?;
677
678 if !absolute {
679 y += self.current_y;
680 }
681
682 let x = self.current_x;
683
684 self.emit_line_to(x, y);
685
686 if self.should_break_arg_sequence() {
687 break;
688 }
689 }
690
691 Ok(())
692 }
693
694 fn curveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
695 loop {
696 let (mut x2, mut y2) = self.coordinate_pair()?;
697
698 self.eat_optional_comma();
699 let (mut x3, mut y3) = self.coordinate_pair()?;
700
701 self.eat_optional_comma();
702 let (mut x4, mut y4) = self.coordinate_pair()?;
703
704 if !absolute {
705 x2 += self.current_x;
706 y2 += self.current_y;
707 x3 += self.current_x;
708 y3 += self.current_y;
709 x4 += self.current_x;
710 y4 += self.current_y;
711 }
712
713 self.emit_curve_to(x2, y2, x3, y3, x4, y4);
714
715 if self.should_break_arg_sequence() {
716 break;
717 }
718 }
719
720 Ok(())
721 }
722
723 fn smooth_curveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
724 loop {
725 let (mut x3, mut y3) = self.coordinate_pair()?;
726 self.eat_optional_comma();
727 let (mut x4, mut y4) = self.coordinate_pair()?;
728
729 if !absolute {
730 x3 += self.current_x;
731 y3 += self.current_y;
732 x4 += self.current_x;
733 y4 += self.current_y;
734 }
735
736 let (x2, y2) = (
737 self.current_x + self.current_x - self.cubic_reflection_x,
738 self.current_y + self.current_y - self.cubic_reflection_y,
739 );
740
741 self.emit_curve_to(x2, y2, x3, y3, x4, y4);
742
743 if self.should_break_arg_sequence() {
744 break;
745 }
746 }
747
748 Ok(())
749 }
750
751 fn quadratic_curveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
752 loop {
753 let (mut a, mut b) = self.coordinate_pair()?;
754 self.eat_optional_comma();
755 let (mut c, mut d) = self.coordinate_pair()?;
756
757 if !absolute {
758 a += self.current_x;
759 b += self.current_y;
760 c += self.current_x;
761 d += self.current_y;
762 }
763
764 self.emit_quadratic_curve_to(a, b, c, d);
765
766 if self.should_break_arg_sequence() {
767 break;
768 }
769 }
770
771 Ok(())
772 }
773
774 fn smooth_quadratic_curveto_argument_sequence(
775 &mut self,
776 absolute: bool,
777 ) -> Result<(), ParseError> {
778 loop {
779 let (mut c, mut d) = self.coordinate_pair()?;
780
781 if !absolute {
782 c += self.current_x;
783 d += self.current_y;
784 }
785
786 let (a, b) = (
787 self.current_x + self.current_x - self.quadratic_reflection_x,
788 self.current_y + self.current_y - self.quadratic_reflection_y,
789 );
790
791 self.emit_quadratic_curve_to(a, b, c, d);
792
793 if self.should_break_arg_sequence() {
794 break;
795 }
796 }
797
798 Ok(())
799 }
800
801 fn elliptical_arc_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
802 loop {
803 let rx = self.match_number()?.abs();
804 let ry = self.match_comma_number()?.abs();
805
806 self.eat_optional_comma();
807 let (x_axis_rotation, f1, f2) = self.match_number_and_flags()?;
808
809 let large_arc = LargeArc(f1);
810
811 let sweep = if f2 { Sweep::Positive } else { Sweep::Negative };
812
813 self.eat_optional_comma();
814
815 let (mut x, mut y) = self.coordinate_pair()?;
816
817 if !absolute {
818 x += self.current_x;
819 y += self.current_y;
820 }
821
822 self.emit_arc(rx, ry, x_axis_rotation, large_arc, sweep, x, y);
823
824 if self.should_break_arg_sequence() {
825 break;
826 }
827 }
828
829 Ok(())
830 }
831}
832
833#[derive(Debug, PartialEq)]
834pub enum ErrorKind {
835 UnexpectedToken(Token),
836 UnexpectedCommand(u8),
837 UnexpectedEof,
838 LexError(LexError),
839}
840
841#[derive(Debug, PartialEq)]
842pub struct ParseError {
843 pub position: usize,
844 pub kind: ErrorKind,
845}
846
847impl ParseError {
848 fn new(pos: usize, k: ErrorKind) -> ParseError {
849 ParseError {
850 position: pos,
851 kind: k,
852 }
853 }
854}
855
856use crate::path_parser::ErrorKind::*;
857
858impl fmt::Display for ParseError {
859 #[mutants::skip]
862 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
863 let description = match self.kind {
864 UnexpectedToken(_t) => "unexpected token",
865 UnexpectedCommand(_c) => "unexpected command",
866 UnexpectedEof => "unexpected end of data",
867 LexError(_le) => "error processing token",
868 };
869 write!(f, "error at position {}: {}", self.position, description)
870 }
871}
872
873#[cfg(test)]
874#[rustfmt::skip]
875mod tests {
876 use super::*;
877
878 fn find_error_pos(s: &str) -> Option<usize> {
879 s.find('^')
880 }
881
882 fn make_parse_result(
883 error_pos_str: &str,
884 error_kind: Option<ErrorKind>,
885 ) -> Result<(), ParseError> {
886 if let Some(pos) = find_error_pos(error_pos_str) {
887 Err(ParseError {
888 position: pos,
889 kind: error_kind.unwrap(),
890 })
891 } else {
892 assert!(error_kind.is_none());
893 Ok(())
894 }
895 }
896
897 fn test_parser(
898 path_str: &str,
899 error_pos_str: &str,
900 expected_commands: &[PathCommand],
901 expected_error_kind: Option<ErrorKind>,
902 ) {
903 let expected_result = make_parse_result(error_pos_str, expected_error_kind);
904
905 let mut builder = PathBuilder::default();
906 let result = builder.parse(path_str);
907
908 let path = builder.into_path();
909 let commands = path.iter().collect::<Vec<_>>();
910
911 assert_eq!(expected_commands, commands.as_slice());
912 assert_eq!(expected_result, result);
913 }
914
915 fn moveto(x: f64, y: f64) -> PathCommand {
916 PathCommand::MoveTo(x, y)
917 }
918
919 fn lineto(x: f64, y: f64) -> PathCommand {
920 PathCommand::LineTo(x, y)
921 }
922
923 fn curveto(x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> PathCommand {
924 PathCommand::CurveTo(CubicBezierCurve {
925 pt1: (x2, y2),
926 pt2: (x3, y3),
927 to: (x4, y4),
928 })
929 }
930
931 fn arc(x2: f64, y2: f64, xr: f64, large_arc: bool, sweep: bool,
932 x3: f64, y3: f64, x4: f64, y4: f64) -> PathCommand {
933 PathCommand::Arc(EllipticalArc {
934 r: (x2, y2),
935 x_axis_rotation: xr,
936 large_arc: LargeArc(large_arc),
937 sweep: match sweep {
938 true => Sweep::Positive,
939 false => Sweep::Negative,
940 },
941 from: (x3, y3),
942 to: (x4, y4),
943 })
944 }
945
946 fn closepath() -> PathCommand {
947 PathCommand::ClosePath
948 }
949
950 #[test]
951 fn handles_empty_data() {
952 test_parser(
953 "",
954 "",
955 &Vec::<PathCommand>::new(),
956 None,
957 );
958 }
959
960 #[test]
961 fn handles_numbers() {
962 test_parser(
963 "M 10 20",
964 "",
965 &[moveto(10.0, 20.0)],
966 None,
967 );
968
969 test_parser(
970 "M -10 -20",
971 "",
972 &[moveto(-10.0, -20.0)],
973 None,
974 );
975
976 test_parser(
977 "M .10 0.20",
978 "",
979 &[moveto(0.10, 0.20)],
980 None,
981 );
982
983 test_parser(
984 "M -.10 -0.20",
985 "",
986 &[moveto(-0.10, -0.20)],
987 None,
988 );
989
990 test_parser(
991 "M-.10-0.20",
992 "",
993 &[moveto(-0.10, -0.20)],
994 None,
995 );
996
997 test_parser(
998 "M10.5.50",
999 "",
1000 &[moveto(10.5, 0.50)],
1001 None,
1002 );
1003
1004 test_parser(
1005 "M.10.20",
1006 "",
1007 &[moveto(0.10, 0.20)],
1008 None,
1009 );
1010
1011 test_parser(
1012 "M .10E1 .20e-4",
1013 "",
1014 &[moveto(1.0, 0.000020)],
1015 None,
1016 );
1017
1018 test_parser(
1019 "M-.10E1-.20",
1020 "",
1021 &[moveto(-1.0, -0.20)],
1022 None,
1023 );
1024
1025 test_parser(
1026 "M10.10E2 -0.20e3",
1027 "",
1028 &[moveto(1010.0, -200.0)],
1029 None,
1030 );
1031
1032 test_parser(
1033 "M-10.10E2-0.20e-3",
1034 "",
1035 &[moveto(-1010.0, -0.00020)],
1036 None,
1037 );
1038
1039 test_parser(
1040 "M1e2.5", "",
1042 &[moveto(100.0, 0.5)],
1043 None,
1044 );
1045
1046 test_parser(
1047 "M1e-2.5", "",
1049 &[moveto(0.01, 0.5)],
1050 None,
1051 );
1052
1053 test_parser(
1054 "M1e+2.5", "",
1056 &[moveto(100.0, 0.5)],
1057 None,
1058 );
1059 }
1060
1061 #[test]
1062 fn detects_bogus_numbers() {
1063 test_parser(
1064 "M+",
1065 " ^",
1066 &[],
1067 Some(ErrorKind::LexError(LexError::UnexpectedEof)),
1068 );
1069
1070 test_parser(
1071 "M-",
1072 " ^",
1073 &[],
1074 Some(ErrorKind::LexError(LexError::UnexpectedEof)),
1075 );
1076
1077 test_parser(
1078 "M+x",
1079 " ^",
1080 &[],
1081 Some(ErrorKind::LexError(LexError::UnexpectedByte(b'x'))),
1082 );
1083
1084 test_parser(
1085 "M10e",
1086 " ^",
1087 &[],
1088 Some(ErrorKind::LexError(LexError::ParseFloatError)),
1089 );
1090
1091 test_parser(
1092 "M10ex",
1093 " ^",
1094 &[],
1095 Some(ErrorKind::LexError(LexError::ParseFloatError)),
1096 );
1097
1098 test_parser(
1099 "M10e-",
1100 " ^",
1101 &[],
1102 Some(ErrorKind::LexError(LexError::ParseFloatError)),
1103 );
1104
1105 test_parser(
1106 "M10e+x",
1107 " ^",
1108 &[],
1109 Some(ErrorKind::LexError(LexError::ParseFloatError)),
1110 );
1111
1112 test_parser(
1113 "M 10 ;",
1114 " ^",
1115 &[],
1116 Some(ErrorKind::LexError(LexError::UnexpectedByte(b';'))),
1117 );
1118 }
1119
1120 #[test]
1121 fn handles_numbers_with_comma() {
1122 test_parser(
1123 "M 10, 20",
1124 "",
1125 &[moveto(10.0, 20.0)],
1126 None,
1127 );
1128
1129 test_parser(
1130 "M -10,-20",
1131 "",
1132 &[moveto(-10.0, -20.0)],
1133 None,
1134 );
1135
1136 test_parser(
1137 "M.10 , 0.20",
1138 "",
1139 &[moveto(0.10, 0.20)],
1140 None,
1141 );
1142
1143 test_parser(
1144 "M -.10, -0.20 ",
1145 "",
1146 &[moveto(-0.10, -0.20)],
1147 None,
1148 );
1149
1150 test_parser(
1151 "M-.10-0.20",
1152 "",
1153 &[moveto(-0.10, -0.20)],
1154 None,
1155 );
1156
1157 test_parser(
1158 "M.10.20",
1159 "",
1160 &[moveto(0.10, 0.20)],
1161 None,
1162 );
1163
1164 test_parser(
1165 "M .10E1,.20e-4",
1166 "",
1167 &[moveto(1.0, 0.000020)],
1168 None,
1169 );
1170
1171 test_parser(
1172 "M-.10E-2,-.20",
1173 "",
1174 &[moveto(-0.0010, -0.20)],
1175 None,
1176 );
1177
1178 test_parser(
1179 "M10.10E2,-0.20e3",
1180 "",
1181 &[moveto(1010.0, -200.0)],
1182 None,
1183 );
1184
1185 test_parser(
1186 "M-10.10E2,-0.20e-3",
1187 "",
1188 &[moveto(-1010.0, -0.00020)],
1189 None,
1190 );
1191 }
1192
1193 #[test]
1194 fn handles_single_moveto() {
1195 test_parser(
1196 "M 10 20 ",
1197 "",
1198 &[moveto(10.0, 20.0)],
1199 None,
1200 );
1201
1202 test_parser(
1203 "M10,20 ",
1204 "",
1205 &[moveto(10.0, 20.0)],
1206 None,
1207 );
1208
1209 test_parser(
1210 "M10 20 ",
1211 "",
1212 &[moveto(10.0, 20.0)],
1213 None,
1214 );
1215
1216 test_parser(
1217 " M10,20 ",
1218 "",
1219 &[moveto(10.0, 20.0)],
1220 None,
1221 );
1222 }
1223
1224 #[test]
1225 fn handles_relative_moveto() {
1226 test_parser(
1227 "m10 20",
1228 "",
1229 &[moveto(10.0, 20.0)],
1230 None,
1231 );
1232 }
1233
1234 #[test]
1235 fn handles_absolute_moveto_with_implicit_lineto() {
1236 test_parser(
1237 "M10 20 30 40",
1238 "",
1239 &[moveto(10.0, 20.0), lineto(30.0, 40.0)],
1240 None,
1241 );
1242
1243 test_parser(
1244 "M10,20,30,40",
1245 "",
1246 &[moveto(10.0, 20.0), lineto(30.0, 40.0)],
1247 None,
1248 );
1249
1250 test_parser(
1251 "M.1-2,3E2-4",
1252 "",
1253 &[moveto(0.1, -2.0), lineto(300.0, -4.0)],
1254 None,
1255 );
1256 }
1257
1258 #[test]
1259 fn handles_relative_moveto_with_implicit_lineto() {
1260 test_parser(
1261 "m10 20 30 40",
1262 "",
1263 &[moveto(10.0, 20.0), lineto(40.0, 60.0)],
1264 None,
1265 );
1266 }
1267
1268 #[test]
1269 fn handles_relative_moveto_with_relative_lineto_sequence() {
1270 test_parser(
1271 "m 46,447 l 0,0.5 -1,0 -1,0 0,1 0,12",
1273 "",
1274 &vec![moveto(46.0, 447.0), lineto(46.0, 447.5), lineto(45.0, 447.5),
1275 lineto(44.0, 447.5), lineto(44.0, 448.5), lineto(44.0, 460.5)],
1276 None,
1277 );
1278 }
1279
1280 #[test]
1281 fn handles_absolute_moveto_with_implicit_linetos() {
1282 test_parser(
1283 "M10,20 30,40,50 60",
1284 "",
1285 &[moveto(10.0, 20.0), lineto(30.0, 40.0), lineto(50.0, 60.0)],
1286 None,
1287 );
1288 }
1289
1290 #[test]
1291 fn handles_relative_moveto_with_implicit_linetos() {
1292 test_parser(
1293 "m10 20 30 40 50 60",
1294 "",
1295 &[moveto(10.0, 20.0), lineto(40.0, 60.0), lineto(90.0, 120.0)],
1296 None,
1297 );
1298 }
1299
1300 #[test]
1301 fn handles_absolute_moveto_moveto() {
1302 test_parser(
1303 "M10 20 M 30 40",
1304 "",
1305 &[moveto(10.0, 20.0), moveto(30.0, 40.0)],
1306 None,
1307 );
1308 }
1309
1310 #[test]
1311 fn handles_relative_moveto_moveto() {
1312 test_parser(
1313 "m10 20 m 30 40",
1314 "",
1315 &[moveto(10.0, 20.0), moveto(40.0, 60.0)],
1316 None,
1317 );
1318 }
1319
1320 #[test]
1321 fn handles_relative_moveto_lineto_moveto() {
1322 test_parser(
1323 "m10 20 30 40 m 50 60",
1324 "",
1325 &[moveto(10.0, 20.0), lineto(40.0, 60.0), moveto(90.0, 120.0)],
1326 None,
1327 );
1328 }
1329
1330 #[test]
1331 fn handles_absolute_moveto_lineto() {
1332 test_parser(
1333 "M10 20 L30,40",
1334 "",
1335 &[moveto(10.0, 20.0), lineto(30.0, 40.0)],
1336 None,
1337 );
1338 }
1339
1340 #[test]
1341 fn handles_relative_moveto_lineto() {
1342 test_parser(
1343 "m10 20 l30,40",
1344 "",
1345 &[moveto(10.0, 20.0), lineto(40.0, 60.0)],
1346 None,
1347 );
1348 }
1349
1350 #[test]
1351 fn handles_relative_moveto_lineto_lineto_abs_lineto() {
1352 test_parser(
1353 "m10 20 30 40l30,40,50 60L200,300",
1354 "",
1355 &vec![
1356 moveto(10.0, 20.0),
1357 lineto(40.0, 60.0),
1358 lineto(70.0, 100.0),
1359 lineto(120.0, 160.0),
1360 lineto(200.0, 300.0),
1361 ],
1362 None,
1363 );
1364 }
1365
1366 #[test]
1367 fn handles_horizontal_lineto() {
1368 test_parser(
1369 "M10 20 H30",
1370 "",
1371 &[moveto(10.0, 20.0), lineto(30.0, 20.0)],
1372 None,
1373 );
1374
1375 test_parser(
1376 "M10 20 H30 40",
1377 "",
1378 &[moveto(10.0, 20.0), lineto(30.0, 20.0), lineto(40.0, 20.0)],
1379 None,
1380 );
1381
1382 test_parser(
1383 "M10 20 H30,40-50",
1384 "",
1385 &vec![
1386 moveto(10.0, 20.0),
1387 lineto(30.0, 20.0),
1388 lineto(40.0, 20.0),
1389 lineto(-50.0, 20.0),
1390 ],
1391 None,
1392 );
1393
1394 test_parser(
1395 "m10 20 h30,40-50",
1396 "",
1397 &vec![
1398 moveto(10.0, 20.0),
1399 lineto(40.0, 20.0),
1400 lineto(80.0, 20.0),
1401 lineto(30.0, 20.0),
1402 ],
1403 None,
1404 );
1405 }
1406
1407 #[test]
1408 fn handles_vertical_lineto() {
1409 test_parser(
1410 "M10 20 V30",
1411 "",
1412 &[moveto(10.0, 20.0), lineto(10.0, 30.0)],
1413 None,
1414 );
1415
1416 test_parser(
1417 "M10 20 V30 40",
1418 "",
1419 &[moveto(10.0, 20.0), lineto(10.0, 30.0), lineto(10.0, 40.0)],
1420 None,
1421 );
1422
1423 test_parser(
1424 "M10 20 V30,40-50",
1425 "",
1426 &vec![
1427 moveto(10.0, 20.0),
1428 lineto(10.0, 30.0),
1429 lineto(10.0, 40.0),
1430 lineto(10.0, -50.0),
1431 ],
1432 None,
1433 );
1434
1435 test_parser(
1436 "m10 20 v30,40-50",
1437 "",
1438 &vec![
1439 moveto(10.0, 20.0),
1440 lineto(10.0, 50.0),
1441 lineto(10.0, 90.0),
1442 lineto(10.0, 40.0),
1443 ],
1444 None,
1445 );
1446 }
1447
1448 #[test]
1449 fn handles_curveto() {
1450 test_parser(
1451 "M10 20 C 30,40 50 60-70,80",
1452 "",
1453 &[moveto(10.0, 20.0),
1454 curveto(30.0, 40.0, 50.0, 60.0, -70.0, 80.0)],
1455 None,
1456 );
1457
1458 test_parser(
1459 "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140",
1460 "",
1461 &[moveto(10.0, 20.0),
1462 curveto(30.0, 40.0, 50.0, 60.0, -70.0, 80.0),
1463 curveto(90.0, 100.0, 110.0, 120.0, 130.0, 140.0)],
1464 None,
1465 );
1466
1467 test_parser(
1468 "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
1469 "",
1470 &[moveto(10.0, 20.0),
1471 curveto(40.0, 60.0, 60.0, 80.0, -60.0, 100.0),
1472 curveto(30.0, 200.0, 50.0, 220.0, 70.0, 240.0)],
1473 None,
1474 );
1475
1476 test_parser(
1477 "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
1478 "",
1479 &[moveto(10.0, 20.0),
1480 curveto(40.0, 60.0, 60.0, 80.0, -60.0, 100.0),
1481 curveto(30.0, 200.0, 50.0, 220.0, 70.0, 240.0)],
1482 None,
1483 );
1484 }
1485
1486 #[test]
1487 fn handles_smooth_curveto() {
1488 test_parser(
1489 "M10 20 S 30,40-50,60",
1490 "",
1491 &[moveto(10.0, 20.0),
1492 curveto(10.0, 20.0, 30.0, 40.0, -50.0, 60.0)],
1493 None,
1494 );
1495
1496 test_parser(
1497 "M10 20 S 30,40 50 60-70,80,90 100",
1498 "",
1499 &[moveto(10.0, 20.0),
1500 curveto(10.0, 20.0, 30.0, 40.0, 50.0, 60.0),
1501 curveto(70.0, 80.0, -70.0, 80.0, 90.0, 100.0)],
1502 None,
1503 );
1504
1505 test_parser(
1506 "m10 20 s 30,40 50 60-70,80,90 100",
1507 "",
1508 &[moveto(10.0, 20.0),
1509 curveto(10.0, 20.0, 40.0, 60.0, 60.0, 80.0),
1510 curveto(80.0, 100.0, -10.0, 160.0, 150.0, 180.0)],
1511 None,
1512 );
1513 }
1514
1515 #[test]
1516 fn handles_quadratic_curveto() {
1517 test_parser(
1518 "M10 20 Q30 40 50 60",
1519 "",
1520 &[moveto(10.0, 20.0),
1521 curveto(
1522 70.0 / 3.0,
1523 100.0 / 3.0,
1524 110.0 / 3.0,
1525 140.0 / 3.0,
1526 50.0,
1527 60.0,
1528 )],
1529 None,
1530 );
1531
1532 test_parser(
1533 "M10 20 Q30 40 50 60,70,80-90 100",
1534 "",
1535 &[moveto(10.0, 20.0),
1536 curveto(
1537 70.0 / 3.0,
1538 100.0 / 3.0,
1539 110.0 / 3.0,
1540 140.0 / 3.0,
1541 50.0,
1542 60.0,
1543 ),
1544 curveto(
1545 190.0 / 3.0,
1546 220.0 / 3.0,
1547 50.0 / 3.0,
1548 260.0 / 3.0,
1549 -90.0,
1550 100.0,
1551 )],
1552 None,
1553 );
1554
1555 test_parser(
1556 "m10 20 q 30,40 50 60-70,80 90 100",
1557 "",
1558 &[moveto(10.0, 20.0),
1559 curveto(
1560 90.0 / 3.0,
1561 140.0 / 3.0,
1562 140.0 / 3.0,
1563 200.0 / 3.0,
1564 60.0,
1565 80.0,
1566 ),
1567 curveto(
1568 40.0 / 3.0,
1569 400.0 / 3.0,
1570 130.0 / 3.0,
1571 500.0 / 3.0,
1572 150.0,
1573 180.0,
1574 )],
1575 None,
1576 );
1577 }
1578
1579 #[test]
1580 fn handles_smooth_quadratic_curveto() {
1581 test_parser(
1582 "M10 20 T30 40",
1583 "",
1584 &[moveto(10.0, 20.0),
1585 curveto(10.0, 20.0, 50.0 / 3.0, 80.0 / 3.0, 30.0, 40.0)],
1586 None,
1587 );
1588
1589 test_parser(
1590 "M10 20 Q30 40 50 60 T70 80",
1591 "",
1592 &[moveto(10.0, 20.0),
1593 curveto(
1594 70.0 / 3.0,
1595 100.0 / 3.0,
1596 110.0 / 3.0,
1597 140.0 / 3.0,
1598 50.0,
1599 60.0,
1600 ),
1601 curveto(190.0 / 3.0, 220.0 / 3.0, 70.0, 80.0, 70.0, 80.0)],
1602 None,
1603 );
1604
1605 test_parser(
1606 "m10 20 q 30,40 50 60t-70,80",
1607 "",
1608 &[moveto(10.0, 20.0),
1609 curveto(
1610 90.0 / 3.0,
1611 140.0 / 3.0,
1612 140.0 / 3.0,
1613 200.0 / 3.0,
1614 60.0,
1615 80.0,
1616 ),
1617 curveto(220.0 / 3.0, 280.0 / 3.0, 50.0, 120.0, -10.0, 160.0)],
1618 None,
1619 );
1620 }
1621
1622 #[test]
1623 fn handles_elliptical_arc() {
1624 test_parser("M 1 2 A 1 2 3 00 6 7",
1626 "",
1627 &[moveto(1.0, 2.0),
1628 arc(1.0, 2.0, 3.0, false, false, 1.0, 2.0, 6.0, 7.0)],
1629 None);
1630 test_parser("M 1 2 A 1 2 3 016 7",
1632 "",
1633 &[moveto(1.0, 2.0),
1634 arc(1.0, 2.0, 3.0, false, true, 1.0, 2.0, 6.0, 7.0)],
1635 None);
1636 test_parser("M 1 2 A 1 2 3 10,6 7",
1638 "",
1639 &[moveto(1.0, 2.0),
1640 arc(1.0, 2.0, 3.0, true, false, 1.0, 2.0, 6.0, 7.0)],
1641 None);
1642 test_parser("M 1 2 A 1 2 3 1,16, 7",
1643 "",
1644 &[moveto(1.0, 2.0),
1645 arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1646 None);
1647 test_parser("M 1 2 A 1 2 3 1,1 6 7",
1648 "",
1649 &[moveto(1.0, 2.0),
1650 arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1651 None);
1652 test_parser("M 1 2 A 1 2 3 1 1 6 7",
1653 "",
1654 &[moveto(1.0, 2.0),
1655 arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1656 None);
1657 test_parser("M 1 2 A 1 2 3 1 16 7",
1658 "",
1659 &[moveto(1.0, 2.0),
1660 arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1661 None);
1662 }
1663
1664 #[test]
1665 fn handles_close_path() {
1666 test_parser("M10 20 Z", "", &[moveto(10.0, 20.0), closepath()], None);
1667
1668 test_parser(
1669 "m10 20 30 40 m 50 60 70 80 90 100z",
1670 "",
1671 &vec![
1672 moveto(10.0, 20.0),
1673 lineto(40.0, 60.0),
1674 moveto(90.0, 120.0),
1675 lineto(160.0, 200.0),
1676 lineto(250.0, 300.0),
1677 closepath(),
1678 ],
1679 None,
1680 );
1681 }
1682
1683 #[test]
1684 fn first_command_must_be_moveto() {
1685 test_parser(
1686 " L10 20",
1687 " ^", &[],
1689 Some(ErrorKind::UnexpectedCommand(b'L')),
1690 );
1691 }
1692
1693 #[test]
1694 fn moveto_args() {
1695 test_parser(
1696 "M",
1697 " ^",
1698 &[],
1699 Some(ErrorKind::UnexpectedEof),
1700 );
1701
1702 test_parser(
1703 "M,",
1704 " ^",
1705 &[],
1706 Some(ErrorKind::UnexpectedToken(Comma)),
1707 );
1708
1709 test_parser(
1710 "M10",
1711 " ^",
1712 &[],
1713 Some(ErrorKind::UnexpectedEof),
1714 );
1715
1716 test_parser(
1717 "M10,",
1718 " ^",
1719 &[],
1720 Some(ErrorKind::UnexpectedEof),
1721 );
1722
1723 test_parser(
1724 "M10x",
1725 " ^",
1726 &[],
1727 Some(ErrorKind::UnexpectedToken(Command(b'x'))),
1728 );
1729
1730 test_parser(
1731 "M10,x",
1732 " ^",
1733 &[],
1734 Some(ErrorKind::UnexpectedToken(Command(b'x'))),
1735 );
1736 }
1737
1738 #[test]
1739 fn moveto_implicit_lineto_args() {
1740 test_parser(
1741 "M10-20,",
1742 " ^",
1743 &[moveto(10.0, -20.0)],
1744 Some(ErrorKind::UnexpectedEof),
1745 );
1746
1747 test_parser(
1748 "M10-20-30",
1749 " ^",
1750 &[moveto(10.0, -20.0)],
1751 Some(ErrorKind::UnexpectedEof),
1752 );
1753
1754 test_parser(
1755 "M10-20-30 x",
1756 " ^",
1757 &[moveto(10.0, -20.0)],
1758 Some(ErrorKind::UnexpectedToken(Command(b'x'))),
1759 );
1760 }
1761
1762 #[test]
1763 fn closepath_no_args() {
1764 test_parser(
1765 "M10-20z10",
1766 " ^",
1767 &[moveto(10.0, -20.0), closepath()],
1768 Some(ErrorKind::UnexpectedToken(Number(10.0))),
1769 );
1770
1771 test_parser(
1772 "M10-20z,",
1773 " ^",
1774 &[moveto(10.0, -20.0), closepath()],
1775 Some(ErrorKind::UnexpectedToken(Comma)),
1776 );
1777 }
1778
1779 #[test]
1780 fn lineto_args() {
1781 test_parser(
1782 "M10-20L10",
1783 " ^",
1784 &[moveto(10.0, -20.0)],
1785 Some(ErrorKind::UnexpectedEof),
1786 );
1787
1788 test_parser(
1789 "M 10,10 L 20,20,30",
1790 " ^",
1791 &[moveto(10.0, 10.0), lineto(20.0, 20.0)],
1792 Some(ErrorKind::UnexpectedEof),
1793 );
1794
1795 test_parser(
1796 "M 10,10 L 20,20,",
1797 " ^",
1798 &[moveto(10.0, 10.0), lineto(20.0, 20.0)],
1799 Some(ErrorKind::UnexpectedEof),
1800 );
1801 }
1802
1803 #[test]
1804 fn horizontal_lineto_args() {
1805 test_parser(
1806 "M10-20H",
1807 " ^",
1808 &[moveto(10.0, -20.0)],
1809 Some(ErrorKind::UnexpectedEof),
1810 );
1811
1812 test_parser(
1813 "M10-20H,",
1814 " ^",
1815 &[moveto(10.0, -20.0)],
1816 Some(ErrorKind::UnexpectedToken(Comma)),
1817 );
1818
1819 test_parser(
1820 "M10-20H30,",
1821 " ^",
1822 &[moveto(10.0, -20.0), lineto(30.0, -20.0)],
1823 Some(ErrorKind::UnexpectedEof),
1824 );
1825 }
1826
1827 #[test]
1828 fn vertical_lineto_args() {
1829 test_parser(
1830 "M10-20v",
1831 " ^",
1832 &[moveto(10.0, -20.0)],
1833 Some(ErrorKind::UnexpectedEof),
1834 );
1835
1836 test_parser(
1837 "M10-20v,",
1838 " ^",
1839 &[moveto(10.0, -20.0)],
1840 Some(ErrorKind::UnexpectedToken(Comma)),
1841 );
1842
1843 test_parser(
1844 "M10-20v30,",
1845 " ^",
1846 &[moveto(10.0, -20.0), lineto(10.0, 10.0)],
1847 Some(ErrorKind::UnexpectedEof),
1848 );
1849 }
1850
1851 #[test]
1852 fn curveto_args() {
1853 test_parser(
1854 "M10-20C1",
1855 " ^",
1856 &[moveto(10.0, -20.0)],
1857 Some(ErrorKind::UnexpectedEof),
1858 );
1859 test_parser(
1860 "M10-20C1,",
1861 " ^",
1862 &[moveto(10.0, -20.0)],
1863 Some(ErrorKind::UnexpectedEof),
1864 );
1865
1866 test_parser(
1867 "M10-20C1 2",
1868 " ^",
1869 &[moveto(10.0, -20.0)],
1870 Some(ErrorKind::UnexpectedEof),
1871 );
1872 test_parser(
1873 "M10-20C1,2,",
1874 " ^",
1875 &[moveto(10.0, -20.0)],
1876 Some(ErrorKind::UnexpectedEof),
1877 );
1878
1879 test_parser(
1880 "M10-20C1 2 3",
1881 " ^",
1882 &[moveto(10.0, -20.0)],
1883 Some(ErrorKind::UnexpectedEof),
1884 );
1885 test_parser(
1886 "M10-20C1,2,3",
1887 " ^",
1888 &[moveto(10.0, -20.0)],
1889 Some(ErrorKind::UnexpectedEof),
1890 );
1891 test_parser(
1892 "M10-20C1,2,3,",
1893 " ^",
1894 &[moveto(10.0, -20.0)],
1895 Some(ErrorKind::UnexpectedEof),
1896 );
1897
1898 test_parser(
1899 "M10-20C1 2 3 4",
1900 " ^",
1901 &[moveto(10.0, -20.0)],
1902 Some(ErrorKind::UnexpectedEof),
1903 );
1904 test_parser(
1905 "M10-20C1,2,3,4",
1906 " ^",
1907 &[moveto(10.0, -20.0)],
1908 Some(ErrorKind::UnexpectedEof),
1909 );
1910 test_parser(
1911 "M10-20C1,2,3,4,",
1912 " ^",
1913 &[moveto(10.0, -20.0)],
1914 Some(ErrorKind::UnexpectedEof),
1915 );
1916
1917 test_parser(
1918 "M10-20C1 2 3 4 5",
1919 " ^",
1920 &[moveto(10.0, -20.0)],
1921 Some(ErrorKind::UnexpectedEof),
1922 );
1923 test_parser(
1924 "M10-20C1,2,3,4,5",
1925 " ^",
1926 &[moveto(10.0, -20.0)],
1927 Some(ErrorKind::UnexpectedEof),
1928 );
1929 test_parser(
1930 "M10-20C1,2,3,4,5,",
1931 " ^",
1932 &[moveto(10.0, -20.0)],
1933 Some(ErrorKind::UnexpectedEof),
1934 );
1935
1936 test_parser(
1937 "M10-20C1,2,3,4,5,6,",
1938 " ^",
1939 &[moveto(10.0, -20.0), curveto(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)],
1940 Some(ErrorKind::UnexpectedEof),
1941 );
1942 }
1943
1944 #[test]
1945 fn smooth_curveto_args() {
1946 test_parser(
1947 "M10-20S1",
1948 " ^",
1949 &[moveto(10.0, -20.0)],
1950 Some(ErrorKind::UnexpectedEof),
1951 );
1952 test_parser(
1953 "M10-20S1,",
1954 " ^",
1955 &[moveto(10.0, -20.0)],
1956 Some(ErrorKind::UnexpectedEof),
1957 );
1958
1959 test_parser(
1960 "M10-20S1 2",
1961 " ^",
1962 &[moveto(10.0, -20.0)],
1963 Some(ErrorKind::UnexpectedEof),
1964 );
1965 test_parser(
1966 "M10-20S1,2,",
1967 " ^",
1968 &[moveto(10.0, -20.0)],
1969 Some(ErrorKind::UnexpectedEof),
1970 );
1971
1972 test_parser(
1973 "M10-20S1 2 3",
1974 " ^",
1975 &[moveto(10.0, -20.0)],
1976 Some(ErrorKind::UnexpectedEof),
1977 );
1978 test_parser(
1979 "M10-20S1,2,3",
1980 " ^",
1981 &[moveto(10.0, -20.0)],
1982 Some(ErrorKind::UnexpectedEof),
1983 );
1984 test_parser(
1985 "M10-20S1,2,3,",
1986 " ^",
1987 &[moveto(10.0, -20.0)],
1988 Some(ErrorKind::UnexpectedEof),
1989 );
1990
1991 test_parser(
1992 "M10-20S1,2,3,4,",
1993 " ^",
1994 &[moveto(10.0, -20.0),
1995 curveto(10.0, -20.0, 1.0, 2.0, 3.0, 4.0)],
1996 Some(ErrorKind::UnexpectedEof),
1997 );
1998 }
1999
2000 #[test]
2001 fn quadratic_bezier_curveto_args() {
2002 test_parser(
2003 "M10-20Q1",
2004 " ^",
2005 &[moveto(10.0, -20.0)],
2006 Some(ErrorKind::UnexpectedEof),
2007 );
2008 test_parser(
2009 "M10-20Q1,",
2010 " ^",
2011 &[moveto(10.0, -20.0)],
2012 Some(ErrorKind::UnexpectedEof),
2013 );
2014
2015 test_parser(
2016 "M10-20Q1 2",
2017 " ^",
2018 &[moveto(10.0, -20.0)],
2019 Some(ErrorKind::UnexpectedEof),
2020 );
2021 test_parser(
2022 "M10-20Q1,2,",
2023 " ^",
2024 &[moveto(10.0, -20.0)],
2025 Some(ErrorKind::UnexpectedEof),
2026 );
2027
2028 test_parser(
2029 "M10-20Q1 2 3",
2030 " ^",
2031 &[moveto(10.0, -20.0)],
2032 Some(ErrorKind::UnexpectedEof),
2033 );
2034 test_parser(
2035 "M10-20Q1,2,3",
2036 " ^",
2037 &[moveto(10.0, -20.0)],
2038 Some(ErrorKind::UnexpectedEof),
2039 );
2040 test_parser(
2041 "M10-20Q1,2,3,",
2042 " ^",
2043 &[moveto(10.0, -20.0)],
2044 Some(ErrorKind::UnexpectedEof),
2045 );
2046
2047 test_parser(
2048 "M10 20 Q30 40 50 60,",
2049 " ^",
2050 &[moveto(10.0, 20.0),
2051 curveto(
2052 70.0 / 3.0,
2053 100.0 / 3.0,
2054 110.0 / 3.0,
2055 140.0 / 3.0,
2056 50.0,
2057 60.0,
2058 )],
2059 Some(ErrorKind::UnexpectedEof),
2060 );
2061 }
2062
2063 #[test]
2064 fn smooth_quadratic_bezier_curveto_args() {
2065 test_parser(
2066 "M10-20T1",
2067 " ^",
2068 &[moveto(10.0, -20.0)],
2069 Some(ErrorKind::UnexpectedEof),
2070 );
2071 test_parser(
2072 "M10-20T1,",
2073 " ^",
2074 &[moveto(10.0, -20.0)],
2075 Some(ErrorKind::UnexpectedEof),
2076 );
2077
2078 test_parser(
2079 "M10 20 T30 40,",
2080 " ^",
2081 &[moveto(10.0, 20.0),
2082 curveto(10.0, 20.0, 50.0 / 3.0, 80.0 / 3.0, 30.0, 40.0)],
2083 Some(ErrorKind::UnexpectedEof),
2084 );
2085 }
2086
2087 #[test]
2088 fn elliptical_arc_args() {
2089 test_parser(
2090 "M10-20A1",
2091 " ^",
2092 &[moveto(10.0, -20.0)],
2093 Some(ErrorKind::UnexpectedEof),
2094 );
2095 test_parser(
2096 "M10-20A1,",
2097 " ^",
2098 &[moveto(10.0, -20.0)],
2099 Some(ErrorKind::UnexpectedEof),
2100 );
2101
2102 test_parser(
2103 "M10-20A1 2",
2104 " ^",
2105 &[moveto(10.0, -20.0)],
2106 Some(ErrorKind::UnexpectedEof),
2107 );
2108 test_parser(
2109 "M10-20A1 2,",
2110 " ^",
2111 &[moveto(10.0, -20.0)],
2112 Some(ErrorKind::UnexpectedEof),
2113 );
2114
2115 test_parser(
2116 "M10-20A1 2 3",
2117 " ^",
2118 &[moveto(10.0, -20.0)],
2119 Some(ErrorKind::UnexpectedEof),
2120 );
2121 test_parser(
2122 "M10-20A1 2 3,",
2123 " ^",
2124 &[moveto(10.0, -20.0)],
2125 Some(ErrorKind::UnexpectedEof),
2126 );
2127
2128 test_parser(
2129 "M10-20A1 2 3 4",
2130 " ^",
2131 &[moveto(10.0, -20.0)],
2132 Some(ErrorKind::LexError(LexError::UnexpectedByte(b'4'))),
2133 );
2134
2135 test_parser(
2136 "M10-20A1 2 3 1",
2137 " ^",
2138 &[moveto(10.0, -20.0)],
2139 Some(ErrorKind::UnexpectedEof),
2140 );
2141 test_parser(
2142 "M10-20A1 2 3,1,",
2143 " ^",
2144 &[moveto(10.0, -20.0)],
2145 Some(ErrorKind::UnexpectedEof),
2146 );
2147
2148 test_parser(
2149 "M10-20A1 2 3 1 5",
2150 " ^",
2151 &[moveto(10.0, -20.0)],
2152 Some(ErrorKind::LexError(LexError::UnexpectedByte(b'5'))),
2153 );
2154
2155 test_parser(
2156 "M10-20A1 2 3 1 1",
2157 " ^",
2158 &[moveto(10.0, -20.0)],
2159 Some(ErrorKind::UnexpectedEof),
2160 );
2161 test_parser(
2162 "M10-20A1 2 3,1,1,",
2163 " ^",
2164 &[moveto(10.0, -20.0)],
2165 Some(ErrorKind::UnexpectedEof),
2166 );
2167
2168 test_parser(
2169 "M10-20A1 2 3 1 1 6",
2170 " ^",
2171 &[moveto(10.0, -20.0)],
2172 Some(ErrorKind::UnexpectedEof),
2173 );
2174 test_parser(
2175 "M10-20A1 2 3,1,1,6,",
2176 " ^",
2177 &[moveto(10.0, -20.0)],
2178 Some(ErrorKind::UnexpectedEof),
2179 );
2180
2181 test_parser("M 1 2 A 1 2 3 1.0 0.0 6 7",
2183 " ^",
2184 &[moveto(1.0, 2.0)],
2185 Some(ErrorKind::UnexpectedToken(Number(0.0))));
2186
2187 test_parser("M10-20A1 2 3,1,1,6,7,",
2188 " ^",
2189 &[moveto(10.0, -20.0),
2190 arc(1.0, 2.0, 3.0, true, true, 10.0, -20.0, 6.0, 7.0)],
2191 Some(ErrorKind::UnexpectedEof));
2192 }
2193
2194 #[test]
2195 fn bugs() {
2196 test_parser(
2198 "M.. 1,0 0,100000",
2199 " ^", &[],
2201 Some(ErrorKind::LexError(LexError::UnexpectedByte(b'.'))),
2202 );
2203 }
2204}