mirror of
https://github.com/lexmount/moli.git
synced 2026-10-07 08:01:00 +00:00
fix(svg): reject trailing decimal path numbers
This commit is contained in:
@@ -92,6 +92,27 @@ mod tests {
|
||||
assert_close(incomplete_curve[0].length(), 20.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_geometry_rejects_numbers_with_trailing_decimal_points() {
|
||||
let separate_command = path_segments("M 0 0 L 10 10 L 20.,30");
|
||||
assert_eq!(separate_command.len(), 1);
|
||||
let end = point_at_length(&separate_command, f64::INFINITY);
|
||||
assert_close(end.x, 10.0);
|
||||
assert_close(end.y, 10.0);
|
||||
|
||||
let compound_command = path_segments("M 0 0 L 10 10 20.,30");
|
||||
assert_eq!(compound_command.len(), 1);
|
||||
let end = point_at_length(&compound_command, f64::INFINITY);
|
||||
assert_close(end.x, 10.0);
|
||||
assert_close(end.y, 10.0);
|
||||
|
||||
let first_coordinate = path_segments("M 0 0 L 15. 20");
|
||||
assert_close(first_coordinate[0].length(), 0.0);
|
||||
let end = point_at_length(&first_coordinate, f64::INFINITY);
|
||||
assert_close(end.x, 0.0);
|
||||
assert_close(end.y, 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_geometry_handles_relative_commands_and_close_path() {
|
||||
let segments = path_segments("m 1 1 l 3 0 v 4 h -3 z");
|
||||
|
||||
+54
-1
@@ -3,7 +3,7 @@ use svgtypes::{SimplePathSegment, SimplifyingPathParser};
|
||||
|
||||
pub(crate) fn path_geometry(raw: &str) -> Option<BezPath> {
|
||||
let mut path = BezPath::new();
|
||||
for segment in SimplifyingPathParser::from(raw) {
|
||||
for segment in SimplifyingPathParser::from(path_data_before_invalid_number(raw)) {
|
||||
let Ok(segment) = segment else {
|
||||
break;
|
||||
};
|
||||
@@ -26,3 +26,56 @@ pub(crate) fn path_geometry(raw: &str) -> Option<BezPath> {
|
||||
}
|
||||
Some(path)
|
||||
}
|
||||
|
||||
fn path_data_before_invalid_number(raw: &str) -> &str {
|
||||
let bytes = raw.as_bytes();
|
||||
let mut position = 0;
|
||||
while let Some(&byte) = bytes.get(position) {
|
||||
match byte {
|
||||
b'\t' | b'\n' | b'\x0c' | b'\r' | b' ' | b',' | b'A' | b'C' | b'H' | b'L' | b'M'
|
||||
| b'Q' | b'S' | b'T' | b'V' | b'Z' | b'a' | b'c' | b'h' | b'l' | b'm' | b'q' | b's'
|
||||
| b't' | b'v' | b'z' => position += 1,
|
||||
b'+' | b'-' | b'.' | b'0'..=b'9' => {
|
||||
let number_start = position;
|
||||
if matches!(bytes[position], b'+' | b'-') {
|
||||
position += 1;
|
||||
}
|
||||
|
||||
let integer_start = position;
|
||||
while bytes.get(position).is_some_and(u8::is_ascii_digit) {
|
||||
position += 1;
|
||||
}
|
||||
let has_integer = position != integer_start;
|
||||
|
||||
if bytes.get(position) == Some(&b'.') {
|
||||
position += 1;
|
||||
let fraction_start = position;
|
||||
while bytes.get(position).is_some_and(u8::is_ascii_digit) {
|
||||
position += 1;
|
||||
}
|
||||
if position == fraction_start {
|
||||
return &raw[..number_start];
|
||||
}
|
||||
} else if !has_integer {
|
||||
return &raw[..number_start];
|
||||
}
|
||||
|
||||
if matches!(bytes.get(position), Some(b'e' | b'E')) {
|
||||
position += 1;
|
||||
if matches!(bytes.get(position), Some(b'+' | b'-')) {
|
||||
position += 1;
|
||||
}
|
||||
let exponent_start = position;
|
||||
while bytes.get(position).is_some_and(u8::is_ascii_digit) {
|
||||
position += 1;
|
||||
}
|
||||
if position == exponent_start {
|
||||
return &raw[..number_start];
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return raw,
|
||||
}
|
||||
}
|
||||
raw
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user