gdritter repos gunpowder_treason / 5e66bb3
Thread writer abstraction through AsSVG Getty Ritter 5 years ago
1 changed file(s) with 50 addition(s) and 47 deletion(s). Collapse all Expand all
11 use std::fmt::Display;
22 use std::fmt::{Formatter, Error};
3 use std::io;
34 use std::io::Write;
45
56 /// An SVG document
2021
2122 fn inches(amt: f64) -> Inches { Inches { amt } }
2223
23 fn xml_tag(w: &mut Vec<u8>, name: &str, attrs: &[(&str, &Display)]) {
24 write!(w, "<{}", name);
24 fn xml_tag(w: &mut Write, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
25 write!(w, "<{}", name)?;
2526 for &(k, v) in attrs {
26 write!(w, " {}=\"{}\"", k, v);
27 }
28 writeln!(w, "/>");
29 }
30
31 fn xml_open(w: &mut Vec<u8>, name: &str, attrs: &[(&str, &Display)]) {
32 write!(w, "<{}", name);
27 write!(w, " {}=\"{}\"", k, v)?;
28 }
29 writeln!(w, "/>")?;
30 Ok(())
31 }
32
33 fn xml_open(w: &mut Write, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
34 write!(w, "<{}", name)?;
3335 for &(k, v) in attrs {
34 write!(w, " {}=\"{}\"", k, v);
35 }
36 writeln!(w, ">");
37 }
38
39 fn xml_close(w: &mut Vec<u8>, name: &str) {
40 write!(w, "</{}>", name);
36 write!(w, " {}=\"{}\"", k, v)?;
37 }
38 writeln!(w, ">")?;
39 Ok(())
40 }
41
42 fn xml_close(w: &mut Write, name: &str) -> io::Result<()> {
43 write!(w, "</{}>", name)?;
44 Ok(())
4145 }
4246
4347 /// Create a new empty SVG document of the specified width and height
5054
5155 impl SVG {
5256 /// Print this SVG document to stdout
53 pub fn to_stdout(self) {
54 let buf = self.to_bytes();
55 println!("{}", String::from_utf8(buf).unwrap());
56 }
57
58 /// Write this SVG document to a file
59 pub fn to_file<W: Write>(self, w: &mut W) -> Result<(), std::io::Error> {
60 let buf = self.to_bytes();
61 w.write(&buf)?;
62 Ok(())
63 }
64
65 pub fn to_bytes(self) -> Vec<u8> {
57 pub fn to_stdout(self) -> io::Result<()> {
58 self.write_svg(&mut std::io::stdout())
59 }
60
61 pub fn to_bytes(self) -> io::Result<Vec<u8>> {
6662 let mut buf = Vec::new();
63 self.write_svg(&mut buf)?;
64 Ok(buf)
65 }
66
67 pub fn write_svg<W: Write>(self, buf: &mut W) -> io::Result<()> {
6768 let (w, h) = self.size;
68 writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
69 writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
6970 xml_open(
70 &mut buf, "svg",
71 buf, "svg",
7172 &[("xmlns", &"http://www.w3.org/2000/svg"),
7273 ("version", &"1.1"),
7374 ("width", &inches(w)),
7576 ("viewBox", &format!("0 0 {} {}", w, h)),
7677 ("stroke-width", &"0.0001in"),
7778 ],
78 );
79 )?;
7980 for elem in self.stuff {
80 elem.draw_svg(&mut buf);
81 elem.draw_svg(buf)?;
8182 }
82 xml_close(&mut buf, "svg");
83 buf
83 xml_close(buf, "svg")
8484 }
8585
8686 /// Add a new drawable thing to this SVG document
9494 /// The AsSVG trait represents things which can be rendered to an SVG
9595 /// canvas.
9696 pub trait AsSVG {
97 fn draw_svg(&self, buf: &mut Vec<u8>);
97 fn draw_svg(&self, buf: &mut Write) -> io::Result<()>;
9898 // This is a bit of a hack to make sure that all of our types live
9999 // long enough: it's now on the implementer of AsSVG to box it up
100100 // and make sure that we can keep a trait object around.
110110 }
111111
112112 impl AsSVG for Line {
113 fn draw_svg(&self, buf: &mut Vec<u8>) {
113 fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
114114 if self.points.len() == 0 {
115115 // if there are no later points, then we just draw a
116116 // 'point' at the starting position, which is just a tiny
120120 buf, "circle",
121121 &[("cx", &x),
122122 ("cy", &y),
123 ("r", &"0.01in"),
123 ("r", &"0.01"),
124124 ("fill", &"black"),
125125 ],
126 );
126 )
127127 } else {
128128 // Otherwise, we draw a path, which mean assembling this
129129 // somewhat wonky path field
131131 let path = {
132132 let mut path = Vec::new();
133133 let (x, y) = self.start;
134 write!(&mut path, "M{} {}", x, y);
134 write!(&mut path, "M{} {}", x, y)?;
135135 for &(x, y) in self.points.iter() {
136 write!(&mut path, " L{} {}", x, y);
136 write!(&mut path, " L{} {}", x, y)?;
137137 }
138138 String::from_utf8(path).unwrap()
139139 };
140140
141141 xml_tag(
142142 buf, "path",
143 &[("d", &path), ("stroke", &"black")],
144 );
143 &[("d", &path),
144 ("stroke", &"black"),
145 ("fill", &"none"),
146 ],
147 )
145148 }
146149 }
147150
181184 }
182185
183186 impl AsSVG for Rect {
184 fn draw_svg(&self, buf: &mut Vec<u8>) {
187 fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
185188 let (x, y) = self.position;
186189 let (w, h) = self.size;
187190 xml_tag(
193196 ("stroke", &"black"),
194197 ("fill", &"white"),
195198 ]
196 );
199 )
197200 }
198201
199202 fn consume(self) -> Box<AsSVG> {
212215 }
213216
214217 impl AsSVG for Circle {
215 fn draw_svg(&self, buf: &mut Vec<u8>) {
218 fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
216219 let (x, y) = self.position;
217220 let r = self.radius;
218221 xml_tag(
223226 ("stroke", &"black"),
224227 ("fill", &"white"),
225228 ]
226 );
229 )
227230 }
228231
229232 fn consume(self) -> Box<AsSVG> {