1
//! Parser for SVG path data.
2

            
3
use std::fmt;
4
use std::iter::Enumerate;
5
use std::str;
6
use std::str::Bytes;
7

            
8
use crate::path_builder::*;
9

            
10
14
#[derive(Debug, PartialEq, Copy, Clone)]
11
pub enum Token {
12
    // pub to allow benchmarking
13
2
    Number(f64),
14
    Flag(bool),
15
3
    Command(u8),
16
    Comma,
17
}
18

            
19
use crate::path_parser::Token::{Comma, Command, Flag, Number};
20

            
21
#[derive(Debug)]
22
pub 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
14
#[derive(Debug, PartialEq, Copy, Clone)]
31
pub enum LexError {
32
    // pub to allow benchmarking
33
    ParseFloatError,
34
4
    UnexpectedByte(u8),
35
    UnexpectedEof,
36
}
37

            
38
impl<'a> Lexer<'_> {
39
1933
    pub fn new(input: &'a str) -> Lexer<'a> {
40
1933
        let mut ci = input.bytes().enumerate();
41
1933
        let current = ci.next();
42
1933
        Lexer {
43
1933
            input: input.as_bytes(),
44
1933
            ci,
45
            current,
46
            flags_required: 0,
47
        }
48
1933
    }
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
1189
    pub fn require_flags(&mut self) {
60
1189
        self.flags_required = 2;
61
1189
    }
62

            
63
4
    fn current_pos(&mut self) -> usize {
64
4
        match self.current {
65
2
            None => self.input.len(),
66
2
            Some((pos, _)) => pos,
67
        }
68
4
    }
69

            
70
135265
    fn advance(&mut self) {
71
135265
        self.current = self.ci.next();
72
135265
    }
73

            
74
142989
    fn advance_over_whitespace(&mut self) -> bool {
75
142989
        let mut found_some = false;
76
200235
        while self.current.is_some() && self.current.unwrap().1.is_ascii_whitespace() {
77
57246
            found_some = true;
78
57246
            self.current = self.ci.next();
79
        }
80
142989
        found_some
81
142989
    }
82

            
83
395505
    fn advance_over_optional(&mut self, needle: u8) -> bool {
84
395505
        match self.current {
85
393595
            Some((_, c)) if c == needle => {
86
79445
                self.advance();
87
79445
                true
88
79445
            }
89
316060
            _ => false,
90
        }
91
395505
    }
92

            
93
170650
    fn advance_over_digits(&mut self) -> bool {
94
170650
        let mut found_some = false;
95
469166
        while self.current.is_some() && self.current.unwrap().1.is_ascii_digit() {
96
298516
            found_some = true;
97
298516
            self.current = self.ci.next();
98
        }
99
170650
        found_some
100
170650
    }
101

            
102
85286
    fn advance_over_simple_number(&mut self) -> bool {
103
85286
        let _ = self.advance_over_optional(b'-') || self.advance_over_optional(b'+');
104
85286
        let found_digit = self.advance_over_digits();
105
85286
        let _ = self.advance_over_optional(b'.');
106
85286
        self.advance_over_digits() || found_digit
107
85286
    }
108

            
109
85175
    fn match_number(&mut self) -> Result<Token, LexError> {
110
        // remember the beginning
111
85175
        let (start_pos, _) = self.current.unwrap();
112
85175
        if !self.advance_over_simple_number() && start_pos != self.current_pos() {
113
4
            match self.current {
114
2
                None => return Err(LexError::UnexpectedEof),
115
2
                Some((_pos, c)) => return Err(LexError::UnexpectedByte(c)),
116
            }
117
        }
118
85171
        if self.advance_over_optional(b'e') || self.advance_over_optional(b'E') {
119
383
            let _ = self.advance_over_optional(b'-') || self.advance_over_optional(b'+');
120
383
            let _ = self.advance_over_digits();
121
        }
122
85213
        let end_pos = match self.current {
123
509
            None => self.input.len(),
124
84704
            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
85213
        match std::str::from_utf8(&self.input[start_pos..end_pos])
134
            .unwrap()
135
            .parse::<f64>()
136
        {
137
85209
            Ok(n) => Ok(Number(n)),
138
4
            Err(_e) => Err(LexError::ParseFloatError),
139
        }
140
85217
    }
141
}
142

            
143
impl Iterator for Lexer<'_> {
144
    type Item = (usize, Result<Token, LexError>);
145

            
146
143149
    fn next(&mut self) -> Option<Self::Item> {
147
        // eat whitespace
148
143149
        self.advance_over_whitespace();
149

            
150
143149
        match self.current {
151
            // commas are separators
152
38424
            Some((pos, b',')) => {
153
38424
                self.advance();
154
38424
                Some((pos, Ok(Comma)))
155
38424
            }
156

            
157
            // alphabetic chars are commands
158
102824
            Some((pos, c)) if c.is_ascii_alphabetic() => {
159
15222
                let token = Command(c);
160
15222
                self.advance();
161
15222
                Some((pos, Ok(token)))
162
15222
            }
163

            
164
87602
            Some((pos, c)) if self.flags_required > 0 && c.is_ascii_digit() => match c {
165
                b'0' => {
166
1422
                    self.flags_required -= 1;
167
1422
                    self.advance();
168
1422
                    Some((pos, Ok(Flag(false))))
169
                }
170
                b'1' => {
171
938
                    self.flags_required -= 1;
172
938
                    self.advance();
173
938
                    Some((pos, Ok(Flag(true))))
174
                }
175
4
                _ => Some((pos, Err(LexError::UnexpectedByte(c)))),
176
            },
177

            
178
85238
            Some((pos, c)) if c.is_ascii_digit() || c == b'-' || c == b'+' || c == b'.' => {
179
85236
                Some((pos, self.match_number()))
180
85236
            }
181

            
182
2
            Some((pos, c)) => {
183
2
                self.advance();
184
2
                Some((pos, Err(LexError::UnexpectedByte(c))))
185
2
            }
186

            
187
1901
            None => None,
188
        }
189
143149
    }
190
}
191

            
192
pub 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
241
impl<'b> PathParser<'b> {
242
1933
    pub fn new(builder: &'b mut PathBuilder, path_str: &'b str) -> PathParser<'b> {
243
1933
        let mut lexer = Lexer::new(path_str);
244
1933
        let pt = lexer.next();
245
1933
        PathParser {
246
1933
            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
1933
    }
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
15223
    fn match_command(&mut self) -> Result<u8, ParseError> {
273
15223
        let result = match &self.current_pos_and_token {
274
15219
            Some((_, Ok(Command(c)))) => Ok(*c),
275
2
            Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
276
2
            Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
277
            None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
278
        };
279
15223
        if result.is_ok() {
280
15218
            self.current_pos_and_token = self.tokens.next();
281
        }
282
15223
        result
283
15223
    }
284

            
285
84156
    fn match_number(&mut self) -> Result<f64, ParseError> {
286
84156
        let result = match &self.current_pos_and_token {
287
84088
            Some((_, Ok(Number(n)))) => Ok(*n),
288
9
            Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
289
8
            Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
290
51
            None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
291
        };
292
84156
        if result.is_ok() {
293
84082
            self.current_pos_and_token = self.tokens.next();
294
        }
295
84156
        result
296
84156
    }
297

            
298
1191
    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
2382
        let n = match &self.current_pos_and_token {
306
1189
            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
2
            None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
310
2
        }?;
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
1189
        self.tokens.require_flags();
315
1189
        self.current_pos_and_token = self.tokens.next();
316

            
317
1189
        self.eat_optional_comma();
318
1189
        let f1 = self.match_flag()?;
319

            
320
1183
        self.eat_optional_comma();
321
1183
        let f2 = self.match_flag()?;
322

            
323
1177
        Ok((n, f1, f2))
324
1191
    }
325

            
326
87690
    fn match_comma(&mut self) -> Result<(), ParseError> {
327
87690
        let result = match &self.current_pos_and_token {
328
38429
            Some((_, Ok(Comma))) => Ok(()),
329
48727
            Some((pos, Ok(t))) => Err(ParseError::new(*pos, UnexpectedToken(*t))),
330
6
            Some((pos, Err(e))) => Err(ParseError::new(*pos, LexError(*e))),
331
528
            None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
332
        };
333
87690
        if result.is_ok() {
334
38426
            self.current_pos_and_token = self.tokens.next();
335
        }
336
87690
        result
337
87690
    }
338

            
339
64693
    fn eat_optional_comma(&mut self) {
340
64693
        let _ = self.match_comma();
341
64693
    }
342

            
343
    // Convenience function; like match_number, but eats a leading comma if present.
344
41426
    fn match_comma_number(&mut self) -> Result<f64, ParseError> {
345
41426
        self.eat_optional_comma();
346
41426
        self.match_number()
347
41426
    }
348

            
349
2372
    fn match_flag(&mut self) -> Result<bool, ParseError> {
350
2372
        let result = match self.current_pos_and_token {
351
2360
            Some((_, Ok(Flag(f)))) => Ok(f),
352
4
            Some((pos, Ok(t))) => Err(ParseError::new(pos, UnexpectedToken(t))),
353
4
            Some((pos, Err(e))) => Err(ParseError::new(pos, LexError(e))),
354
4
            None => Err(ParseError::new(self.tokens.input.len(), UnexpectedEof)),
355
        };
356
2372
        if result.is_ok() {
357
2360
            self.current_pos_and_token = self.tokens.next();
358
        }
359
2372
        result
360
2372
    }
361

            
362
    // peek_* methods are the twins of match_*, but don't consume the token, and so
363
    // can't return ParseError
364

            
365
15137
    fn peek_command(&mut self) -> Option<u8> {
366
15137
        match &self.current_pos_and_token {
367
13291
            Some((_, Ok(Command(c)))) => Some(*c),
368
1846
            _ => None,
369
        }
370
15137
    }
371

            
372
22965
    fn peek_number(&mut self) -> Option<f64> {
373
22965
        match &self.current_pos_and_token {
374
10056
            Some((_, Ok(Number(n)))) => Some(*n),
375
12909
            _ => None,
376
        }
377
22965
    }
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
1932
    pub fn parse(&mut self) -> Result<(), ParseError> {
383
1932
        if self.current_pos_and_token.is_none() {
384
2
            return Ok(());
385
        }
386

            
387
1930
        self.moveto_drawto_command_groups()
388
1932
    }
389

            
390
1
    fn error(&self, kind: ErrorKind) -> ParseError {
391
1
        match self.current_pos_and_token {
392
1
            Some((pos, _)) => ParseError {
393
                position: pos,
394
                kind,
395
1
            },
396
            None => ParseError { position: 0, kind }, // FIXME: ???
397
        }
398
1
    }
399

            
400
40265
    fn coordinate_pair(&mut self) -> Result<(f64, f64), ParseError> {
401
40265
        Ok((self.match_number()?, self.match_comma_number()?))
402
40265
    }
403

            
404
15871
    fn set_current_point(&mut self, x: f64, y: f64) {
405
15871
        self.current_x = x;
406
15871
        self.current_y = y;
407

            
408
15871
        self.cubic_reflection_x = self.current_x;
409
15871
        self.cubic_reflection_y = self.current_y;
410

            
411
15871
        self.quadratic_reflection_x = self.current_x;
412
15871
        self.quadratic_reflection_y = self.current_y;
413
15871
    }
414

            
415
9252
    fn set_cubic_reflection_and_current_point(&mut self, x3: f64, y3: f64, x4: f64, y4: f64) {
416
9252
        self.cubic_reflection_x = x3;
417
9252
        self.cubic_reflection_y = y3;
418

            
419
9252
        self.current_x = x4;
420
9252
        self.current_y = y4;
421

            
422
9252
        self.quadratic_reflection_x = self.current_x;
423
9252
        self.quadratic_reflection_y = self.current_y;
424
9252
    }
425

            
426
100
    fn set_quadratic_reflection_and_current_point(&mut self, a: f64, b: f64, c: f64, d: f64) {
427
100
        self.quadratic_reflection_x = a;
428
100
        self.quadratic_reflection_y = b;
429

            
430
100
        self.current_x = c;
431
100
        self.current_y = d;
432

            
433
100
        self.cubic_reflection_x = self.current_x;
434
100
        self.cubic_reflection_y = self.current_y;
435
100
    }
436

            
437
2883
    fn emit_move_to(&mut self, x: f64, y: f64) {
438
2883
        self.set_current_point(x, y);
439

            
440
2883
        self.subpath_start_x = self.current_x;
441
2883
        self.subpath_start_y = self.current_y;
442

            
443
2883
        self.builder.move_to(self.current_x, self.current_y);
444
2883
    }
445

            
446
9586
    fn emit_line_to(&mut self, x: f64, y: f64) {
447
9586
        self.set_current_point(x, y);
448

            
449
9586
        self.builder.line_to(self.current_x, self.current_y);
450
9586
    }
451

            
452
9252
    fn emit_curve_to(&mut self, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) {
453
9252
        self.set_cubic_reflection_and_current_point(x3, y3, x4, y4);
454

            
455
9252
        self.builder.curve_to(x2, y2, x3, y3, x4, y4);
456
9252
    }
457

            
458
100
    fn emit_quadratic_curve_to(&mut self, a: f64, b: f64, c: f64, d: f64) {
459
        // raise quadratic Bézier to cubic
460
100
        let x2 = (self.current_x + 2.0 * a) / 3.0;
461
100
        let y2 = (self.current_y + 2.0 * b) / 3.0;
462
        let x4 = c;
463
        let y4 = d;
464
100
        let x3 = (x4 + 2.0 * a) / 3.0;
465
100
        let y3 = (y4 + 2.0 * b) / 3.0;
466

            
467
100
        self.set_quadratic_reflection_and_current_point(a, b, c, d);
468

            
469
100
        self.builder.curve_to(x2, y2, x3, y3, x4, y4);
470
100
    }
471

            
472
1173
    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
1173
        let (start_x, start_y) = (self.current_x, self.current_y);
483

            
484
1173
        self.set_current_point(x, y);
485

            
486
2346
        self.builder.arc(
487
            start_x,
488
            start_y,
489
            rx,
490
            ry,
491
            x_axis_rotation,
492
            large_arc,
493
            sweep,
494
1173
            self.current_x,
495
1173
            self.current_y,
496
        );
497
1173
    }
