use anyhow::{Context, Result};
use quick_xml::events::{BytesStart, Event};
use quick_xml::reader::Reader;
use quick_xml::writer::Writer;
pub fn strip_text(src: &str) -> Result<(String, bool)> {
let mut reader = Reader::from_str(src);
let mut writer = Writer::new(Vec::new());
let mut removed = false;
loop {
match reader.read_event().context("parsing SVG")? {
Event::Eof => break,
Event::Start(e) if is_text(&e) => {
reader
.read_to_end(e.name())
.context("unterminated <text> element")?;
removed = true;
}
Event::Empty(e) if is_text(&e) => removed = true,
other => writer.write_event(other)?,
}
}
Ok((String::from_utf8(writer.into_inner())?, removed))
}
fn is_text(e: &BytesStart) -> bool {
e.local_name().as_ref() == "text"
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strips_text_and_keeps_the_artwork() {
let src = r#"<svg xmlns="http://www.w3.org/2000/svg"><path d="M0 0h8v8z"/><text x="1" y="2">Hi</text><circle r="4"/></svg>"#;
let (out, removed) = strip_text(src).unwrap();
assert!(removed);
assert!(!out.contains("<text"));
assert!(!out.contains("Hi"));
assert!(out.contains(r#"<path d="M0 0h8v8z"/>"#));
assert!(out.contains(r#"<circle r="4"/>"#));
}
#[test]
fn strips_nested_text_children() {
let src =
r##"<svg><text><tspan>a</tspan><textPath href="#p">b</textPath></text><rect/></svg>"##;
let (out, removed) = strip_text(src).unwrap();
assert!(removed);
assert_eq!(out, "<svg><rect/></svg>");
}
#[test]
fn strips_empty_and_prefixed_text() {
let (out, removed) = strip_text(r#"<svg><text x="1"/><rect/></svg>"#).unwrap();
assert!(removed);
assert_eq!(out, "<svg><rect/></svg>");
let (out, removed) =
strip_text(r"<svg:svg><svg:text>Hi</svg:text><svg:rect/></svg:svg>").unwrap();
assert!(removed);
assert_eq!(out, "<svg:svg><svg:rect/></svg:svg>");
}
#[test]
fn path_artwork_is_left_alone() {
let src = r##"<svg xmlns="http://www.w3.org/2000/svg" font-size="12"><path d="M0 0h8v8z" fill="#000"/></svg>"##;
let (out, removed) = strip_text(src).unwrap();
assert!(!removed);
assert_eq!(out, src);
}
#[test]
fn unterminated_text_is_an_error() {
assert!(strip_text("<svg><text>unclosed").is_err());
}
}