mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 02:46:51 -06:00
Read SVG arc flags as single characters in path data (#1726)
Found while testing crates/io/src/svg_read.rs:892-894 (arc flags read via Numbers::next_num, 675-709) Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
4251f1e253
commit
8dd8635e10
@@ -756,17 +756,33 @@ enum Tok {
|
||||
struct Tokenizer<'a> {
|
||||
s: &'a [u8],
|
||||
i: usize,
|
||||
/// Start of the most recently returned token.
|
||||
tok_start: usize,
|
||||
}
|
||||
|
||||
impl<'a> Tokenizer<'a> {
|
||||
fn new(s: &'a str) -> Self {
|
||||
Tokenizer { s: s.as_bytes(), i: 0 }
|
||||
Tokenizer { s: s.as_bytes(), i: 0, tok_start: 0 }
|
||||
}
|
||||
|
||||
/// Read an arc flag: a single `0` or `1` character, which needs no separator after it.
|
||||
fn next_flag(&mut self) -> Option<f64> {
|
||||
let mut nums = Numbers { s: self.s, i: self.i };
|
||||
nums.skip_seps();
|
||||
let v = match self.s.get(nums.i)? {
|
||||
b'0' => 0.0,
|
||||
b'1' => 1.0,
|
||||
_ => return None,
|
||||
};
|
||||
self.i = nums.i + 1;
|
||||
Some(v)
|
||||
}
|
||||
|
||||
fn next_tok(&mut self) -> Option<Tok> {
|
||||
let mut nums = Numbers { s: self.s, i: self.i };
|
||||
nums.skip_seps();
|
||||
self.i = nums.i;
|
||||
self.tok_start = self.i;
|
||||
let c = self.s.get(self.i).copied()?;
|
||||
if c.is_ascii_alphabetic() {
|
||||
self.i += 1;
|
||||
@@ -843,7 +859,18 @@ fn parse_path(d: &str) -> Result<Vec<Subpath>> {
|
||||
_ => return Err(IoError::Format(format!("SVG: unknown path command `{}`", cmd as char))),
|
||||
};
|
||||
let mut args = [0.0f64; 7];
|
||||
for slot in args.iter_mut().take(count) {
|
||||
for (idx, slot) in args.iter_mut().enumerate().take(count) {
|
||||
if abs == b'A' && (idx == 3 || idx == 4) {
|
||||
// Flags are single characters; re-scan a pending number token from its start.
|
||||
if matches!(lookahead, Some(Tok::Num(_))) {
|
||||
lookahead = None;
|
||||
tk.i = tk.tok_start;
|
||||
}
|
||||
if lookahead.is_none() {
|
||||
*slot = tk.next_flag().ok_or_else(|| IoError::Format("SVG: path command is missing coordinates".into()))?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*slot = next_num(&mut tk, &mut lookahead)?;
|
||||
}
|
||||
let point = |x: f64, y: f64| if rel { cur + Vec2::new(x, y) } else { Vec2::new(x, y) };
|
||||
@@ -1030,3 +1057,25 @@ fn elliptical_params(p0: Vec2, p1: Vec2, rx: f64, ry: f64, phi: f64, large: bool
|
||||
let ccw = angle(ux, uy, vx, vy).rem_euclid(std::f64::consts::TAU);
|
||||
Some((center, theta, pick_sweep(ccw, large, sweep)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn arc_of(d: &str) -> (bool, bool, Vec2) {
|
||||
let subs = parse_path(d).unwrap_or_else(|e| panic!("{d}: {e:?}"));
|
||||
match &subs[0].segs[0] {
|
||||
Seg::Arc { large, sweep, end, .. } => (*large, *sweep, *end),
|
||||
_ => panic!("not an arc"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_arc_flags_read_as_single_characters() {
|
||||
let spaced = arc_of("M10 10 a20 20 0 0 1 20 20");
|
||||
assert_eq!(arc_of("M10 10 a20 20 0 01 20 20"), spaced);
|
||||
assert_eq!(arc_of("M10 10 a20 20 0 0120 20"), spaced);
|
||||
assert_eq!(arc_of("M10 10 a20 20 0 1 0 20 20"), (true, false, spaced.2));
|
||||
assert!(arc_of("M10 10 a20 20 0 10-20 20").0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user