498

            
499
2899
    fn moveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
500
2899
        let (mut x, mut y) = self.coordinate_pair()?;
501

            
502
2883
        if !absolute {
503
1972
            x += self.current_x;
504
1972
            y += self.current_y;
505
        }
506

            
507
2883
        self.emit_move_to(x, y);
508

            
509
2883
        if self.match_comma().is_ok() || self.peek_number().is_some() {
510
741
            self.lineto_argument_sequence(absolute)
511
        } else {
512
2142
            Ok(())
513
        }
514
2899
    }
515

            
516
2903
    fn moveto(&mut self) -> Result<(), ParseError> {
517
2903
        match self.match_command()? {
518
927
            b'M' => self.moveto_argument_sequence(true),
519
1971
            b'm' => self.moveto_argument_sequence(false),
520
1
            c => Err(self.error(ErrorKind::UnexpectedCommand(c))),
521
        }
522
2903
    }
523

            
524
2902
    fn moveto_drawto_command_group(&mut self) -> Result<(), ParseError> {
525
2902
        self.moveto()?;
526
5692
        self.optional_drawto_commands().map(|_| ())
527
2902
    }
528

            
529
1929
    fn moveto_drawto_command_groups(&mut self) -> Result<(), ParseError> {
530
1929
        loop {
531
2901
            self.moveto_drawto_command_group()?;
532

            
533
2815
            if self.current_pos_and_token.is_none() {
534
                break;
535
            }
536
        }
537

            
538
1843
        Ok(())
539
1929
    }
