extern crate rctree;
use std::cell::Ref;
use std::path;
use svgdom;
pub use self::nodes::*;
pub use self::attributes::*;
use {
Error,
Options,
};
mod attributes;
mod convert;
mod nodes;
pub mod prelude {
pub use tree::FuzzyEq;
pub use tree::FuzzyZero;
pub use super::NodeExt;
}
pub type Node = rctree::Node<NodeKind>;
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct Tree {
root: Node,
}
impl Tree {
pub fn from_data(data: &[u8], opt: &Options) -> Result<Self, Error> {
if data.starts_with(&[0x1f, 0x8b]) {
let text = deflate(data, data.len())?;
Self::from_str(&text, opt)
} else {
let text = ::std::str::from_utf8(data).map_err(|_| Error::NotAnUtf8Str)?;
Self::from_str(text, opt)
}
}
pub fn from_str(text: &str, opt: &Options) -> Result<Self, Error> {
let dom_opt = svgdom::ParseOptions {
skip_invalid_attributes: true,
skip_invalid_css: true,
skip_unresolved_classes: true,
};
let doc = svgdom::Document::from_str_with_opt(text, &dom_opt)
.map_err(|e| Error::ParsingFailed(e))?;
Ok(Self::from_dom(doc, &opt))
}
pub fn from_dom(mut doc: svgdom::Document, opt: &Options) -> Self {
super::preproc::prepare_doc(&mut doc, opt);
super::convert::convert_doc(&doc, opt)
}
pub fn from_file<P: AsRef<path::Path>>(
path: P,
opt: &Options,
) -> Result<Self, Error> {
let text = load_svg_file(path.as_ref())?;
Self::from_str(&text, opt)
}
pub fn create(svg: Svg) -> Self {
let mut root_node = Node::new(NodeKind::Svg(svg));
let defs_node = Node::new(NodeKind::Defs);
root_node.append(defs_node);
Tree {
root: root_node,
}
}
pub fn root(&self) -> Node {
self.root.clone()
}
pub fn svg_node(&self) -> Ref<Svg> {
Ref::map(self.root.borrow(), |v| {
match *v {
NodeKind::Svg(ref svg) => svg,
_ => unreachable!(),
}
})
}
pub fn defs(&self) -> Node {
self.root.first_child().unwrap()
}
pub fn is_in_defs(&self, node: &Node) -> bool {
let defs = self.defs();
node.ancestors().any(|n| n == defs)
}
pub fn append_to_defs(&mut self, kind: NodeKind) -> Node {
let new_node = Node::new(kind);
self.defs().append(new_node.clone());
new_node
}
pub fn defs_by_id(&self, id: &str) -> Option<Node> {
for n in self.defs().children() {
if &*n.id() == id {
return Some(n);
}
}
None
}
pub fn node_by_id(&self, id: &str) -> Option<Node> {
if id.is_empty() {
return None;
}
for node in self.root().descendants() {
if !self.is_in_defs(&node) {
if &*node.id() == id {
return Some(node);
}
}
}
None
}
pub fn to_svgdom(&self) -> svgdom::Document {
convert::conv_doc(self)
}
}
pub trait NodeExt {
fn id(&self) -> Ref<str>;
fn transform(&self) -> Transform;
fn append_kind(&mut self, kind: NodeKind) -> Node;
fn tree(&self) -> Tree;
}
impl NodeExt for Node {
fn id(&self) -> Ref<str> {
Ref::map(self.borrow(), |v| v.id())
}
fn transform(&self) -> Transform {
self.borrow().transform()
}
fn append_kind(&mut self, kind: NodeKind) -> Node {
let new_node = Node::new(kind);
self.append(new_node.clone());
new_node
}
fn tree(&self) -> Tree {
Tree { root: self.root() }
}
}
pub fn load_svg_file(path: &path::Path) -> Result<String, Error> {
use std::fs;
use std::io::Read;
use std::path::Path;
let mut file = fs::File::open(path).map_err(|_| Error::FileOpenFailed)?;
let length = file.metadata().map_err(|_| Error::FileOpenFailed)?.len() as usize + 1;
let ext = if let Some(ext) = Path::new(path).extension() {
ext.to_str().map(|s| s.to_lowercase()).unwrap_or_default()
} else {
String::new()
};
match ext.as_str() {
"svgz" => {
deflate(&file, length)
}
"svg" => {
let mut s = String::with_capacity(length);
file.read_to_string(&mut s).map_err(|_| Error::NotAnUtf8Str)?;
Ok(s)
}
_ => {
Err(Error::InvalidFileSuffix)
}
}
}
fn deflate<R: ::std::io::Read>(inner: R, len: usize) -> Result<String, Error> {
use std::io::Read;
let mut decoder = libflate::gzip::Decoder::new(inner).map_err(|_| Error::MalformedGZip)?;
let mut decoded = String::with_capacity(len * 2);
decoder.read_to_string(&mut decoded).map_err(|_| Error::NotAnUtf8Str)?;
Ok(decoded)
}