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#[derive(Debug)]
22pub struct Lexer<'a> {
23    // pub to allow benchmarking
24    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    // pub to allow benchmarking
33    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    // The way Flag tokens work is a little annoying. We don't have
51    // any way to distinguish between numbers and flags without context
52    // from the parser. The only time we need to return flags is within the
53    // argument sequence of an elliptical arc, and then we need 2 in a row
54    // or it's an error. So, when the parser gets to that point, it calls
55    // this method and we switch from our usual mode of handling digits as
56    // numbers to looking for two 'flag' characters (either 0 or 1) in a row
57    // (with optional intervening whitespace, and possibly comma tokens.)
58    // Every time we find a flag we decrement flags_required.
59    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        // remember the beginning
111        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        // If you need path parsing to be faster, you can do from_utf8_unchecked to
128        // avoid re-validating all the chars, and std::str::parse<i*> calls are
129        // faster than std::str::parse<f64> for numbers that are not floats.
130
131        // bare unwrap here should be safe since we've already checked all the bytes
132        // in the range
133        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        // eat whitespace
148        self.advance_over_whitespace();
149
150        match self.current {
151            // commas are separators
152            Some((pos, b',')) => {
153                self.advance();
154                Some((pos, Ok(Comma)))
155            }
156
157            // alphabetic chars are commands
158            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 point; adjusted at every command
199    current_x: f64,
200    current_y: f64,
201
202    // Last control point from previous cubic curve command, used to reflect
203    // the new control point for smooth cubic curve commands.
204    cubic_reflection_x: f64,
205    cubic_reflection_y: f64,
206
207    // Last control point from previous quadratic curve command, used to reflect
208    // the new control point for smooth quadratic curve commands.
209    quadratic_reflection_x: f64,
210    quadratic_reflection_y: f64,
211
212    // Start point of current subpath (i.e. position of last moveto);
213    // used for closepath.
214    subpath_start_x: f64,
215    subpath_start_y: f64,
216}
217
218// This is a recursive descent parser for path data in SVG files,
219// as specified in https://www.w3.org/TR/SVG/paths.html#PathDataBNF
220// Some peculiarities:
221//
222// - SVG allows optional commas inside coordinate pairs, and between
223// coordinate pairs.  So, for example, these are equivalent:
224//
225//     M 10 20 30 40
226//     M 10, 20 30, 40
227//     M 10, 20, 30, 40
228//
229// - Whitespace is optional.  These are equivalent:
230//
231//     M10,20 30,40
232//     M10,20,30,40
233//
234//   These are also equivalent:
235//
236//     M-10,20-30-40
237//     M -10 20 -30 -40
238//
239//     M.1-2,3E2-4
240//     M 0.1 -2 300 -4
241impl<'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    // Our match_* methods all either consume the token we requested
266    // and return the unwrapped value, or return an error without
267    // advancing the token stream.
268    //
269    // You can safely use them to probe for a particular kind of token,
270    // fail to match it, and try some other type.
271
272    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        // We can't just do self.match_number() here, because we have to
300        // tell the lexer, if we do find a number, to switch to looking for flags
301        // before we advance it to the next token. Otherwise it will treat the flag
302        // characters as numbers.
303        //
304        // So, first we do the guts of match_number...
305        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        // Then we tell the lexer that we're going to need to find Flag tokens,
313        // *then* we can advance the token stream.
314        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    // Convenience function; like match_number, but eats a leading comma if present.
344    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    // peek_* methods are the twins of match_*, but don't consume the token, and so
363    // can't return ParseError
364
365    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    // This is the entry point for parsing a given blob of path data.
380    // All the parsing just uses various match_* methods to consume tokens
381    // and retrieve the values.
382    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 }, // FIXME: ???
397        }
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        // raise quadratic Bézier to cubic
460        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            // everything happens in the drawto_command() calls.
544        }
545
546        Ok(false)
547    }
548
549    // FIXME: This should not just fail to match 'M' and 'm', but make sure the
550    // command is in the set of drawto command characters.
551    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            // if there is a comma (indicating we should continue to loop), eat the comma
624            // so we're ready at the next start of the loop to process the next token.
625            false
626        } else {
627            // continue to process args in the sequence unless the next token is a comma
628            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", // a decimal after exponent start the next number
1035            "",
1036            &[moveto(100.0, 0.5)],
1037            None,
1038        );
1039
1040        test_parser(
1041            "M1e-2.5", // but we are allowed a sign after exponent
1042            "",
1043            &[moveto(0.01, 0.5)],
1044            None,
1045        );
1046
1047        test_parser(
1048            "M1e+2.5", // but we are allowed a sign after exponent
1049            "",
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            //          1     2    3    4   5
1259            "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        // no space required between arc flags
1612        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        // or after...
1618        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        // commas and whitespace are optionally allowed
1624        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            "   ^", // FIXME: why is this not at position 2?
1675            &[],
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        // no non 0|1 chars allowed for flags
2169        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        // https://gitlab.gnome.org/GNOME/librsvg/issues/345
2184        test_parser(
2185            "M.. 1,0 0,100000",
2186            " ^", // FIXME: we have to report position of error in lexer errors to make this right
2187            &[],
2188            Some(ErrorKind::LexError(LexError::UnexpectedByte(b'.'))),
2189        );
2190    }
2191}