1use crate::{PathBuilder, Point, Scalar, Transform};
5use std::{
6 fmt,
7 io::{Cursor, Read},
8 str::FromStr,
9};
10
11#[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 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 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 pub fn offset(&self) -> usize {
79 self.offset
80 }
81
82 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 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 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 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 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 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
238pub struct SvgPathParser<I> {
242 parser: Parser<I>,
243 prev_op: Option<u8>,
245 prev_cmd: Option<SvgPathCmd>,
247 position: Point,
249 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 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 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 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 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 b'M' | b'm' => {
334 let dst = self.parse_point()?;
335 self.subpath_start = dst;
336 SvgPathCmd::MoveTo(dst)
337 }
338 b'L' | b'l' => SvgPathCmd::LineTo(self.parse_point()?),
340 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 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 b'Q' | b'q' => SvgPathCmd::QuadTo(self.parse_point()?, self.parse_point()?),
364 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 b'C' | b'c' => SvgPathCmd::CubicTo(
375 self.parse_point()?,
376 self.parse_point()?,
377 self.parse_point()?,
378 ),
379 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 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 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
423const 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 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 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 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#[derive(Debug)]
606pub enum SvgParserError {
607 InvalidCmd { offset: usize, cmd: char },
609 InvalidScalar { offset: usize },
611 InvalidFlag { offset: usize, flag: Option<char> },
613 UnexpectedSegmentType,
615 InvalidTransformOp { offset: usize, op: String },
617 InvalidBBox,
619 InvalidUnits { offset: usize, units: String },
621 InvalidFillRule,
623 BracketExpected { offset: usize },
625 #[cfg(feature = "serde")]
627 Json(serde_json::Error),
628 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}