rasterize/
svg.rs

1//! SVG path parser
2//!
3//! See [SVG Path Specification](https://www.w3.org/TR/SVG11/paths.html#PathData)
4use crate::{PathBuilder, Point, Scalar, Transform};
5use std::{
6    fmt,
7    io::{Cursor, Read},
8    str::FromStr,
9};
10
11/// Possible SVG path commands
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum SvgPathCmd {
14    MoveTo(Point),
15    LineTo(Point),
16    QuadTo(Point, Point),
17    CubicTo(Point, Point, Point),
18    ArcTo {
19        radii: Point,
20        x_axis_rot: Scalar,
21        large: bool,
22        sweep: bool,
23        dst: Point,
24    },
25    Close(Point),
26}
27
28impl SvgPathCmd {
29    /// Get destination point of the SVG command
30    pub fn dst(&self) -> Point {
31        use SvgPathCmd::*;
32        *match self {
33            MoveTo(dst) => dst,
34            LineTo(dst) => dst,
35            QuadTo(_, dst) => dst,
36            CubicTo(_, _, dst) => dst,
37            ArcTo { dst, .. } => dst,
38            Close(dst) => dst,
39        }
40    }
41
42    /// Apply SVG command to path builder
43    pub fn apply(&self, builder: &mut PathBuilder) {
44        use SvgPathCmd::*;
45        match self {
46            MoveTo(p) => builder.move_to(p),
47            LineTo(p) => builder.line_to(p),
48            QuadTo(p1, p2) => builder.quad_to(p1, p2),
49            CubicTo(p1, p2, p3) => builder.cubic_to(p1, p2, p3),
50            Close(_) => builder.close(),
51            ArcTo {
52                radii,
53                x_axis_rot,
54                large,
55                sweep,
56                dst,
57            } => builder.arc_to(radii, *x_axis_rot, *large, *sweep, dst),
58        };
59    }
60}
61
62struct Parser<I> {
63    input: I,
64    input_buffer: Option<u8>,
65    offset: usize,
66}
67
68impl<I: Read> Parser<I> {
69    pub fn new(input: I) -> Self {
70        Self {
71            input,
72            input_buffer: None,
73            offset: 0,
74        }
75    }
76
77    // return current offset
78    pub fn offset(&self) -> usize {
79        self.offset
80    }
81
82    // consume single byte from the input
83    pub fn parse_byte(&mut self) -> Result<Option<u8>, SvgParserError> {
84        match self.input_buffer.take() {
85            None => {
86                let mut byte = [0; 1];
87                if self.input.read(&mut byte)? != 0 {
88                    self.offset += 1;
89                    Ok(Some(byte[0]))
90                } else {
91                    Ok(None)
92                }
93            }
94            byte => {
95                self.offset += 1;
96                Ok(byte)
97            }
98        }
99    }
100
101    // put byte into input buffer, at most one byte is cached
102    pub fn unparse_byte(&mut self, byte: u8) {
103        debug_assert!(self.input_buffer.is_none());
104        self.offset = self.offset.saturating_sub(1);
105        self.input_buffer = Some(byte);
106    }
107
108    // consume input while `pred` predicate is true, call `proc` on consumed bytes
109    pub fn parse_while(
110        &mut self,
111        mut pred: impl FnMut(u8) -> bool,
112        mut proc: impl FnMut(u8),
113    ) -> Result<usize, SvgParserError> {
114        let mut count = 0;
115        loop {
116            let byte = match self.parse_byte()? {
117                None => break,
118                Some(byte) => byte,
119            };
120            if !pred(byte) {
121                self.unparse_byte(byte);
122                break;
123            }
124            count += 1;
125            proc(byte);
126        }
127        Ok(count)
128    }
129
130    // consume at most one byte from the input, if predicate returns true
131    pub fn parse_once(
132        &mut self,
133        pred: impl FnOnce(u8) -> bool,
134        proc: impl FnOnce(u8),
135    ) -> Result<bool, SvgParserError> {
136        let byte = match self.parse_byte()? {
137            None => return Ok(false),
138            Some(byte) => byte,
139        };
140        if pred(byte) {
141            proc(byte);
142            Ok(true)
143        } else {
144            self.unparse_byte(byte);
145            Ok(false)
146        }
147    }
148
149    // consume separators from the input
150    pub fn parse_separators(&mut self) -> Result<(), SvgParserError> {
151        loop {
152            let byte = match self.parse_byte()? {
153                None => break,
154                Some(byte) => byte,
155            };
156            if !matches!(byte, b' ' | b'\t' | b'\r' | b'\n' | b',') {
157                self.unparse_byte(byte);
158                break;
159            }
160        }
161        Ok(())
162    }
163
164    // parse single scalar value from the input
165    pub fn parse_scalar(&mut self) -> Result<Scalar, SvgParserError> {
166        self.parse_separators()?;
167
168        let mut mantissa: i64 = 0;
169        let mut exponent: i64 = 0;
170        let mut sign = 1;
171
172        fn push_digit(value: &mut i64, byte: u8) {
173            let digit = byte - b'0';
174            *value = value.wrapping_mul(10).wrapping_add(digit as i64);
175        }
176
177        self.parse_once(
178            |byte| matches!(byte, b'-' | b'+'),
179            |byte| {
180                if byte == b'-' {
181                    sign = -1
182                }
183            },
184        )?;
185        let whole = self.parse_while(
186            |byte| byte.is_ascii_digit(),
187            |byte| push_digit(&mut mantissa, byte),
188        )?;
189        let matches_dot = self.parse_once(|byte| matches!(byte, b'.'), |_| {})?;
190        let frac = if matches_dot {
191            self.parse_while(
192                |byte| byte.is_ascii_digit(),
193                |byte| {
194                    push_digit(&mut mantissa, byte);
195                    exponent -= 1;
196                },
197            )?
198        } else {
199            0
200        };
201        mantissa *= sign;
202
203        if whole + frac == 0 {
204            return Err(SvgParserError::InvalidScalar {
205                offset: self.offset(),
206            });
207        }
208
209        let matches_exp = self.parse_once(|byte| matches!(byte, b'e' | b'E'), |_| {})?;
210        if matches_exp {
211            let mut sci: i64 = 0;
212            let mut sci_sign = 1;
213            self.parse_once(
214                |byte| matches!(byte, b'-' | b'+'),
215                |byte| {
216                    if byte == b'-' {
217                        sci_sign = -1
218                    }
219                },
220            )?;
221            if self.parse_while(
222                |byte| byte.is_ascii_digit(),
223                |byte| push_digit(&mut sci, byte),
224            )? == 0
225            {
226                return Err(SvgParserError::InvalidScalar {
227                    offset: self.offset(),
228                });
229            }
230            exponent = exponent.wrapping_add(sci_sign * sci)
231        }
232
233        let ten: Scalar = 10.0;
234        Ok((mantissa as Scalar) * ten.powi(exponent as i32))
235    }
236}
237
238/// Path parser for SVG encoded path
239///
240/// See [SVG Path Specification](https://www.w3.org/TR/SVG11/paths.html#PathData)
241pub struct SvgPathParser<I> {
242    parser: Parser<I>,
243    // previous operation
244    prev_op: Option<u8>,
245    // previous command (used to determine smooth points)
246    prev_cmd: Option<SvgPathCmd>,
247    // current position from which next relative curve will start
248    position: Point,
249    // current sub-path starting position
250    subpath_start: Point,
251}
252
253impl<I: Read> SvgPathParser<I> {
254    pub fn new(input: I) -> Self {
255        Self {
256            parser: Parser::new(input),
257            prev_op: None,
258            prev_cmd: None,
259            position: Point::new(0.0, 0.0),
260            subpath_start: Point::new(0.0, 0.0),
261        }
262    }
263
264    // parse pair of scalars and convert it to a point
265    fn parse_point(&mut self) -> Result<Point, SvgParserError> {
266        let point = Point::new(self.parser.parse_scalar()?, self.parser.parse_scalar()?);
267        match self.prev_op {
268            Some(cmd) if cmd.is_ascii_lowercase() => Ok(point + self.position),
269            _ => Ok(point),
270        }
271    }
272
273    // parse flag `0|1` used by elliptic arc command
274    fn parse_flag(&mut self) -> Result<bool, SvgParserError> {
275        self.parser.parse_separators()?;
276        match self.parser.parse_byte()? {
277            Some(b'0') => Ok(false),
278            Some(b'1') => Ok(true),
279            byte => {
280                if let Some(byte) = byte {
281                    self.parser.unparse_byte(byte);
282                }
283                Err(SvgParserError::InvalidFlag {
284                    offset: self.parser.offset(),
285                    flag: byte.map(char::from),
286                })
287            }
288        }
289    }
290
291    // parse svg command, none indicates end of input
292    fn parse_op(&mut self) -> Result<Option<u8>, SvgParserError> {
293        let op = match self.parser.parse_byte()? {
294            None => return Ok(None),
295            Some(op) => op,
296        };
297        match op {
298            b'M' | b'm' | b'L' | b'l' | b'V' | b'v' | b'H' | b'h' | b'C' | b'c' | b'S' | b's'
299            | b'Q' | b'q' | b'T' | b't' | b'A' | b'a' | b'Z' | b'z' => {
300                self.prev_op = if op == b'm' {
301                    Some(b'l')
302                } else if op == b'M' {
303                    Some(b'L')
304                } else if op == b'Z' || op == b'z' {
305                    None
306                } else {
307                    Some(op)
308                };
309                Ok(Some(op))
310            }
311            byte => {
312                self.parser.unparse_byte(byte);
313                match self.prev_op {
314                    Some(op) => Ok(Some(op)),
315                    None => Err(SvgParserError::InvalidCmd {
316                        offset: self.parser.offset(),
317                        cmd: op.into(),
318                    }),
319                }
320            }
321        }
322    }
323
324    /// Parse single SVG path command from the input
325    pub fn parse_cmd(&mut self) -> Result<Option<SvgPathCmd>, SvgParserError> {
326        self.parser.parse_separators()?;
327        let op = match self.parse_op()? {
328            None => return Ok(None),
329            Some(op) => op,
330        };
331        let cmd = match op {
332            // move
333            b'M' | b'm' => {
334                let dst = self.parse_point()?;
335                self.subpath_start = dst;
336                SvgPathCmd::MoveTo(dst)
337            }
338            // line
339            b'L' | b'l' => SvgPathCmd::LineTo(self.parse_point()?),
340            // vertical line
341            b'V' | b'v' => {
342                let y = self.parser.parse_scalar()?;
343                let p0 = self.position;
344                let p1 = if op == b'v' {
345                    Point::new(p0.x(), p0.y() + y)
346                } else {
347                    Point::new(p0.x(), y)
348                };
349                SvgPathCmd::LineTo(p1)
350            }
351            // horizontal line
352            b'H' | b'h' => {
353                let x = self.parser.parse_scalar()?;
354                let p0 = self.position;
355                let p1 = if op == b'h' {
356                    Point::new(p0.x() + x, p0.y())
357                } else {
358                    Point::new(x, p0.y())
359                };
360                SvgPathCmd::LineTo(p1)
361            }
362            // quadratic bezier curve
363            b'Q' | b'q' => SvgPathCmd::QuadTo(self.parse_point()?, self.parse_point()?),
364            // smooth quadratic bezier curve
365            b'T' | b't' => {
366                let p1 = match self.prev_cmd {
367                    Some(SvgPathCmd::QuadTo(p1, p2)) => 2.0 * p2 - p1,
368                    _ => self.position,
369                };
370                let p2 = self.parse_point()?;
371                SvgPathCmd::QuadTo(p1, p2)
372            }
373            // cubic bezier curve
374            b'C' | b'c' => SvgPathCmd::CubicTo(
375                self.parse_point()?,
376                self.parse_point()?,
377                self.parse_point()?,
378            ),
379            // smooth cubic bezier curve
380            b'S' | b's' => {
381                let p1 = match self.prev_cmd {
382                    Some(SvgPathCmd::CubicTo(_, p2, p3)) => 2.0 * p3 - p2,
383                    _ => self.position,
384                };
385                let p2 = self.parse_point()?;
386                let p3 = self.parse_point()?;
387                SvgPathCmd::CubicTo(p1, p2, p3)
388            }
389            // elliptical arc
390            b'A' | b'a' => {
391                let rx = self.parser.parse_scalar()?;
392                let ry = self.parser.parse_scalar()?;
393                let x_axis_rot = self.parser.parse_scalar()?;
394                let large_flag = self.parse_flag()?;
395                let sweep_flag = self.parse_flag()?;
396                let dst = self.parse_point()?;
397                SvgPathCmd::ArcTo {
398                    radii: Point::new(rx, ry),
399                    x_axis_rot,
400                    large: large_flag,
401                    sweep: sweep_flag,
402                    dst,
403                }
404            }
405            // close path
406            b'Z' | b'z' => SvgPathCmd::Close(self.subpath_start),
407            _ => unreachable!(),
408        };
409        self.position = cmd.dst();
410        self.prev_cmd = Some(cmd);
411        Ok(self.prev_cmd)
412    }
413}
414
415impl<I: Read> Iterator for SvgPathParser<I> {
416    type Item = Result<SvgPathCmd, SvgParserError>;
417
418    fn next(&mut self) -> Option<Self::Item> {
419        self.parse_cmd().transpose()
420    }
421}
422
423// maximum length of matrix | translate(X|Y)? | scale(X|Y)? | rotate | skew(X|Y) | deg | rad
424const TRANSFORM_BUF: usize = 10;
425
426struct SvgTransformParser<I> {
427    parser: Parser<I>,
428    buf: [u8; TRANSFORM_BUF],
429    buf_len: usize,
430}
431
432impl<I: Read> SvgTransformParser<I> {
433    fn new(input: I) -> Self {
434        Self {
435            parser: Parser::new(input),
436            buf: [0; TRANSFORM_BUF],
437            buf_len: 0,
438        }
439    }
440
441    fn parse_ident(&mut self) -> Result<&[u8], SvgParserError> {
442        self.buf_len = 0;
443        self.parser.parse_while(
444            |b| b.is_ascii_alphabetic(),
445            |b| {
446                self.buf[self.buf_len] = b;
447                self.buf_len = (self.buf_len + 1) % TRANSFORM_BUF;
448            },
449        )?;
450
451        Ok(&self.buf[..self.buf_len])
452    }
453
454    // parse angle in radians
455    fn parse_angle(&mut self) -> Result<Scalar, SvgParserError> {
456        let value = self.parser.parse_scalar()?;
457        let offset = self.parser.offset();
458        let units = self.parse_ident()?;
459        match units {
460            b"" | b"deg" => Ok(value * crate::PI / 180.0),
461            b"rad" => Ok(value),
462            _ => Err(SvgParserError::InvalidUnits {
463                offset,
464                units: String::from_utf8_lossy(units).to_string(),
465            }),
466        }
467    }
468
469    fn parse_length(&mut self) -> Result<Scalar, SvgParserError> {
470        let value = self.parser.parse_scalar()?;
471        let _units = self.parse_ident()?;
472        // TODO: units px|cm|in? ...
473        Ok(value)
474    }
475
476    fn parse_transform(&mut self) -> Result<Option<Transform>, SvgParserError> {
477        enum Op {
478            Matrix,
479            Rotate,
480            Translate,
481            TranslateX,
482            TranslateY,
483            Scale,
484            ScaleX,
485            ScaleY,
486            SkewX,
487            SkewY,
488        }
489
490        self.parser.parse_separators()?;
491        match self.parser.parse_byte()? {
492            None => return Ok(None),
493            Some(b) => self.parser.unparse_byte(b),
494        }
495
496        let offset = self.parser.offset();
497        let op = match self.parse_ident()? {
498            b"matrix" => Op::Matrix,
499            b"rotate" => Op::Rotate,
500            b"translate" => Op::Translate,
501            b"translateX" => Op::TranslateX,
502            b"translateY" => Op::TranslateY,
503            b"scale" => Op::Scale,
504            b"scaleX" => Op::ScaleX,
505            b"scaleY" => Op::ScaleY,
506            b"skewX" => Op::SkewX,
507            b"skewY" => Op::SkewY,
508            op => {
509                return Err(SvgParserError::InvalidTransformOp {
510                    offset,
511                    op: String::from_utf8_lossy(op).to_string(),
512                });
513            }
514        };
515
516        self.parser.parse_separators()?;
517        if !matches!(self.parser.parse_byte()?, Some(b'(')) {
518            return Err(SvgParserError::BracketExpected {
519                offset: self.parser.offset(),
520            });
521        }
522
523        let tr = match op {
524            Op::Matrix => {
525                let m00 = self.parser.parse_scalar()?;
526                let m10 = self.parser.parse_scalar()?;
527                let m01 = self.parser.parse_scalar()?;
528                let m11 = self.parser.parse_scalar()?;
529                let m02 = self.parser.parse_scalar()?;
530                let m12 = self.parser.parse_scalar()?;
531                Transform::new(m00, m01, m02, m10, m11, m12)
532            }
533            Op::Rotate => {
534                let mut tr = Transform::new_rotate(self.parse_angle()?);
535                if let Ok(tx) = self.parse_length() {
536                    let ty = self.parse_length()?;
537                    // rotate around (tx, ty)
538                    tr = Transform::new_translate(tx, ty)
539                        .pre_concat(tr)
540                        .pre_translate(-tx, -ty);
541                }
542                tr
543            }
544            Op::Translate => {
545                let tx = self.parse_length()?;
546                let ty = self.parse_length().unwrap_or(0.0);
547                Transform::new_translate(tx, ty)
548            }
549            Op::TranslateX => {
550                let tx = self.parse_length()?;
551                Transform::new_translate(tx, 0.0)
552            }
553            Op::TranslateY => {
554                let ty = self.parse_length()?;
555                Transform::new_translate(0.0, ty)
556            }
557            Op::Scale => {
558                let sx = self.parser.parse_scalar()?;
559                let sy = self.parser.parse_scalar().unwrap_or(sx);
560                Transform::new_scale(sx, sy)
561            }
562            Op::ScaleX => {
563                let sx = self.parser.parse_scalar()?;
564                Transform::new_scale(sx, 1.0)
565            }
566            Op::ScaleY => {
567                let sy = self.parser.parse_scalar()?;
568                Transform::new_scale(1.0, sy)
569            }
570            Op::SkewX => {
571                let ax = self.parse_angle()?;
572                Transform::new_skew(ax, 0.0)
573            }
574            Op::SkewY => {
575                let ay = self.parse_angle()?;
576                Transform::new_skew(0.0, ay)
577            }
578        };
579
580        self.parser.parse_separators()?;
581        if !matches!(self.parser.parse_byte()?, Some(b')')) {
582            return Err(SvgParserError::BracketExpected {
583                offset: self.parser.offset(),
584            });
585        }
586
587        Ok(Some(tr))
588    }
589}
590
591impl FromStr for Transform {
592    type Err = SvgParserError;
593
594    fn from_str(text: &str) -> Result<Self, Self::Err> {
595        let mut tr = Transform::identity();
596        let mut parser = SvgTransformParser::new(Cursor::new(text));
597        while let Some(tr_next) = parser.parse_transform()? {
598            tr = tr * tr_next;
599        }
600        Ok(tr)
601    }
602}
603
604/// Error while parsing path in the SVG format
605#[derive(Debug)]
606pub enum SvgParserError {
607    /// Failed to parse SVG command
608    InvalidCmd { offset: usize, cmd: char },
609    /// Failed to parse scalar value
610    InvalidScalar { offset: usize },
611    /// Failed to parse flag value
612    InvalidFlag { offset: usize, flag: Option<char> },
613    /// Unexpected segment type found while parsing curve segment
614    UnexpectedSegmentType,
615    /// Invalid transform operation
616    InvalidTransformOp { offset: usize, op: String },
617    /// Invalid bounding box
618    InvalidBBox,
619    /// Invalid (Angle|Gradient|Length)
620    InvalidUnits { offset: usize, units: String },
621    /// Invalid Fill Rule
622    InvalidFillRule,
623    /// Bracket expected,
624    BracketExpected { offset: usize },
625    /// JSON error
626    #[cfg(feature = "serde")]
627    Json(serde_json::Error),
628    /// IO error propagated while reading input stream
629    IoError(std::io::Error),
630}
631
632impl fmt::Display for SvgParserError {
633    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634        write!(f, "SvgPathParser::{:?}", self)
635    }
636}
637
638impl From<std::io::Error> for SvgParserError {
639    fn from(error: std::io::Error) -> Self {
640        Self::IoError(error)
641    }
642}
643
644#[cfg(feature = "serde")]
645impl From<serde_json::Error> for SvgParserError {
646    fn from(error: serde_json::Error) -> Self {
647        Self::Json(error)
648    }
649}
650
651impl From<SvgParserError> for std::io::Error {
652    fn from(error: SvgParserError) -> Self {
653        match error {
654            SvgParserError::IoError(error) => error,
655            _ => Self::new(std::io::ErrorKind::InvalidData, error),
656        }
657    }
658}
659
660impl std::error::Error for SvgParserError {}
661
662#[cfg(test)]
663mod tests {
664    use super::*;
665    use crate::assert_approx_eq;
666
667    #[test]
668    fn test_parse_scalar() -> Result<(), SvgParserError> {
669        let mut parser = Parser::new(Cursor::new("1 .22e0.32 3.21e-3-1.24 1e4"));
670        assert_approx_eq!(parser.parse_scalar()?, 1.0);
671        assert_approx_eq!(parser.parse_scalar()?, 0.22);
672        assert_approx_eq!(parser.parse_scalar()?, 0.32);
673        assert_approx_eq!(parser.parse_scalar()?, 3.21e-3);
674        assert_approx_eq!(parser.parse_scalar()?, -1.24);
675        assert_approx_eq!(parser.parse_scalar()?, 1e4);
676        Ok(())
677    }
678
679    #[test]
680    fn test_parse_transform() -> Result<(), SvgParserError> {
681        let tr_str = r#"
682            translate(1 2)
683            skewX(30deg)
684            matrix(1  2 3 4 -3-7)
685            scale(2,1)
686            rotate(10 1 2)
687            rotate(1rad)
688        "#;
689        let tr_fmt = "matrix(6.56129 5.23393 -1.92617 -2.14614 -5.23999 -4.1231)";
690        let tr = Transform::from_str(tr_str)?;
691        assert_eq!(format!("{tr:#?}"), tr_fmt);
692        let tr = Transform::from_str(tr_fmt)?;
693        assert_eq!(format!("{tr:#?}"), tr_fmt);
694        Ok(())
695    }
696}