microcad_export/svg/
writer.rs1use microcad_core::*;
7
8use crate::svg::{SvgTagAttributes, canvas::Canvas};
9
10pub struct SvgWriter {
12 writer: Box<dyn std::io::Write>,
14 level: usize,
16 canvas: Canvas,
18}
19
20impl SvgWriter {
21 pub fn new_canvas(
27 mut writer: Box<dyn std::io::Write>,
28 size: Option<Size2>,
29 content_rect: Rect,
30 scale: Option<Scalar>,
31 ) -> std::io::Result<Self> {
32 let size = match size {
33 Some(size) => size,
34 None => Size2 {
35 width: content_rect.width(),
36 height: content_rect.height(),
37 },
38 };
39 let x = 0;
40 let y = 0;
41 let w = size.width;
42 let h = size.height;
43 let canvas = Canvas::new_centered_content(size, content_rect, scale);
44
45 writeln!(&mut writer, "<?xml version='1.0' encoding='UTF-8'?>")?;
46 writeln!(
47 &mut writer,
48 "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' viewBox='{x} {y} {w} {h}' width='{w}mm' height='{h}mm'>",
49 )?;
50 writeln!(
51 &mut writer,
52 r#"
53 <defs>
54 <!-- A marker to be used as an arrowhead -->
55 <marker
56 id="arrow"
57 viewBox="0 0 16 16"
58 refX="8"
59 refY="8"
60 markerWidth="9"
61 markerHeight="9"
62 orient="auto-start-reverse">
63 <path d="M 0 0 L 16 8 L 0 16 z" stroke="none" fill="context-fill" />
64 </marker>
65 </defs>
66 "#
67 )?;
68
69 Ok(Self {
70 writer: Box::new(writer),
71 level: 1,
72 canvas,
73 })
74 }
75
76 pub fn canvas(&self) -> &Canvas {
78 &self.canvas
79 }
80
81 fn tag_inner(tag: &str, attr: &SvgTagAttributes) -> String {
82 format!(
83 "{tag}{attr}",
84 attr = if attr.is_empty() {
85 String::new()
86 } else {
87 format!(" {attr}")
88 }
89 )
90 }
91
92 pub fn with_indent(&mut self, s: &str) -> std::io::Result<()> {
94 writeln!(self.writer, "{:indent$}{s}", "", indent = 2 * self.level)
95 }
96
97 pub fn tag(&mut self, tag: &str, attr: &SvgTagAttributes) -> std::io::Result<()> {
99 self.with_indent(&format!(
100 "<{tag_inner}/>",
101 tag_inner = Self::tag_inner(tag, attr)
102 ))
103 }
104
105 pub fn open_tag(&mut self, tag: &str, attr: &SvgTagAttributes) -> std::io::Result<()> {
107 self.with_indent(&format!(
108 "<{tag_inner}>",
109 tag_inner = Self::tag_inner(tag, attr)
110 ))?;
111
112 self.level += 1;
113 Ok(())
114 }
115
116 pub fn close_tag(&mut self, tag: &str) -> std::io::Result<()> {
118 self.level -= 1;
119 self.with_indent(format!("</{tag}>").as_str())
120 }
121
122 pub fn begin_group(&mut self, attr: &SvgTagAttributes) -> std::io::Result<()> {
124 self.open_tag("g", attr)
125 }
126
127 pub fn end_group(&mut self) -> std::io::Result<()> {
129 self.close_tag("g")
130 }
131
132 pub fn defs(&mut self, inner: &str) -> std::io::Result<()> {
134 self.open_tag("defs", &Default::default())?;
135 self.with_indent(inner)?;
136 self.close_tag("defs")
137 }
138
139 pub fn style(&mut self, inner: &str) -> std::io::Result<()> {
141 self.open_tag("style", &Default::default())?;
142 self.with_indent(inner)?;
143 self.close_tag("style")
144 }
145
146 pub fn finish(&mut self) -> std::io::Result<()> {
148 writeln!(self.writer, "</svg>")
149 }
150}
151
152impl Drop for SvgWriter {
153 fn drop(&mut self) {
154 self.finish().expect("No error")
155 }
156}