diff --git a/src/attribute/attribute_value.rs b/src/attribute/attribute_value.rs
index 22f080e..bee5b76 100644
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>
+");
+}