1use super::command::Command;
4use super::geometry::Vector;
5use super::path_builder::{Arc, ArcSize, ArcSweep};
6
7#[derive(Copy, Clone)]
8enum State {
9 Initial,
10 Next,
11 Continue(u8),
12}
13
14#[derive(Clone)]
15pub struct SvgCommands<'a> {
16 buf: &'a [u8],
17 cur: u8,
18 pub pos: usize,
19 cmd_pos: usize,
20 pub error: bool,
21 pub done: bool,
22 start_point: Vector,
23 cur_point: Vector,
24 last_control: Vector,
25 last_cmd: u8,
26 state: State,
27 arc: Arc,
28}
29
30impl Iterator for SvgCommands<'_> {
31 type Item = Command;
32
33 fn next(&mut self) -> Option<Self::Item> {
34 self.parse()
35 }
36}
37
38impl<'a> SvgCommands<'a> {
39 pub(crate) fn new(source: &'a str) -> Self {
40 Self {
41 buf: source.as_bytes(),
42 cur: 0,
43 pos: 0,
44 cmd_pos: 0,
45 error: false,
46 done: false,
47 start_point: Vector::ZERO,
48 cur_point: Vector::ZERO,
49 last_control: Vector::ZERO,
50 last_cmd: 0,
51 state: State::Initial,
52 arc: Arc::default(),
53 }
54 }
55
56 fn parse(&mut self) -> Option<Command> {
57 use Command::*;
58 let mut cmd = self.cur;
59 loop {
60 if let Some(cmd) = self.arc.next() {
61 return Some(cmd);
62 }
63 self.last_cmd = cmd;
64 match self.state {
65 State::Initial => {
66 self.advance();
67 self.skip_whitespace();
68 self.state = State::Next;
69 continue;
70 }
71 State::Next => {
72 self.skip_whitespace();
73 self.cmd_pos = self.pos;
74 cmd = self.cur;
75 self.advance();
76 self.skip_whitespace();
77 self.state = State::Continue(cmd);
78 match cmd {
79 b'z' | b'Z' => {
80 self.state = State::Next;
81 self.cur_point = self.start_point;
82 return Some(Close);
83 }
84 b'M' => {
85 let to = self.point_to()?;
86 self.start_point = to;
87 self.skip_comma_whitespace();
88 return Some(MoveTo(to));
89 }
90 b'm' => {
91 let to = self.rel_point_to()?;
92 self.start_point = to;
93 self.skip_comma_whitespace();
94 return Some(MoveTo(to));
95 }
96 b'L' => {
97 let to = self.point_to()?;
98 self.skip_comma_whitespace();
99 return Some(LineTo(to));
100 }
101 b'l' => {
102 let to = self.rel_point_to()?;
103 self.skip_comma_whitespace();
104 return Some(LineTo(to));
105 }
106 b'H' => {
107 let x = self.coord()?;
108 let to = Vector::new(x, self.cur_point.y);
109 self.cur_point = to;
110 self.skip_comma_whitespace();
111 return Some(LineTo(to));
112 }
113 b'h' => {
114 let x = self.coord()?;
115 let to = Vector::new(self.cur_point.x + x, self.cur_point.y);
116 self.cur_point = to;
117 self.skip_comma_whitespace();
118 return Some(LineTo(to));
119 }
120 b'V' => {
121 let y = self.coord()?;
122 let to = Vector::new(self.cur_point.x, y);
123 self.cur_point = to;
124 self.skip_comma_whitespace();
125 return Some(LineTo(to));
126 }
127 b'v' => {
128 let y = self.coord()?;
129 let to = Vector::new(self.cur_point.x, self.cur_point.y + y);
130 self.cur_point = to;
131 self.skip_comma_whitespace();
132 return Some(LineTo(to));
133 }
134 b'C' => {
135 let (c1, c2, to) = self.three_points_to()?;
136 self.last_control = c2;
137 self.skip_comma_whitespace();
138 return Some(CurveTo(c1, c2, to));
139 }
140 b'c' => {
141 let (c1, c2, to) = self.rel_three_points_to()?;
142 self.last_control = c2;
143 self.skip_comma_whitespace();
144 return Some(CurveTo(c1, c2, to));
145 }
146 b'S' => {
147 let (c2, to) = self.two_points()?;
148 let c1 = self.reflected_control(cmd);
149 self.cur_point = to;
150 self.last_control = c2;
151 self.skip_comma_whitespace();
152 return Some(CurveTo(c1, c2, to));
153 }
154 b's' => {
155 let (c2, to) = self.rel_two_points()?;
156 let c1 = self.reflected_control(cmd);
157 self.cur_point = to;
158 self.last_control = c2;
159 self.skip_comma_whitespace();
160 return Some(CurveTo(c1, c2, to));
161 }
162 b'Q' => {
163 let (c, to) = self.two_points_to()?;
164 self.last_control = c;
165 self.skip_comma_whitespace();
166 return Some(QuadTo(c, to));
167 }
168 b'q' => {
169 let (c, to) = self.rel_two_points_to()?;
170 self.last_control = c;
171 self.skip_comma_whitespace();
172 return Some(QuadTo(c, to));
173 }
174 b'T' => {
175 let to = self.point()?;
176 let c = self.reflected_control(cmd);
177 self.cur_point = to;
178 self.last_control = c;
179 self.skip_comma_whitespace();
180 return Some(QuadTo(c, to));
181 }
182 b't' => {
183 let to = self.rel_point()?;
184 let c = self.reflected_control(cmd);
185 self.cur_point = to;
186 self.last_control = c;
187 self.skip_comma_whitespace();
188 return Some(QuadTo(c, to));
189 }
190 b'A' => {
191 let from = self.cur_point;
192 let (rx, ry, a, size, sweep, to) = self.arc_arguments(false)?;
193 self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
194 self.skip_comma_whitespace();
195 continue;
196 }
197 b'a' => {
198 let from = self.cur_point;
199 let (rx, ry, a, size, sweep, to) = self.arc_arguments(true)?;
200 self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
201 self.skip_comma_whitespace();
202 continue;
203 }
204 _ => {
205 if !self.done || cmd != 0 {
206 self.error = true;
207 self.pos = self.cmd_pos;
208 }
209 return None;
210 }
211 }
212 }
213 State::Continue(cmd) => match cmd {
214 b'M' => {
215 if let Some(to) = self.point_to() {
216 self.skip_comma_whitespace();
217 return Some(LineTo(to));
218 } else {
219 self.state = State::Next;
220 }
221 }
222 b'm' => {
223 if let Some(to) = self.rel_point_to() {
224 self.skip_comma_whitespace();
225 return Some(LineTo(to));
226 } else {
227 self.state = State::Next;
228 }
229 }
230 b'L' => {
231 if let Some(to) = self.point_to() {
232 self.skip_comma_whitespace();
233 return Some(LineTo(to));
234 } else {
235 self.state = State::Next;
236 }
237 }
238 b'l' => {
239 if let Some(to) = self.rel_point_to() {
240 self.skip_comma_whitespace();
241 return Some(LineTo(to));
242 } else {
243 self.state = State::Next;
244 }
245 }
246 b'H' => {
247 if let Some(x) = self.coord() {
248 let to = Vector::new(x, self.cur_point.y);
249 self.cur_point = to;
250 self.skip_comma_whitespace();
251 return Some(LineTo(to));
252 } else {
253 self.state = State::Next;
254 }
255 }
256 b'h' => {
257 if let Some(x) = self.coord() {
258 let to = Vector::new(self.cur_point.x + x, self.cur_point.y);
259 self.cur_point = to;
260 self.skip_comma_whitespace();
261 return Some(LineTo(to));
262 } else {
263 self.state = State::Next;
264 }
265 }
266 b'V' => {
267 if let Some(y) = self.coord() {
268 let to = Vector::new(self.cur_point.x, y);
269 self.cur_point = to;
270 self.skip_comma_whitespace();
271 return Some(LineTo(to));
272 } else {
273 self.state = State::Next;
274 }
275 }
276 b'v' => {
277 if let Some(y) = self.coord() {
278 let to = Vector::new(self.cur_point.x, self.cur_point.y + y);
279 self.cur_point = to;
280 self.skip_comma_whitespace();
281 return Some(LineTo(to));
282 } else {
283 self.state = State::Next;
284 }
285 }
286 b'C' => {
287 if let Some(c1) = self.point() {
288 self.skip_comma_whitespace();
289 let (c2, to) = self.two_points_to()?;
290 self.last_control = c2;
291 self.skip_comma_whitespace();
292 return Some(CurveTo(c1, c2, to));
293 } else {
294 self.state = State::Next;
295 }
296 }
297 b'c' => {
298 if let Some(c1) = self.rel_point() {
299 self.skip_comma_whitespace();
300 let (c2, to) = self.rel_two_points_to()?;
301 self.last_control = c2;
302 self.skip_comma_whitespace();
303 return Some(CurveTo(c1, c2, to));
304 } else {
305 self.state = State::Next;
306 }
307 }
308 b'S' => {
309 if let Some(c2) = self.point() {
310 self.skip_comma_whitespace();
311 let to = self.point()?;
312 let c1 = self.reflected_control(cmd);
313 self.cur_point = to;
314 self.last_control = c2;
315 self.skip_comma_whitespace();
316 return Some(CurveTo(c1, c2, to));
317 } else {
318 self.state = State::Next;
319 }
320 }
321 b's' => {
322 if let Some(c2) = self.rel_point() {
323 self.skip_comma_whitespace();
324 let to = self.rel_point()?;
325 let c1 = self.reflected_control(cmd);
326 self.cur_point = to;
327 self.last_control = c2;
328 self.skip_comma_whitespace();
329 return Some(CurveTo(c1, c2, to));
330 } else {
331 self.state = State::Next;
332 }
333 }
334 b'Q' => {
335 if let Some(c) = self.point() {
336 self.last_control = c;
337 self.skip_comma_whitespace();
338 let to = self.point_to()?;
339 self.skip_comma_whitespace();
340 return Some(QuadTo(c, to));
341 } else {
342 self.state = State::Next;
343 }
344 }
345 b'q' => {
346 if let Some(c) = self.rel_point() {
347 self.last_control = c;
348 self.skip_comma_whitespace();
349 let to = self.rel_point_to()?;
350 self.skip_comma_whitespace();
351 return Some(QuadTo(c, to));
352 } else {
353 self.state = State::Next;
354 }
355 }
356 b'T' => {
357 if let Some(to) = self.point() {
358 let c = self.reflected_control(cmd);
359 self.cur_point = to;
360 self.last_control = c;
361 self.skip_comma_whitespace();
362 return Some(QuadTo(c, to));
363 } else {
364 self.state = State::Next;
365 }
366 }
367 b't' => {
368 if let Some(to) = self.rel_point() {
369 let c = self.reflected_control(cmd);
370 self.cur_point = to;
371 self.last_control = c;
372 self.skip_comma_whitespace();
373 return Some(QuadTo(c, to));
374 } else {
375 self.state = State::Next;
376 }
377 }
378 b'A' => {
379 if let Some(rx) = self.coord() {
380 let from = self.cur_point;
381 let (ry, a, size, sweep, to) = self.arc_rest_arguments(false)?;
382 self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
383 self.skip_comma_whitespace();
384 } else {
385 self.state = State::Next;
386 }
387 }
388 b'a' => {
389 if let Some(rx) = self.coord() {
390 let from = self.cur_point;
391 let (ry, a, size, sweep, to) = self.arc_rest_arguments(true)?;
392 self.arc = Arc::new(from, rx, ry, a.to_radians(), size, sweep, to);
393 self.skip_comma_whitespace();
394 } else {
395 self.state = State::Next;
396 }
397 }
398 _ => {
399 if !self.done || cmd != 0 {
400 self.error = true;
401 self.pos = self.cmd_pos;
402 }
403 return None;
404 }
405 },
406 }
407 }
408 }
409
410 fn reflected_control(&self, cmd: u8) -> Vector {
411 let cur = self.cur_point;
412 let old = self.last_control;
413 if cmd == b'S' || cmd == b's' {
414 match self.last_cmd {
415 b'C' | b'c' | b'S' | b's' => (2. * cur.x - old.x, 2. * cur.y - old.y).into(),
416 _ => self.cur_point,
417 }
418 } else {
419 match self.last_cmd {
420 b'Q' | b'q' | b'T' | b't' => (2. * cur.x - old.x, 2. * cur.y - old.y).into(),
421 _ => self.cur_point,
422 }
423 }
424 }
425
426 fn arc_arguments(&mut self, rel: bool) -> Option<(f32, f32, f32, ArcSize, ArcSweep, Vector)> {
427 let rx = self.coord()?;
428 self.skip_comma_whitespace();
429 let ry = self.coord()?;
430 self.skip_comma_whitespace();
431 let a = self.coord()?;
432 self.skip_comma_whitespace();
433 let large_arc = self.boolean()?;
434 self.skip_comma_whitespace();
435 let sweep = self.boolean()?;
436 self.skip_comma_whitespace();
437 let to = if rel {
438 self.rel_point_to()?
439 } else {
440 self.point_to()?
441 };
442 let size = if large_arc {
443 ArcSize::Large
444 } else {
445 ArcSize::Small
446 };
447 let sweep = if sweep {
448 ArcSweep::Positive
449 } else {
450 ArcSweep::Negative
451 };
452 Some((rx, ry, a, size, sweep, to))
453 }
454
455 fn arc_rest_arguments(&mut self, rel: bool) -> Option<(f32, f32, ArcSize, ArcSweep, Vector)> {
456 let ry = self.coord()?;
457 self.skip_comma_whitespace();
458 let a = self.coord()?;
459 self.skip_comma_whitespace();
460 let large_arc = self.boolean()?;
461 self.skip_comma_whitespace();
462 let sweep = self.boolean()?;
463 self.skip_comma_whitespace();
464 let to = if rel {
465 self.rel_point_to()?
466 } else {
467 self.point_to()?
468 };
469 let size = if large_arc {
470 ArcSize::Large
471 } else {
472 ArcSize::Small
473 };
474 let sweep = if sweep {
475 ArcSweep::Positive
476 } else {
477 ArcSweep::Negative
478 };
479 Some((ry, a, size, sweep, to))
480 }
481
482 fn point(&mut self) -> Option<Vector> {
483 let a = self.coord()?;
484 self.skip_comma_whitespace();
485 let b = self.coord()?;
486 Some((a, b).into())
487 }
488
489 fn point_to(&mut self) -> Option<Vector> {
490 let p = self.point()?;
491 self.cur_point = p;
492 Some(p)
493 }
494
495 fn rel_point(&mut self) -> Option<Vector> {
496 let p = self.point()?;
497 Some(p + self.cur_point)
498 }
499
500 fn rel_point_to(&mut self) -> Option<Vector> {
501 let p = self.rel_point()?;
502 self.cur_point = p;
503 Some(p)
504 }
505
506 fn two_points_to(&mut self) -> Option<(Vector, Vector)> {
507 let a = self.point()?;
508 self.skip_comma_whitespace();
509 let b = self.point_to()?;
510 Some((a, b))
511 }
512
513 fn two_points(&mut self) -> Option<(Vector, Vector)> {
514 let a = self.point()?;
515 self.skip_comma_whitespace();
516 let b = self.point()?;
517 Some((a, b))
518 }
519
520 fn rel_two_points_to(&mut self) -> Option<(Vector, Vector)> {
521 let a = self.rel_point()?;
522 self.skip_comma_whitespace();
523 let b = self.rel_point_to()?;
524 Some((a, b))
525 }
526
527 fn rel_two_points(&mut self) -> Option<(Vector, Vector)> {
528 let a = self.rel_point()?;
529 self.skip_comma_whitespace();
530 let b = self.rel_point()?;
531 Some((a, b))
532 }
533
534 fn three_points_to(&mut self) -> Option<(Vector, Vector, Vector)> {
535 let a = self.point()?;
536 self.skip_comma_whitespace();
537 let b = self.point()?;
538 self.skip_comma_whitespace();
539 let c = self.point_to()?;
540 Some((a, b, c))
541 }
542
543 fn rel_three_points_to(&mut self) -> Option<(Vector, Vector, Vector)> {
544 let a = self.rel_point()?;
545 self.skip_comma_whitespace();
546 let b = self.rel_point()?;
547 self.skip_comma_whitespace();
548 let c = self.rel_point_to()?;
549 Some((a, b, c))
550 }
551
552 fn coord(&mut self) -> Option<f32> {
553 match self.cur {
554 b'+' => {
555 self.advance();
556 self.number()
557 }
558 b'-' => {
559 self.advance();
560 Some(-self.number()?)
561 }
562 _ => self.number(),
563 }
564 }
565
566 fn number(&mut self) -> Option<f32> {
567 let mut buf = [0u8; 32];
568 let mut pos = 0;
569 let mut has_decimal = false;
570 loop {
571 match self.cur {
572 b'.' => {
573 if has_decimal {
574 break;
575 } else {
576 *buf.get_mut(pos)? = self.cur;
577 pos += 1;
578 has_decimal = true;
579 }
580 }
581 b'0'..=b'9' => {
582 *buf.get_mut(pos)? = self.cur;
583 pos += 1;
584 }
585 _ => break,
586 }
587 self.advance();
588 }
589 let s = core::str::from_utf8(&buf[..pos]).ok()?;
590 s.parse::<f32>().ok()
591 }
592
593 fn boolean(&mut self) -> Option<bool> {
594 match self.cur {
595 b'0' => {
596 self.advance();
597 Some(false)
598 }
599 b'1' => {
600 self.advance();
601 Some(true)
602 }
603 _ => None,
604 }
605 }
606
607 fn skip_comma_whitespace(&mut self) {
608 self.skip_whitespace();
609 if self.accept(b',') {
610 self.skip_whitespace();
611 }
612 }
613
614 #[allow(clippy::match_like_matches_macro)]
615 fn skip_whitespace(&mut self) {
616 while self.accept_by(|b| match b {
617 0x9 | 0x20 | 0xA | 0xC | 0xD => true,
618 _ => false,
619 }) {}
620 }
621
622 fn accept(&mut self, b: u8) -> bool {
623 if self.cur == b {
624 self.advance();
625 return true;
626 }
627 false
628 }
629
630 fn accept_by(&mut self, f: impl Fn(u8) -> bool) -> bool {
631 if f(self.cur) {
632 self.advance();
633 return true;
634 }
635 false
636 }
637
638 fn advance(&mut self) {
639 if self.pos == self.buf.len() {
640 self.done = true;
641 self.cur = 0;
642 return;
643 }
644 self.cur = self.buf[self.pos];
645 self.pos += 1;
646 }
647}
648
649pub fn validate_svg(svg: &str) -> Result<(), usize> {
651 let cmds = &mut SvgCommands::new(svg);
652 cmds.count();
653 let pos = cmds.pos;
654 if cmds.error || pos != svg.len() {
655 Err(pos.saturating_sub(1))
656 } else {
657 Ok(())
658 }
659}