mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-11 04:51:51 +00:00
Fix looks_like_svg rejecting DOCTYPE with an internal subset
Found while testing crates/svg/src/import/mod.rs:723-740 (looks_like_svg, the `<!` arm using s.find('>'))
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
43af145a12
commit
213cc75ff2
2 files changed
+51
-1
No files matched your search
@@ -718,6 +718,45 @@ fn options(data: &[u8], limits: budget::Limits) -> usvg::Options<'static> {
|
||||
o
|
||||
}
|
||||
|
||||
/// Length of the `<!...>` declaration at the start of `s`, skipping `>` inside quotes and an
|
||||
/// internal subset (`[ ... ]`).
|
||||
fn doctype_end(s: &str) -> Option<usize> {
|
||||
let mut quote = None;
|
||||
let mut depth = 0u32;
|
||||
let mut skip_to = 0;
|
||||
for (i, c) in s.char_indices() {
|
||||
if i < skip_to {
|
||||
continue;
|
||||
}
|
||||
if quote.is_none() && depth > 0 {
|
||||
// Comments and processing instructions in the subset may hold quotes and brackets.
|
||||
let rest = &s[i..];
|
||||
let close = if rest.starts_with("<!--") {
|
||||
Some("-->")
|
||||
} else if rest.starts_with("<?") {
|
||||
Some("?>")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(close) = close {
|
||||
let len = rest.find(close)? + close.len();
|
||||
skip_to = i + len;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
match (quote, c) {
|
||||
(Some(q), _) if c == q => quote = None,
|
||||
(Some(_), _) => {}
|
||||
(None, '"' | '\'') => quote = Some(c),
|
||||
(None, '[') => depth += 1,
|
||||
(None, ']') => depth = depth.saturating_sub(1),
|
||||
(None, '>') if depth == 0 => return Some(i + 1),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Whether `text` (from the clipboard, say) is an SVG document: `<svg` after an optional byte
|
||||
/// order mark, XML declaration, comments, doctype and whitespace.
|
||||
pub fn looks_like_svg(text: &str) -> bool {
|
||||
@@ -731,7 +770,7 @@ pub fn looks_like_svg(text: &str) -> bool {
|
||||
} else if s.starts_with("<!--") {
|
||||
s.find("-->").map(|i| i + 3)
|
||||
} else if s.starts_with("<!") {
|
||||
s.find('>').map(|i| i + 1)
|
||||
doctype_end(s)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -107,3 +107,14 @@ fn layer_names_decode_id_escapes() {
|
||||
assert_eq!(layer_name("", "Group"), "Group");
|
||||
assert_eq!(layer_name("é_x41_", "Group"), "éA");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctype_with_internal_subset_is_still_svg() {
|
||||
let s = "<?xml version=\"1.0\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" [\n\t<!ENTITY ns_svg \"http://www.w3.org/2000/svg\">\n]>\n<svg xmlns=\"&ns_svg;\" width=\"10\" height=\"10\"/>";
|
||||
assert!(super::looks_like_svg(s));
|
||||
let quoted = "<!DOCTYPE svg [ <!ENTITY a \"x>y]\"> ]><svg/>";
|
||||
assert!(super::looks_like_svg(quoted));
|
||||
let commented = "<!DOCTYPE svg [ <!-- don't ] > --> <!ENTITY a \"b\"> ]><svg/>";
|
||||
assert!(super::looks_like_svg(commented));
|
||||
assert!(!super::looks_like_svg("<!DOCTYPE html [ <svg/> ]><html/>"));
|
||||
}
|
||||
Reference in new issue
Block a user