540

            
541
2877
    fn optional_drawto_commands(&mut self) -> Result<bool, ParseError> {
542
15138
        while self.drawto_command()? {
543
            // everything happens in the drawto_command() calls.
544
        }
545

            
546
2815
        Ok(false)
547
2877
    }
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
15140
    fn match_if_drawto_command_with_absolute(&mut self) -> Option<(u8, bool)> {
552
15140
        let cmd = self.peek_command();
553
15140
        let result = match cmd {
554
138
            Some(b'M') => None,
555
832
            Some(b'm') => None,
556
12324
            Some(c) => {
557
12324
                let c_up = c.to_ascii_uppercase();
558
12324
                if c == c_up {
559
1920
                    Some((c_up, true))
560
                } else {
561
10404
                    Some((c_up, false))
562
                }
563
            }
564
1846
            _ => None,
565
        };
566
15140
        if result.is_some() {
567
12323
            let _ = self.match_command();
568
        }
569
15140
        result
570
15140
    }
571

            
572
15143
    fn drawto_command(&mut self) -> Result<bool, ParseError> {
573
15143
        match self.match_if_drawto_command_with_absolute() {
574
            Some((b'Z', _)) => {
575
2233
                self.emit_close_path();
576
2233
                Ok(true)
577
            }
578
3146
            Some((b'L', abs)) => {
579
3146
                self.lineto_argument_sequence(abs)?;
580
3143
                Ok(true)
581
3143
            }
582
674
            Some((b'H', abs)) => {
583
674
                self.horizontal_lineto_argument_sequence(abs)?;
584
671
                Ok(true)
585
671
            }
586
586
            Some((b'V', abs)) => {
587
586
                self.vertical_lineto_argument_sequence(abs)?;
588
583
                Ok(true)
589
583
            }
590
4633
            Some((b'C', abs)) => {
591
4633
                self.curveto_argument_sequence(abs)?;
592
4619
                Ok(true)
593
4619
            }
594
44
            Some((b'S', abs)) => {
595
44
                self.smooth_curveto_argument_sequence(abs)?;
596
36
                Ok(true)
597
36
            }
598
78
            Some((b'Q', abs)) => {
599
78
                self.quadratic_curveto_argument_sequence(abs)?;
600
70
                Ok(true)
601
70
            }
602
22
            Some((b'T', abs)) => {
603
22
                self.smooth_quadratic_curveto_argument_sequence(abs)?;
604
19
                Ok(true)
605
19
            }
606
912
            Some((b'A', abs)) => {
607
16055
                self.elliptical_arc_argument_sequence(abs)?;
608
891
                Ok(true)
609
891
            }
610
2815
            _ => Ok(false),
611
        }
612
15143
    }
613

            
614
2233
    fn emit_close_path(&mut self) {
615
2233
        let (x, y) = (self.subpath_start_x, self.subpath_start_y);
616
2233
        self.set_current_point(x, y);
617

            
618
2233
        self.builder.close_path();
619
2233
    }
