svgdom 0.7.0

Library to represent an SVG as a DOM.
Documentation
diff --git a/src/attribute/attribute_value.rs b/src/attribute/attribute_value.rs
index 22f080e..bee5b76 100644
--- a/src/attribute/attribute_value.rs
+++ b/src/attribute/attribute_value.rs
@@ -13,12 +13,12 @@ use {
     WriteToString,
 };
 use types::{
+    path,
     Color,
     Length,
     LengthList,
     LengthUnit,
     NumberList,
-    path,
     Transform,
 };
 
diff --git a/src/lib.rs b/src/lib.rs
index a33a92e..45858e8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -50,7 +50,6 @@ DOM structure itself based on: https://github.com/SimonSapin/rust-forest/tree/ma
 */
 
 #![warn(missing_docs)]
-#![deny(unused_import_braces)]
 
 #[macro_use]
 extern crate svgparser;
diff --git a/src/types/path/writer.rs b/src/types/path/writer.rs
index 4571395..57dc053 100644
--- a/src/types/path/writer.rs
+++ b/src/types/path/writer.rs
@@ -42,13 +42,19 @@ impl WriteBuffer for Path {
         if !opt.paths.use_compact_notation {
             let len = buf.len();
             buf.truncate(len - 1);
+        } else if opt.paths.new_lines {
+            let len = buf.len();
+            buf.truncate(len - 1);
         }
     }
 }
 
