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