620

            
621
20110
    fn should_break_arg_sequence(&mut self) -> bool {
622
20110
        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
21
            false
626
        } else {
627
            // continue to process args in the sequence unless the next token is a comma
628
20089
            self.peek_number().is_none()
629
        }
630
20110
    }
631

            
632
3887
    fn lineto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
633
3887
        loop {
634
8271
            let (mut x, mut y) = self.coordinate_pair()?;
635

            
636
8264
            if !absolute {
637
7242
                x += self.current_x;
638
7242
                y += self.current_y;
639
            }
640

            
641
8264
            self.emit_line_to(x, y);
642

            
643
8264
            if self.should_break_arg_sequence() {
644
                break;
645
            }
646
        }
647

            
648
3880
        Ok(())
649
3887
    }
650

            
651
674
    fn horizontal_lineto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
652
674
        loop {
653
709
            let mut x = self.match_number()?;
654

            
655
706
            if !absolute {
656
600
                x += self.current_x;
657
            }
658

            
659
706
            let y = self.current_y;
660

            
661
706
            self.emit_line_to(x, y);
662

            
663
706
            if self.should_break_arg_sequence() {
664
                break;
665
            }
666
        }
667

            
668
671
        Ok(())
669
674
    }
670

            
671
586
    fn vertical_lineto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
672
586
        loop {
673
619
            let mut y = self.match_number()?;
674

            
675
616
            if !absolute {
676
528
                y += self.current_y;
677
            }
678

            
679
616
            let x = self.current_x;
680

            
681
616
            self.emit_line_to(x, y);
682

            
683
616
            if self.should_break_arg_sequence() {
684
                break;
685
            }
686
        }
687

            
688
583
        Ok(())
689
586
    }
690

            
691
4634
    fn curveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
692
4634
        loop {
693
9211
            let (mut x2, mut y2) = self.coordinate_pair()?;
694

            
695
9208
            self.eat_optional_comma();
696
9208
            let (mut x3, mut y3) = self.coordinate_pair()?;
697

            
698
9203
            self.eat_optional_comma();
699
9203
            let (mut x4, mut y4) = self.coordinate_pair()?;
700

            
701
9197
            if !absolute {
702
8616
                x2 += self.current_x;
703
8616
                y2 += self.current_y;
704
8616
                x3 += self.current_x;
705
8616
                y3 += self.current_y;
706
8616
                x4 += self.current_x;
707
8616
                y4 += self.current_y;
708
            }
709

            
710
9197
            self.emit_curve_to(x2, y2, x3, y3, x4, y4);
711

            
712
9197
            if self.should_break_arg_sequence() {
713
                break;
714
            }
715
        }
716

            
717
4620
        Ok(())
718
4634
    }
719

            
720
44
    fn smooth_curveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
721
44
        loop {
722
64
            let (mut x3, mut y3) = self.coordinate_pair()?;
723
61
            self.eat_optional_comma();
724
61
            let (mut x4, mut y4) = self.coordinate_pair()?;
725

            
726
56
            if !absolute {
727
37
                x3 += self.current_x;
728
37
                y3 += self.current_y;
729
37
                x4 += self.current_x;
730
37
                y4 += self.current_y;
731
            }
732

            
733
56
            let (x2, y2) = (
734
56
                self.current_x + self.current_x - self.cubic_reflection_x,
735
56
                self.current_y + self.current_y - self.cubic_reflection_y,
736
            );
737

            
738
56
            self.emit_curve_to(x2, y2, x3, y3, x4, y4);
739

            
740
56
            if self.should_break_arg_sequence() {
741
                break;
742
            }
743
        }
744

            
745
36
        Ok(())
746
44
    }
747

            
748
78
    fn quadratic_curveto_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
749
78
        loop {
750
85
            let (mut a, mut b) = self.coordinate_pair()?;
751
82
            self.eat_optional_comma();
752
82
            let (mut c, mut d) = self.coordinate_pair()?;
753

            
754
77
            if !absolute {
755
12
                a += self.current_x;
756
12
                b += self.current_y;
757
12
                c += self.current_x;
758
12
                d += self.current_y;
759
            }
760

            
761
77
            self.emit_quadratic_curve_to(a, b, c, d);
762

            
763
77
            if self.should_break_arg_sequence() {
764
                break;
765
            }
766
        }
767

            
768
70
        Ok(())
769
78
    }
770

            
771
22
    fn smooth_quadratic_curveto_argument_sequence(
772
        &mut self,
773
        absolute: bool,
774
    ) -> Result<(), ParseError> {
775
22
        loop {
776
26
            let (mut c, mut d) = self.coordinate_pair()?;
777

            
778
23
            if !absolute {
779
9
                c += self.current_x;
780
9
                d += self.current_y;
781
            }
782

            
783
23
            let (a, b) = (
784
23
                self.current_x + self.current_x - self.quadratic_reflection_x,
785
23
                self.current_y + self.current_y - self.quadratic_reflection_y,
786
            );
787

            
788
23
            self.emit_quadratic_curve_to(a, b, c, d);
789

            
790
23
            if self.should_break_arg_sequence() {
791
                break;
792
            }
793
        }
794

            
795
19
        Ok(())
796
22
    }
797

            
798
912
    fn elliptical_arc_argument_sequence(&mut self, absolute: bool) -> Result<(), ParseError> {
799
912
        loop {
800
1194
            let rx = self.match_number()?.abs();
801
1193
            let ry = self.match_comma_number()?.abs();
802

            
803
1191
            self.eat_optional_comma();
804
1191
            let (x_axis_rotation, f1, f2) = self.match_number_and_flags()?;
805

            
806
1177
            let large_arc = LargeArc(f1);
807

            
808
1177
            let sweep = if f2 { Sweep::Positive } else { Sweep::Negative };
809

            
810
1177
            self.eat_optional_comma();
811

            
812
1177
            let (mut x, mut y) = self.coordinate_pair()?;
813

            
814
1173
            if !absolute {
815
976
                x += self.current_x;
816
976
                y += self.current_y;
817
            }
818

            
819
1173
            self.emit_arc(rx, ry, x_axis_rotation, large_arc, sweep, x, y);
820

            
821
1173
            if self.should_break_arg_sequence() {
822
                break;
823
            }
824
        }
825

            
826
891
        Ok(())
827
912
    }
