svg/
lib.rs

1//! An SVG composer and parser.
2//!
3//! ## Example: Composing
4//!
5//! ```
6//! # extern crate svg;
7//! use svg::Document;
8//! use svg::node::element::Path;
9//! use svg::node::element::path::Data;
10//!
11//! # fn main() {
12//! let data = Data::new()
13//!     .move_to((10, 10))
14//!     .line_by((0, 50))
15//!     .line_by((50, 0))
16//!     .line_by((0, -50))
17//!     .close();
18//!
19//! let path = Path::new()
20//!     .set("fill", "none")
21//!     .set("stroke", "black")
22//!     .set("stroke-width", 3)
23//!     .set("d", data);
24//!
25//! let document = Document::new()
26//!     .set("viewBox", (0, 0, 70, 70))
27//!     .add(path);
28//!
29//! svg::save("image.svg", &document).unwrap();
30//! # std::fs::remove_file("image.svg");
31//! # }
32//! ```
33//!
34//! ## Example: Parsing
35//!
36//! ```
37//! # extern crate svg;
38//! use svg::node::element::path::{Command, Data};
39//! use svg::node::element::tag::Path;
40//! use svg::parser::Event;
41//!
42//! # fn main() {
43//! let path = "image.svg";
44//! # let path = "tests/fixtures/benton.svg";
45//! let mut content = String::new();
46//! for event in svg::open(path, &mut content).unwrap() {
47//!     match event {
48//!         Event::Tag(Path, _, attributes) => {
49//!             let data = attributes.get("d").unwrap();
50//!             let data = Data::parse(data).unwrap();
51//!             for command in data.iter() {
52//!                 match command {
53//!                     &Command::Move(..) => { /* … */ },
54//!                     &Command::Line(..) => { /* … */ },
55//!                     _ => {}
56//!                 }
57//!             }
58//!         }
59//!         _ => {}
60//!     }
61//! }
62//! # }
63//! ```
64
65use std::fs::File;
66use std::io::{Read, Result, Write};
67use std::path::Path;
68
69pub mod node;
70pub mod parser;
71
72pub use crate::node::Node;
73pub use crate::parser::Parser;
74
75/// A document.
76pub type Document = node::element::SVG;
77
78/// Open a document.
79pub fn open<T>(path: T, content: &'_ mut String) -> Result<Parser<'_>>
80where
81    T: AsRef<Path>,
82{
83    let mut file = File::open(path)?;
84    file.read_to_string(content)?;
85    read(content)
86}
87
88/// Read a document.
89pub fn read(content: &'_ str) -> Result<Parser<'_>> {
90    Ok(Parser::new(content))
91}
92
93/// Save a document.
94pub fn save<T, U>(path: T, document: &U) -> Result<()>
95where
96    T: AsRef<Path>,
97    U: Node,
98{
99    let mut file = File::create(path)?;
100    file.write_all(&document.to_string().into_bytes())
101}
102
103/// Write a document.
104pub fn write<T, U>(mut target: T, document: &U) -> Result<()>
105where
106    T: Write,
107    U: Node,
108{
109    target.write_all(&document.to_string().into_bytes())
110}
111
112#[cfg(test)]
113mod tests {
114    use std::fs::File;
115    use std::io::Read;
116
117    use crate::parser::{Event, Parser};
118
119    const TEST_PATH: &'static str = "tests/fixtures/benton.svg";
120
121    #[test]
122    fn open() {
123        let mut content = String::new();
124        exercise(crate::open(self::TEST_PATH, &mut content).unwrap());
125    }
126
127    #[test]
128    fn read() {
129        let mut content = String::new();
130        let mut file = File::open(self::TEST_PATH).unwrap();
131        file.read_to_string(&mut content).unwrap();
132
133        exercise(crate::read(&content).unwrap());
134    }
135
136    fn exercise<'l>(mut parser: Parser<'l>) {
137        macro_rules! test(
138            ($matcher:pat) => (match parser.next().unwrap() {
139                $matcher => {}
140                _ => unreachable!(),
141            });
142        );
143
144        test!(Event::Instruction(_));
145        test!(Event::Comment(_));
146        test!(Event::Declaration(_));
147        test!(Event::Tag("svg", _, _));
148        test!(Event::Tag("path", _, _));
149        test!(Event::Tag("path", _, _));
150        test!(Event::Tag("path", _, _));
151        test!(Event::Tag("path", _, _));
152        test!(Event::Tag("svg", _, _));
153
154        assert!(parser.next().is_none());
155    }
156}