-fn write_cmd(seg: &Segment, prev_cmd: &mut Option<PrevCmd>,
-             opt: &WriteOptions, buf: &mut Vec<u8>) -> bool {
-
+fn write_cmd(
+    seg: &Segment,
+    prev_cmd: &mut Option<PrevCmd>,
+    opt: &WriteOptions,
+    buf: &mut Vec<u8>
+) -> bool {
     let mut print_cmd = true;
     if opt.paths.remove_duplicated_commands {
         // check that previous command is the same as current
@@ -139,9 +145,13 @@ pub fn write_cmd_char(seg: &Segment, buf: &mut Vec<u8>) {
     buf.push(cmd);
 }
 
-pub fn write_segment(data: &SegmentData, is_written: bool, prev_coord_has_dot: &mut bool,
-                     opt: &WriteOptions, buf: &mut Vec<u8>)
-{
+pub fn write_segment(
+    data: &SegmentData,
+    is_written: bool,
+    prev_coord_has_dot: &mut bool,
+    opt: &WriteOptions,
+    buf: &mut Vec<u8>
+) {
     match *data {
           SegmentData::MoveTo { x, y }
         | SegmentData::LineTo { x, y }
@@ -196,16 +206,24 @@ pub fn write_segment(data: &SegmentData, is_written: bool, prev_coord_has_dot: &
             write_coords(&[x, y], true, prev_coord_has_dot, opt, buf);
         }
         SegmentData::ClosePath => {
-            if !opt.paths.use_compact_notation {
-                buf.push(b' ');
+            if opt.paths.new_lines {
+                buf.push(b'\n');
+            } else {
+                if !opt.paths.use_compact_notation {
+                    buf.push(b' ');
+                }
             }
         }
     }
 }
 
-fn write_coords(coords: &[f64], is_explicit_cmd: bool, prev_coord_has_dot: &mut bool,
-                    opt: &WriteOptions, buf: &mut Vec<u8>)
-{
+fn write_coords(
+    coords: &[f64],
+    is_explicit_cmd: bool,
+    prev_coord_has_dot: &mut bool,
+    opt: &WriteOptions,
+    buf: &mut Vec<u8>
+) {
     if opt.paths.use_compact_notation {
         for (i, num) in coords.iter().enumerate() {
             let start_pos = buf.len() - 1;
@@ -237,11 +255,20 @@ fn write_coords(coords: &[f64], is_explicit_cmd: bool, prev_coord_has_dot: &mut
                 }
             }
         }
+
+        if opt.paths.new_lines {
+            buf.push(b'\n');
+        }
     } else {
         for num in coords.iter() {
             number::write_num(num, opt.remove_leading_zero, buf);
             buf.push(b' ');
         }
+
+        if opt.paths.new_lines {
+            let len = buf.len() - 1;
+            buf[len] = b'\n';
+        }
     }
 }
 
@@ -406,4 +433,17 @@ mod tests {
 
         assert_eq_text!(path.to_string_with_opt(&opt), "m10 20A10 10 0 1 0 0 0 2 2 0 1 0 2 0");
     }
+
+    #[test]
+    fn gen_path_20() {
+        let path = Path::from_str("M 10 20 L 30 40 Z M 10 20 L 30 40").unwrap();
+
+        let mut opt = WriteOptions::default();
+        opt.paths.new_lines = true;
+
+        assert_eq_text!(path.to_string_with_opt(&opt), "M 10 20\nL 30 40\nZ\nM 10 20\nL 30 40");
+
+        opt.paths.use_compact_notation = true;
+        assert_eq_text!(path.to_string_with_opt(&opt), "M10 20\nL30 40\nZ\nM10 20\nL30 40");
+    }
 }
diff --git a/src/writer/options.rs b/src/writer/options.rs
index 702056f..91dd57d 100644
--- a/src/writer/options.rs
+++ b/src/writer/options.rs
@@ -63,6 +63,11 @@ pub struct WriteOptionsPaths {
     ///
     /// Default: disabled
     pub use_implicit_lineto_commands: bool,
+
+    /// Print each segment on a new line.
+    ///
+    /// Default: disabled
+    pub new_lines: bool,
 }
 
 /// Options that defines SVG writing.
@@ -89,6 +94,8 @@ pub struct WriteOptions {
     /// Default: 4 spaces
     pub indent: Indent,
 
+    pub attributes_indent: Indent,
+
     /// Use single quote marks instead of double quote.
     ///
     /// # Examples
@@ -157,6 +164,7 @@ impl Default for WriteOptions {
     fn default() -> WriteOptions {
         WriteOptions {
             indent: Indent::Spaces(4),
+            attributes_indent: Indent::None,
             use_single_quote: false,
             trim_hex_colors: false,
             write_hidden_attributes: false,
@@ -166,6 +174,7 @@ impl Default for WriteOptions {
                 join_arc_to_flags: false,
                 remove_duplicated_commands: false,
                 use_implicit_lineto_commands: false,
+                new_lines: false,
             },
             simplify_transform_matrices: false,
         }
diff --git a/src/writer/writer.rs b/src/writer/writer.rs
index a22a16c..c05c043 100644
--- a/src/writer/writer.rs
+++ b/src/writer/writer.rs
@@ -71,12 +71,15 @@ impl Depth {
 /// Writes a document into the buffer.
 pub fn write_dom(doc: &Document, opt: &WriteOptions, out: &mut Vec<u8>) {
     let mut depth = Depth::new(opt.indent);
+    let mut attrs_depth = Depth::new(opt.attributes_indent);
     let mut iter = doc.root().traverse();
 
+    attrs_depth.value += 1;
+
     while let Some(edge) = iter.next() {
         match edge {
             NodeEdge::Start(node) => {
-                write_start_edge(&node, &mut iter, &mut depth, opt, out)
+                write_start_edge(&node, &mut iter, &mut depth, &attrs_depth, opt, out)
             }
             NodeEdge::End(node) => {
                 write_end_edge(&node, &mut depth, opt.indent, out)
@@ -86,16 +89,22 @@ pub fn write_dom(doc: &Document, opt: &WriteOptions, out: &mut Vec<u8>) {
 }
 
 /// Writes node's start edge.
-fn write_start_edge(node: &Node, iter: &mut Traverse, depth: &mut Depth, opt: &WriteOptions,
-                    out: &mut Vec<u8>) {
+fn write_start_edge(
+    node: &Node,
+    iter: &mut Traverse,
+    depth: &mut Depth,
+    attrs_depth: &Depth,
+    opt: &WriteOptions,
+    out: &mut Vec<u8>
+) {
     match node.node_type() {
         NodeType::Root => {}
         NodeType::Element => {
             depth.write_indent(out);
-            write_element_start(node, opt, out);
+            write_element_start(node, depth, attrs_depth, opt, out);
 
             if node.is_tag_name(ElementId::Text) && node.has_children() {
-                write_text_elem(iter, opt, node, out);
+                write_text_elem(iter, depth, attrs_depth, opt, node, out);
                 write_newline(opt.indent, out);
                 return;
             }
@@ -166,11 +175,17 @@ fn write_node(prefix: &[u8], data: Ref<String>, suffix: &[u8], out: &mut Vec<u8>
 /// - tag name
 /// - attributes
 /// - closing tag, if a node has children
-fn write_element_start(node: &Node, opt: &WriteOptions, out: &mut Vec<u8>) {
+fn write_element_start(
+    node: &Node,
+    depth: &Depth,
+    attrs_depth: &Depth,
+    opt: &WriteOptions,
+    out: &mut Vec<u8>
+) {
     out.push(b'<');
 
     write_tag_name(&node.tag_name().unwrap(), out);
-    write_attributes(node, opt, out);
+    write_attributes(node, depth, attrs_depth, opt, out);
 
     if node.has_children() {
         out.push(b'>');
@@ -196,12 +211,17 @@ fn write_tag_name(tag_name: &Name<ElementId>, out: &mut Vec<u8>) {
 /// - 'id'
 /// - sorted SVG attributes
 /// - unsorted non-SVG attributes
-fn write_attributes(node: &Node, opt: &WriteOptions, out: &mut Vec<u8>) {
+fn write_attributes(
+    node: &Node,
+    depth: &Depth,
+    attrs_depth: &Depth,
+    opt: &WriteOptions,
+    out: &mut Vec<u8>
+) {
     // write 'id'
     if node.has_id() {
-        out.push(b' ');
         let attr = Attribute::new(AttributeId::Id, node.id().clone());
-        attr.write_buf_opt(opt, out);
+        write_attribute(&attr, depth, attrs_depth, opt, out);
     }
 
     let attrs = node.attributes();
@@ -220,27 +240,50 @@ fn write_attributes(node: &Node, opt: &WriteOptions, out: &mut Vec<u8>) {
             continue;
         }
 
-        out.push(b' ');
-        attr.write_buf_opt(opt, out);
+        write_attribute(attr, depth, attrs_depth, opt, out);
     }
 
     // write non-SVG attributes
     for attr in attrs.iter() {
         if let Name::Name(_) = attr.name {
-            out.push(b' ');
-            attr.write_buf_opt(opt, out);
+            write_attribute(attr, depth, attrs_depth, opt, out);
         }
     }
 }
 
+fn write_attribute(
+    attr: &Attribute,
+    depth: &Depth,
+    attrs_depth: &Depth,
+    opt: &WriteOptions,
+    out: &mut Vec<u8>
+) {
+    if opt.attributes_indent == Indent::None {
+        out.push(b' ');
+    } else {
+        out.push(b'\n');
+        depth.write_indent(out);
+        attrs_depth.write_indent(out);
+    }
+
+    attr.write_buf_opt(opt, out);
+}
+
 /// Writes a `text` element node and it's children.
-fn write_text_elem(iter: &mut Traverse, opt: &WriteOptions, root: &Node, out: &mut Vec<u8>) {
+fn write_text_elem(
+    iter: &mut Traverse,
+    depth: &Depth,
+    attrs_depth: &Depth,
+    opt: &WriteOptions,
+    root: &Node,
+    out: &mut Vec<u8>
+) {
     for edge in iter {
         match edge {
             NodeEdge::Start(node) => {
                 match node.node_type() {
                     NodeType::Element => {
-                        write_element_start(&node, opt, out);
+                        write_element_start(&node, depth, attrs_depth, opt, out);
                     }
                     NodeType::Text => {
                         out.extend_from_slice(node.text().as_bytes());
diff --git a/tests/writer.rs b/tests/writer.rs
index e113f10..902157c 100644
--- a/tests/writer.rs
+++ b/tests/writer.rs
@@ -581,6 +581,37 @@ fn indent_5() {
 }
 
 #[test]
+fn attrs_indent_1() {
+    let doc = Document::from_str(
+"<svg id='svg1' width='100' height='100'>
+    <g fill='red' stroke='blue' custom='qwe'>
+        <rect id='rect1' stroke-width='2'/>
+    </g>
+</svg>
+").unwrap();
+
+    let mut opt = WriteOptions::default();
+    opt.attributes_indent = Indent::Spaces(3);
+    opt.use_single_quote = true;
+    assert_eq_text!(doc.to_string_with_opt(&opt),
+"<svg
+   id='svg1'
+   height='100'
+   width='100'>
+    <g
+       fill='#ff0000'
+       stroke='#0000ff'
+       custom='qwe'>
+        <rect
+           id='rect1'
+           stroke-width='2'/>
+    </g>
+</svg>
+");
+}
+
+
+#[test]
 fn single_quote_1() {
     let doc = Document::from_str(
 "<svg id=\"svg1\"/>").unwrap();
@@ -590,3 +621,27 @@ fn single_quote_1() {
     opt.use_single_quote = true;
     assert_eq_text!(doc.to_string_with_opt(&opt), "<svg id='svg1'/>");
 }
+
+#[test]
+fn paths_1() {
+    use Document;
+
+    let doc = Document::from_str("
+<svg>
+<path d='M 10 20 L 30 40 Z M 10 20 L 30 40'/>
+</svg>
+").unwrap();
+
+    let mut opt = WriteOptions::default();
+    opt.use_single_quote = true;
+    opt.paths.new_lines = true;
+    assert_eq_text!(doc.to_string_with_opt(&opt), "
+<svg>
+    <path d='M 10 20
+             L 30 40
+             Z
+             M 10 20
+             L 30 40'/>
+</svg>
+");
+}