828
}
829

            
830
97
#[derive(Debug, PartialEq)]
831
pub enum ErrorKind {
832
9
    UnexpectedToken(Token),
833
1
    UnexpectedCommand(u8),
834
    UnexpectedEof,
835
10
    LexError(LexError),
836
}
837

            
838
154
#[derive(Debug, PartialEq)]
839
pub struct ParseError {
840
77
    pub position: usize,
841
77
    pub kind: ErrorKind,
842
}
843

            
844
impl ParseError {
845
49338
    fn new(pos: usize, k: ErrorKind) -> ParseError {
846
49338
        ParseError {
847
            position: pos,
848
            kind: k,
849
        }
850
49338
    }
851
}
852

            
853
use crate::path_parser::ErrorKind::*;
854

            
855
impl 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]
869
mod tests {
870
    use super::*;
871

            
872
151
    fn find_error_pos(s: &str) -> Option<usize> {
873
151
        s.find('^')
874
151
    }
875

            
876
151
    fn make_parse_result(
877
        error_pos_str: &str,
878
        error_kind: Option<ErrorKind>,
879
    ) -> Result<(), ParseError> {
880
224
        if let Some(pos) = find_error_pos(error_pos_str) {
881
78
            Err(ParseError {
882
                position: pos,
883
78
                kind: error_kind.unwrap(),
884
            })
885
        } else {
886
73
            assert!(error_kind.is_none());
887
73
            Ok(())
888
        }
889
151
    }
890

            
891
150
    fn test_parser(
892
        path_str: &str,
893
        error_pos_str: &str,
894
        expected_commands: &[PathCommand],
895
        expected_error_kind: Option<ErrorKind>,
896
    ) {
897
150
        let expected_result = make_parse_result(error_pos_str, expected_error_kind);
898

            
899
150
        let mut builder = PathBuilder::default();
900
150
        let result = builder.parse(path_str);
901

            
902
147
        let path = builder.into_path();
903
147
        let commands = path.iter().collect::<Vec<_>>();
904

            
905
145
        assert_eq!(expected_commands, commands.as_slice());
906
145
        assert_eq!(expected_result, result);
907
145
    }
908

            
909
138
    fn moveto(x: f64, y: f64) -> PathCommand {
910
138
        PathCommand::MoveTo(x, y)
911
138
    }
912

            
913
45
    fn lineto(x: f64, y: f64) -> PathCommand {
914
45
        PathCommand::LineTo(x, y)
915
45
    }
916

            
917
26
    fn curveto(x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> PathCommand {
918
26
        PathCommand::CurveTo(CubicBezierCurve {
919
            pt1: (x2, y2),
920
            pt2: (x3, y3),
921
            to: (x4, y4),
922
        })
923
26
    }
924

            
925
8
    fn arc(x2: f64, y2: f64, xr: f64, large_arc: bool, sweep: bool,
926
           x3: f64, y3: f64, x4: f64, y4: f64) -> PathCommand {
927
8
        PathCommand::Arc(EllipticalArc {
928
            r: (x2, y2),
929
            x_axis_rotation: xr,
930
            large_arc: LargeArc(large_arc),
931
8
            sweep: match sweep {
932
6
                true => Sweep::Positive,
933
2
                false => Sweep::Negative,
934
            },
935
            from: (x3, y3),
936
            to: (x4, y4),
937
        })
938
8
    }
939

            
940
4
    fn closepath() -> PathCommand {
941
4
        PathCommand::ClosePath
942
4
    }
943

            
944
    #[test]
945
2
    fn handles_empty_data() {
946
1
        test_parser(
947
            "",
948
            "",
949
1
            &Vec::<PathCommand>::new(),
950
1
            None,
951
1
        );
952
2
    }
953

            
954
    #[test]
955
2
    fn handles_numbers() {
956
1
        test_parser(
957
            "M 10 20",
958
            "",
959
1
            &[moveto(10.0, 20.0)],
960
1
            None,
961
        );
962

            
963
1
        test_parser(
964
            "M -10 -20",
965
            "",
966
1
            &[moveto(-10.0, -20.0)],
967
1
            None,
968
        );
969

            
970
1
        test_parser(
971
            "M .10 0.20",
972
            "",
973
1
            &[moveto(0.10, 0.20)],
974
1
            None,
975
        );
976

            
977
1
        test_parser(
978
            "M -.10 -0.20",
979
            "",
980
1
            &[moveto(-0.10, -0.20)],
981
1
            None,
982
        );
983

            
984
1
        test_parser(
985
            "M-.10-0.20",
986
            "",
987
1
            &[moveto(-0.10, -0.20)],
988
1
            None,
989
        );
990

            
991
1
        test_parser(
992
            "M10.5.50",
993
            "",
994
1
            &[moveto(10.5, 0.50)],
995
1
            None,
996
        );
997

            
998
1
        test_parser(
999
            "M.10.20",
            "",
1
            &[moveto(0.10, 0.20)],
1
            None,
        );
1
        test_parser(
            "M .10E1 .20e-4",
            "",
1
            &[moveto(1.0, 0.000020)],
1
            None,
        );
1
        test_parser(
            "M-.10E1-.20",
            "",
1
            &[moveto(-1.0, -0.20)],
1
            None,
        );
1
        test_parser(
            "M10.10E2 -0.20e3",
            "",
1
            &[moveto(1010.0, -200.0)],
1
            None,
        );
1
        test_parser(
            "M-10.10E2-0.20e-3",
            "",
1
            &[moveto(-1010.0, -0.00020)],
1
            None,
        );
1
        test_parser(
            "M1e2.5", // a decimal after exponent start the next number
            "",
1
            &[moveto(100.0, 0.5)],
1
            None,
        );
1
        test_parser(
            "M1e-2.5", // but we are allowed a sign after exponent
            "",
1
            &[moveto(0.01, 0.5)],
1
            None,
        );
1
        test_parser(
            "M1e+2.5", // but we are allowed a sign after exponent
            "",
1
            &[moveto(100.0, 0.5)],
1
            None,
        );
2
    }
    #[test]
2
    fn detects_bogus_numbers() {
1
        test_parser(
            "M+",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::UnexpectedEof)),
        );
1
        test_parser(
            "M-",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::UnexpectedEof)),
        );
