rsvg/
path_parser.rs

1//! Parser for SVG path data.
2
3use 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    // pub to allow benchmarking
13    Number(f64),
14    Flag(bool),
15    Command(u8),
16    Comma,
17}
18
19use crate::path_parser::Token::{Comma, Command, Flag, Number};
20
21/// Splits an input string from a `path`'s `d` attribute into tokens.
22///
23/// `Lexer` implements the [Iterator] trait; that's how calling code iterates over tokens.
24#[derive(Debug)]
25pub struct Lexer<'a> {
26    // pub to allow benchmarking
27    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    // pub to allow benchmarking
36    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    // The way Flag tokens work is a little annoying. We don't have
54    // any way to distinguish between numbers and flags without context
55    // from the parser. The only time we need to return flags is within the
56    // argument sequence of an elliptical arc, and then we need 2 in a row
57    // or it's an error. So, when the parser gets to that point, it calls
58    // this method and we switch from our usual mode of handling digits as
59    // numbers to looking for two 'flag' characters (either 0 or 1) in a row
60    // (with optional intervening whitespace, and possibly comma tokens.)
61    // Every time we find a flag we decrement flags_required.
62    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        // remember the beginning
114        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        // If you need path parsing to be faster, you can do from_utf8_unchecked to
131        // avoid re-validating all the chars, and std::str::parse<i*> calls are
132        // faster than std::str::parse<f64> for numbers that are not floats.
133
134        // bare unwrap here should be safe since we've already checked all the bytes
135        // in the range
136        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        // eat whitespace
151        self.advance_over_whitespace();
152
153        match self.current {
154            // commas are separators
155            Some((pos, b',')) => {
156                self.advance();
157                Some((pos, Ok(Comma)))
158            }
159
160            // alphabetic chars are commands
161            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 point; adjusted at every command
202    current_x: f64,
203    current_y: f64,
204
205    // Last control point from previous cubic curve command, used to reflect
206    // the new control point for smooth cubic curve commands.
207    cubic_reflection_x: f64,
208    cubic_reflection_y: f64,
209
210    // Last control point from previous quadratic curve command, used to reflect
211    // the new control point for smooth quadratic curve commands.
212    quadratic_reflection_x: f64,
213    quadratic_reflection_y: f64,
214
215    // Start point of current subpath (i.e. position of last moveto);
216    // used for closepath.
217    subpath_start_x: f64,
218    subpath_start_y: f64,
219}
220
221// This is a recursive descent parser for path data in SVG files,
222// as specified in https://www.w3.org/TR/SVG/paths.html#PathDataBNF
223// Some peculiarities:
224//
225// - SVG allows optional commas inside coordinate pairs, and between
226// coordinate pairs.  So, for example, these are equivalent:
227//
228//     M 10 20 30 40
229//     M 10, 20 30, 40
230//     M 10, 20, 30, 40
231//
232// - Whitespace is optional.  These are equivalent:
233//
234//     M10,20 30,40
235//     M10,20,30,40
236//
237//   These are also equivalent:
238//
239//     M-10,20-30-40
240//     M -10 20 -30 -40
241//
242//     M.1-2,3E2-4
243//     M 0.1 -2 300 -4
244impl<'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    // Our match_* methods all either consume the token we requested
269    // and return the unwrapped value, or return an error without
270    // advancing the token stream.
271    //
272    // You can safely use them to probe for a particular kind of token,
273    // fail to match it, and try some other type.
274
275    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        // We can't just do self.match_number() here, because we have to
303        // tell the lexer, if we do find a number, to switch to looking for flags
304        // before we advance it to the next token. Otherwise it will treat the flag
305        // characters as numbers.
306        //
307        // So, first we do the guts of match_number...
308        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        // Then we tell the lexer that we're going to need to find Flag tokens,
316        // *then* we can advance the token stream.
317        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    // Convenience function; like match_number, but eats a leading comma if present.
347    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    // peek_* methods are the twins of match_*, but don't consume the token, and so
366    // can't return ParseError
367
368    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    // This is the entry point for parsing a given blob of path data.
383    // All the parsing just uses various match_* methods to consume tokens
384    // and retrieve the values.
385    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 }, // FIXME: ???
400        }
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        // raise quadratic Bézier to cubic
463        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            // everything happens in the drawto_command() calls.
547        }
548
549        Ok(false)
550    }
551
552    // FIXME: This should not just fail to match 'M' and 'm', but make sure the
553    // command is in the set of drawto command characters.
554    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            // if there is a comma (indicating we should continue to loop), eat the comma
627            // so we're ready at the next start of the loop to process the next token.
628            false
629        } else {
630            // continue to process args in the sequence unless the next token is a comma
631            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    // Skipped for mutation testing; this is just an error formatter.  These errors are just
860    // surfaced as log messages.
861    #[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", // a decimal after exponent start the next number
1041            "",
1042            &[moveto(100.0, 0.5)],
1043            None,
1044        );
1045
1046        test_parser(
1047            "M1e-2.5", // but we are allowed a sign after exponent
1048            "",
1049            &[moveto(0.01, 0.5)],
1050            None,
1051        );
1052
1053        test_parser(
1054            "M1e+2.5", // but we are allowed a sign after exponent
1055            "",
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            //          1     2    3    4   5
1272            "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        // no space required between arc flags
1625        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        // or after...
1631        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        // commas and whitespace are optionally allowed
1637        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            "   ^", // FIXME: why is this not at position 2?
1688            &[],
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        // no non 0|1 chars allowed for flags
2182        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        // https://gitlab.gnome.org/GNOME/librsvg/issues/345
2197        test_parser(
2198            "M.. 1,0 0,100000",
2199            " ^", // FIXME: we have to report position of error in lexer errors to make this right
2200            &[],
2201            Some(ErrorKind::LexError(LexError::UnexpectedByte(b'.'))),
2202        );
2203    }
2204}