use crate::structures::common::StructureError;
use aho_corasick::AhoCorasick;
const SVG_OPEN_TAG: &[u8] = b"<svg ";
const SVG_CLOSE_TAG: &[u8] = b"</svg>";
const SVG_HEAD_MAGIC: &str = "xmlns=\"http://www.w3.org/2000/svg\"";
#[derive(Debug, Default, Clone)]
pub struct SVGImage {
pub total_size: usize,
}
pub fn parse_svg_image(svg_data: &[u8]) -> Result<SVGImage, StructureError> {
let mut head_tag_count: usize = 0;
let mut unclosed_svg_tags: usize = 0;
let svg_tags = vec![SVG_OPEN_TAG, SVG_CLOSE_TAG];
let grep = AhoCorasick::new(svg_tags).unwrap();
for tag_match in grep.find_overlapping_iter(svg_data) {
let tag_start: usize = tag_match.start();
match parse_svg_tag(&svg_data[tag_start..]) {
Err(_) => {
break;
}
Ok(svg_tag) => {
if svg_tag.is_head {
head_tag_count += 1;
}
if svg_tag.is_open {
unclosed_svg_tags += 1;
}
if svg_tag.is_close {
unclosed_svg_tags -= 1;
}
if head_tag_count > 1 {
break;
}
if head_tag_count == 1 && unclosed_svg_tags == 0 {
return Ok(SVGImage {
total_size: tag_start + SVG_CLOSE_TAG.len(),
});
}
}
}
}
Err(StructureError)
}
#[derive(Debug, Default, Clone)]
struct SVGTag {
pub is_head: bool,
pub is_open: bool,
pub is_close: bool,
}
fn parse_svg_tag(tag_data: &[u8]) -> Result<SVGTag, StructureError> {
const END_TAG: u8 = 0x3E;
let mut result = SVGTag {
..Default::default()
};
let svg_open_tag = String::from_utf8(SVG_OPEN_TAG.to_vec()).unwrap();
let svg_close_tag = String::from_utf8(SVG_CLOSE_TAG.to_vec()).unwrap();
let svg_head_string = SVG_HEAD_MAGIC.to_string();
for i in 0..tag_data.len() {
if tag_data[i] == END_TAG {
if let Some(tag_bytes) = tag_data.get(0..i + 1) {
if let Ok(tag_string) = String::from_utf8(tag_bytes.to_vec()) {
result.is_open = tag_string.starts_with(&svg_open_tag);
result.is_close = tag_string.starts_with(&svg_close_tag);
result.is_head = tag_string.contains(&svg_head_string);
return Ok(result);
}
}
}
}
Err(StructureError)
}