1
        test_parser(
            "M+x",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::UnexpectedByte(b'x'))),
        );
1
        test_parser(
            "M10e",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::ParseFloatError)),
        );
1
        test_parser(
            "M10ex",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::ParseFloatError)),
        );
1
        test_parser(
            "M10e-",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::ParseFloatError)),
        );
1
        test_parser(
            "M10e+x",
            " ^",
            &[],
1
            Some(ErrorKind::LexError(LexError::ParseFloatError)),
        );
2
    }
    #[test]
2
    fn handles_numbers_with_comma() {
1
        test_parser(
            "M 10, 20",
            "",
1
            &[moveto(10.0, 20.0)],
1
            None,
        );
1
        test_parser(
            "M -10,-20",
            "",
1
            &[moveto(-10.0, -20.0)],
1
            None,
        );
1
        test_parser(
            "M.10    ,    0.20",
            "",
1
            &[moveto(0.10, 0.20)],
1
            None,
        );
1
        test_parser(
            "M -.10, -0.20   ",
            "",
1
            &[moveto(-0.10, -0.20)],
1
            None,
        );
1
        test_parser(
            "M-.10-0.20",
            "",
1
            &[moveto(-0.10, -0.20)],
1
            None,
        );
1
        test_parser(
            "M.10.20",
            "",
1
            &[moveto(0.10, 0.20)],
1
            None,
        );
1
        test_parser(
            "M .10E1,.20e-4",
            "",
1
            &[moveto(1.0, 0.000020)],
1
            None,
        );
1
        test_parser(
            "M-.10E-2,-.20",
            "",
1
            &[moveto(-0.0010, -0.20)],
1
            None,
        );
1
        test_parser(
            "M10.10E2,-0.20e3",
            "",
1
            &[moveto(1010.0, -200.0)],
1
            None,
        );
1
        test_parser(
            "M-10.10E2,-0.20e-3",
            "",
1
            &[moveto(-1010.0, -0.00020)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_single_moveto() {
1
        test_parser(
            "M 10 20 ",
            "",
1
            &[moveto(10.0, 20.0)],
1
            None,
        );
1
        test_parser(
            "M10,20  ",
            "",
1
            &[moveto(10.0, 20.0)],
1
            None,
        );
1
        test_parser(
            "M10 20   ",
            "",
1
            &[moveto(10.0, 20.0)],
1
            None,
        );
1
        test_parser(
            "    M10,20     ",
            "",
1
            &[moveto(10.0, 20.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto() {
1
        test_parser(
            "m10 20",
            "",
1
            &[moveto(10.0, 20.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_absolute_moveto_with_implicit_lineto() {
1
        test_parser(
            "M10 20 30 40",
            "",
1
            &[moveto(10.0, 20.0), lineto(30.0, 40.0)],
1
            None,
        );
1
        test_parser(
            "M10,20,30,40",
            "",
1
            &[moveto(10.0, 20.0), lineto(30.0, 40.0)],
1
            None,
        );
1
        test_parser(
            "M.1-2,3E2-4",
            "",
1
            &[moveto(0.1, -2.0), lineto(300.0, -4.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_with_implicit_lineto() {
1
        test_parser(
            "m10 20 30 40",
            "",
1
            &[moveto(10.0, 20.0), lineto(40.0, 60.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_with_relative_lineto_sequence() {
1
        test_parser(
            //          1     2    3    4   5
            "m 46,447 l 0,0.5 -1,0 -1,0 0,1 0,12",
            "",
2
            &vec![moveto(46.0, 447.0), lineto(46.0, 447.5), lineto(45.0, 447.5),
1
                  lineto(44.0, 447.5), lineto(44.0, 448.5), lineto(44.0, 460.5)],
1
            None,
1
        );
2
    }
    #[test]
2
    fn handles_absolute_moveto_with_implicit_linetos() {
1
        test_parser(
            "M10,20 30,40,50 60",
            "",
1
            &[moveto(10.0, 20.0), lineto(30.0, 40.0), lineto(50.0, 60.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_with_implicit_linetos() {
1
        test_parser(
            "m10 20 30 40 50 60",
            "",
1
            &[moveto(10.0, 20.0), lineto(40.0, 60.0), lineto(90.0, 120.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_absolute_moveto_moveto() {
1
        test_parser(
            "M10 20 M 30 40",
            "",
1
            &[moveto(10.0, 20.0), moveto(30.0, 40.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_moveto() {
1
        test_parser(
            "m10 20 m 30 40",
            "",
1
            &[moveto(10.0, 20.0), moveto(40.0, 60.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_lineto_moveto() {
1
        test_parser(
            "m10 20 30 40 m 50 60",
            "",
1
            &[moveto(10.0, 20.0), lineto(40.0, 60.0), moveto(90.0, 120.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_absolute_moveto_lineto() {
1
        test_parser(
            "M10 20 L30,40",
            "",
1
            &[moveto(10.0, 20.0), lineto(30.0, 40.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_lineto() {
1
        test_parser(
            "m10 20 l30,40",
            "",
1
            &[moveto(10.0, 20.0), lineto(40.0, 60.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_relative_moveto_lineto_lineto_abs_lineto() {
1
        test_parser(
            "m10 20 30 40l30,40,50 60L200,300",
            "",
2
            &vec![
1
                moveto(10.0, 20.0),
1
                lineto(40.0, 60.0),
1
                lineto(70.0, 100.0),
1
                lineto(120.0, 160.0),
1
                lineto(200.0, 300.0),
            ],
1
            None,
1
        );
2
    }
    #[test]
2
    fn handles_horizontal_lineto() {
1
        test_parser(
            "M10 20 H30",
            "",
1
            &[moveto(10.0, 20.0), lineto(30.0, 20.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 H30 40",
            "",
1
            &[moveto(10.0, 20.0), lineto(30.0, 20.0), lineto(40.0, 20.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 H30,40-50",
            "",
2
            &vec![
1
                moveto(10.0, 20.0),
1
                lineto(30.0, 20.0),
1
                lineto(40.0, 20.0),
1
                lineto(-50.0, 20.0),
            ],
1
            None,
1
        );
1
        test_parser(
            "m10 20 h30,40-50",
            "",
2
            &vec![
1
                moveto(10.0, 20.0),
1
                lineto(40.0, 20.0),
1
                lineto(80.0, 20.0),
1
                lineto(30.0, 20.0),
            ],
1
            None,
1
        );
2
    }
    #[test]
2
    fn handles_vertical_lineto() {
1
        test_parser(
            "M10 20 V30",
            "",
1
            &[moveto(10.0, 20.0), lineto(10.0, 30.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 V30 40",
            "",
1
            &[moveto(10.0, 20.0), lineto(10.0, 30.0), lineto(10.0, 40.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 V30,40-50",
            "",
2
            &vec![
1
                moveto(10.0, 20.0),
1
                lineto(10.0, 30.0),
1
                lineto(10.0, 40.0),
1
                lineto(10.0, -50.0),
            ],
1
            None,
1
        );
1
        test_parser(
            "m10 20 v30,40-50",
            "",
2
            &vec![
1
                moveto(10.0, 20.0),
1
                lineto(10.0, 50.0),
1
                lineto(10.0, 90.0),
1
                lineto(10.0, 40.0),
            ],
1
            None,
1
        );
2
    }
    #[test]
2
    fn handles_curveto() {
1
        test_parser(
            "M10 20 C 30,40 50 60-70,80",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(30.0, 40.0, 50.0, 60.0, -70.0, 80.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 C 30,40 50 60-70,80,90 100,110 120,130,140",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(30.0, 40.0, 50.0, 60.0, -70.0, 80.0),
1
                curveto(90.0, 100.0, 110.0, 120.0, 130.0, 140.0)],
1
            None,
        );
1
        test_parser(
            "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(40.0, 60.0, 60.0, 80.0, -60.0, 100.0),
1
                curveto(30.0, 200.0, 50.0, 220.0, 70.0, 240.0)],
1
            None,
        );
1
        test_parser(
            "m10 20 c 30,40 50 60-70,80,90 100,110 120,130,140",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(40.0, 60.0, 60.0, 80.0, -60.0, 100.0),
1
                curveto(30.0, 200.0, 50.0, 220.0, 70.0, 240.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_smooth_curveto() {
1
        test_parser(
            "M10 20 S 30,40-50,60",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(10.0, 20.0, 30.0, 40.0, -50.0, 60.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 S 30,40 50 60-70,80,90 100",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(10.0, 20.0, 30.0, 40.0, 50.0, 60.0),
1
                curveto(70.0, 80.0, -70.0, 80.0, 90.0, 100.0)],
1
            None,
        );
1
        test_parser(
            "m10 20 s 30,40 50 60-70,80,90 100",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(10.0, 20.0, 40.0, 60.0, 60.0, 80.0),
1
                curveto(80.0, 100.0, -10.0, 160.0, 150.0, 180.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_quadratic_curveto() {
1
        test_parser(
            "M10 20 Q30 40 50 60",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(
                    70.0 / 3.0,
                    100.0 / 3.0,
                    110.0 / 3.0,
                    140.0 / 3.0,
                    50.0,
                    60.0,
                )],
1
            None,
        );
1
        test_parser(
            "M10 20 Q30 40 50 60,70,80-90 100",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(
                    70.0 / 3.0,
                    100.0 / 3.0,
                    110.0 / 3.0,
                    140.0 / 3.0,
                    50.0,
                    60.0,
                ),
1
                curveto(
                    190.0 / 3.0,
                    220.0 / 3.0,
                    50.0 / 3.0,
                    260.0 / 3.0,
                    -90.0,
                    100.0,
                )],
1
            None,
        );
1
        test_parser(
            "m10 20 q 30,40 50 60-70,80 90 100",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(
                    90.0 / 3.0,
                    140.0 / 3.0,
                    140.0 / 3.0,
                    200.0 / 3.0,
                    60.0,
                    80.0,
                ),
1
                curveto(
                    40.0 / 3.0,
                    400.0 / 3.0,
                    130.0 / 3.0,
                    500.0 / 3.0,
                    150.0,
                    180.0,
                )],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_smooth_quadratic_curveto() {
1
        test_parser(
            "M10 20 T30 40",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(10.0, 20.0, 50.0 / 3.0, 80.0 / 3.0, 30.0, 40.0)],
1
            None,
        );
1
        test_parser(
            "M10 20 Q30 40 50 60 T70 80",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(
                    70.0 / 3.0,
                    100.0 / 3.0,
                    110.0 / 3.0,
                    140.0 / 3.0,
                    50.0,
                    60.0,
                ),
1
                curveto(190.0 / 3.0, 220.0 / 3.0, 70.0, 80.0, 70.0, 80.0)],
1
            None,
        );
1
        test_parser(
            "m10 20 q 30,40 50 60t-70,80",
            "",
2
            &[moveto(10.0, 20.0),
1
                curveto(
                    90.0 / 3.0,
                    140.0 / 3.0,
                    140.0 / 3.0,
                    200.0 / 3.0,
                    60.0,
                    80.0,
                ),
1
                curveto(220.0 / 3.0, 280.0 / 3.0, 50.0, 120.0, -10.0, 160.0)],
1
            None,
        );
2
    }
    #[test]
2
    fn handles_elliptical_arc() {
        // no space required between arc flags
1
        test_parser("M 1 2 A 1 2 3 00 6 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, false, false, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
        // or after...
1
        test_parser("M 1 2 A 1 2 3 016 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, false, true, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
        // commas and whitespace are optionally allowed
1
        test_parser("M 1 2 A 1 2 3 10,6 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, true, false, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
1
        test_parser("M 1 2 A 1 2 3 1,16, 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
1
        test_parser("M 1 2 A 1 2 3 1,1 6 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
1
        test_parser("M 1 2 A 1 2 3 1 1 6 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
1
        test_parser("M 1 2 A 1 2 3 1 16 7",
                    "",
2
                    &[moveto(1.0, 2.0),
1
                          arc(1.0, 2.0, 3.0, true, true, 1.0, 2.0, 6.0, 7.0)],
1
                    None);
2
    }
    #[test]
2
    fn handles_close_path() {
1
        test_parser("M10 20 Z", "", &[moveto(10.0, 20.0), closepath()], None);
1
        test_parser(
            "m10 20 30 40 m 50 60 70 80 90 100z",
            "",
2
            &vec![
1
                moveto(10.0, 20.0),
1
                lineto(40.0, 60.0),
1
                moveto(90.0, 120.0),
1
                lineto(160.0, 200.0),
1
                lineto(250.0, 300.0),
1
                closepath(),
            ],
1
            None,
1
        );
2
    }
    #[test]
2
    fn first_command_must_be_moveto() {
1
        test_parser(
            "  L10 20",
            "   ^", // FIXME: why is this not at position 2?
            &[],
1
            Some(ErrorKind::UnexpectedCommand(b'L')),
        );
2
    }
    #[test]
2
    fn moveto_args() {
1
        test_parser(
            "M",
            " ^",
            &[],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M,",
            " ^",
            &[],
1
            Some(ErrorKind::UnexpectedToken(Comma)),
        );
1
        test_parser(
            "M10",
            "   ^",
            &[],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10,",
            "    ^",
            &[],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10x",
            "   ^",
            &[],
1
            Some(ErrorKind::UnexpectedToken(Command(b'x'))),
        );
1
        test_parser(
            "M10,x",
            "    ^",
            &[],
1
            Some(ErrorKind::UnexpectedToken(Command(b'x'))),
        );
2
    }
    #[test]
2
    fn moveto_implicit_lineto_args() {
1
        test_parser(
            "M10-20,",
            "       ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20-30",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20-30 x",
            "          ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedToken(Command(b'x'))),
        );
2
    }
    #[test]
2
    fn closepath_no_args() {
1
        test_parser(
            "M10-20z10",
            "       ^",
1
            &[moveto(10.0, -20.0), closepath()],
1
            Some(ErrorKind::UnexpectedToken(Number(10.0))),
        );
1
        test_parser(
            "M10-20z,",
            "       ^",
1
            &[moveto(10.0, -20.0), closepath()],
1
            Some(ErrorKind::UnexpectedToken(Comma)),
        );
2
    }
    #[test]
2
    fn lineto_args() {
1
        test_parser(
            "M10-20L10",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M 10,10 L 20,20,30",
            "                  ^",
1
            &[moveto(10.0, 10.0), lineto(20.0, 20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M 10,10 L 20,20,",
            "                ^",
1
            &[moveto(10.0, 10.0), lineto(20.0, 20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn horizontal_lineto_args() {
1
        test_parser(
            "M10-20H",
            "       ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20H,",
            "       ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedToken(Comma)),
        );
1
        test_parser(
            "M10-20H30,",
            "          ^",
1
            &[moveto(10.0, -20.0), lineto(30.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn vertical_lineto_args() {
1
        test_parser(
            "M10-20v",
            "       ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20v,",
            "       ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedToken(Comma)),
        );
1
        test_parser(
            "M10-20v30,",
            "          ^",
1
            &[moveto(10.0, -20.0), lineto(10.0, 10.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn curveto_args() {
1
        test_parser(
            "M10-20C1",
            "        ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1 2",
            "          ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,",
            "           ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1 2 3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3,",
            "             ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1 2 3 4",
            "              ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3,4",
            "              ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3,4,",
            "               ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1 2 3 4 5",
            "                ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3,4,5",
            "                ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3,4,5,",
            "                 ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20C1,2,3,4,5,6,",
            "                   ^",
1
            &[moveto(10.0, -20.0), curveto(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn smooth_curveto_args() {
1
        test_parser(
            "M10-20S1",
            "        ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1,",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1 2",
            "          ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1,2,",
            "           ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1 2 3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1,2,3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1,2,3,",
            "             ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20S1,2,3,4,",
            "               ^",
2
            &[moveto(10.0, -20.0),
1
                curveto(10.0, -20.0, 1.0, 2.0, 3.0, 4.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn quadratic_bezier_curveto_args() {
1
        test_parser(
            "M10-20Q1",
            "        ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20Q1,",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20Q1 2",
            "          ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20Q1,2,",
            "           ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20Q1 2 3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20Q1,2,3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20Q1,2,3,",
            "             ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10 20 Q30 40 50 60,",
            "                    ^",
2
            &[moveto(10.0, 20.0),
1
                curveto(
                    70.0 / 3.0,
                    100.0 / 3.0,
                    110.0 / 3.0,
                    140.0 / 3.0,
                    50.0,
                    60.0,
                )],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn smooth_quadratic_bezier_curveto_args() {
1
        test_parser(
            "M10-20T1",
            "        ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20T1,",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10 20 T30 40,",
            "              ^",
2
            &[moveto(10.0, 20.0),
1
                curveto(10.0, 20.0, 50.0 / 3.0, 80.0 / 3.0, 30.0, 40.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
2
    }
    #[test]
2
    fn elliptical_arc_args() {
1
        test_parser(
            "M10-20A1",
            "        ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1,",
            "         ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2",
            "          ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2,",
            "           ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3",
            "            ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3,",
            "             ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3 4",
            "             ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::LexError(LexError::UnexpectedByte(b'4'))),
        );
1
        test_parser(
            "M10-20A1 2 3 1",
            "              ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3,1,",
            "               ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3 1 5",
            "               ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::LexError(LexError::UnexpectedByte(b'5'))),
        );
1
        test_parser(
            "M10-20A1 2 3 1 1",
            "                ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3,1,1,",
            "                 ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3 1 1 6",
            "                  ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
1
        test_parser(
            "M10-20A1 2 3,1,1,6,",
            "                   ^",
1
            &[moveto(10.0, -20.0)],
1
            Some(ErrorKind::UnexpectedEof),
        );
        // no non 0|1 chars allowed for flags
1
        test_parser("M 1 2 A 1 2 3 1.0 0.0 6 7",
                    "               ^",
1
                    &[moveto(1.0, 2.0)],
1
                    Some(ErrorKind::UnexpectedToken(Number(0.0))));
1
        test_parser("M10-20A1 2 3,1,1,6,7,",
                    "                     ^",
2
                    &[moveto(10.0, -20.0),
1
                          arc(1.0, 2.0, 3.0, true, true, 10.0, -20.0, 6.0, 7.0)],
1
                    Some(ErrorKind::UnexpectedEof));
2
    }
    #[test]
2
    fn bugs() {
        // https://gitlab.gnome.org/GNOME/librsvg/issues/345
1
        test_parser(
            "M.. 1,0 0,100000",
            " ^", // FIXME: we have to report position of error in lexer errors to make this right
            &[],
1
            Some(ErrorKind::LexError(LexError::UnexpectedByte(b'.'))),
        );
2
    }
}