|
@@ -1,5 +1,6 @@
|
|
use std::fmt::Display;
|
|
use std::fmt::Display;
|
|
use std::fmt::{Formatter, Error};
|
|
use std::fmt::{Formatter, Error};
|
|
|
|
+use std::io;
|
|
use std::io::Write;
|
|
use std::io::Write;
|
|
|
|
|
|
/// An SVG document
|
|
/// An SVG document
|
|
@@ -20,24 +21,27 @@ impl Display for Inches {
|
|
|
|
|
|
fn inches(amt: f64) -> Inches { Inches { amt } }
|
|
fn inches(amt: f64) -> Inches { Inches { amt } }
|
|
|
|
|
|
-fn xml_tag(w: &mut Vec<u8>, name: &str, attrs: &[(&str, &Display)]) {
|
|
|
|
- write!(w, "<{}", name);
|
|
|
|
|
|
+fn xml_tag(w: &mut Write, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
|
|
|
|
+ write!(w, "<{}", name)?;
|
|
for &(k, v) in attrs {
|
|
for &(k, v) in attrs {
|
|
- write!(w, " {}=\"{}\"", k, v);
|
|
|
|
|
|
+ write!(w, " {}=\"{}\"", k, v)?;
|
|
}
|
|
}
|
|
- writeln!(w, "/>");
|
|
|
|
|
|
+ writeln!(w, "/>")?;
|
|
|
|
+ Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
-fn xml_open(w: &mut Vec<u8>, name: &str, attrs: &[(&str, &Display)]) {
|
|
|
|
- write!(w, "<{}", name);
|
|
|
|
|
|
+fn xml_open(w: &mut Write, name: &str, attrs: &[(&str, &Display)]) -> io::Result<()> {
|
|
|
|
+ write!(w, "<{}", name)?;
|
|
for &(k, v) in attrs {
|
|
for &(k, v) in attrs {
|
|
- write!(w, " {}=\"{}\"", k, v);
|
|
|
|
|
|
+ write!(w, " {}=\"{}\"", k, v)?;
|
|
}
|
|
}
|
|
- writeln!(w, ">");
|
|
|
|
|
|
+ writeln!(w, ">")?;
|
|
|
|
+ Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
-fn xml_close(w: &mut Vec<u8>, name: &str) {
|
|
|
|
- write!(w, "</{}>", name);
|
|
|
|
|
|
+fn xml_close(w: &mut Write, name: &str) -> io::Result<()> {
|
|
|
|
+ write!(w, "</{}>", name)?;
|
|
|
|
+ Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
/// Create a new empty SVG document of the specified width and height
|
|
/// Create a new empty SVG document of the specified width and height
|
|
@@ -50,24 +54,21 @@ pub fn svg(w: f64, h: f64) -> SVG {
|
|
|
|
|
|
impl SVG {
|
|
impl SVG {
|
|
/// Print this SVG document to stdout
|
|
/// Print this SVG document to stdout
|
|
- pub fn to_stdout(self) {
|
|
|
|
- let buf = self.to_bytes();
|
|
|
|
- println!("{}", String::from_utf8(buf).unwrap());
|
|
|
|
|
|
+ pub fn to_stdout(self) -> io::Result<()> {
|
|
|
|
+ self.write_svg(&mut std::io::stdout())
|
|
}
|
|
}
|
|
|
|
|
|
- /// Write this SVG document to a file
|
|
|
|
- pub fn to_file<W: Write>(self, w: &mut W) -> Result<(), std::io::Error> {
|
|
|
|
- let buf = self.to_bytes();
|
|
|
|
- w.write(&buf)?;
|
|
|
|
- Ok(())
|
|
|
|
|
|
+ pub fn to_bytes(self) -> io::Result<Vec<u8>> {
|
|
|
|
+ let mut buf = Vec::new();
|
|
|
|
+ self.write_svg(&mut buf)?;
|
|
|
|
+ Ok(buf)
|
|
}
|
|
}
|
|
|
|
|
|
- pub fn to_bytes(self) -> Vec<u8> {
|
|
|
|
- let mut buf = Vec::new();
|
|
|
|
|
|
+ pub fn write_svg<W: Write>(self, buf: &mut W) -> io::Result<()> {
|
|
let (w, h) = self.size;
|
|
let (w, h) = self.size;
|
|
- writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
|
|
|
|
|
+ writeln!(buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")?;
|
|
xml_open(
|
|
xml_open(
|
|
- &mut buf, "svg",
|
|
|
|
|
|
+ buf, "svg",
|
|
&[("xmlns", &"http://www.w3.org/2000/svg"),
|
|
&[("xmlns", &"http://www.w3.org/2000/svg"),
|
|
("version", &"1.1"),
|
|
("version", &"1.1"),
|
|
("width", &inches(w)),
|
|
("width", &inches(w)),
|
|
@@ -75,12 +76,11 @@ impl SVG {
|
|
("viewBox", &format!("0 0 {} {}", w, h)),
|
|
("viewBox", &format!("0 0 {} {}", w, h)),
|
|
("stroke-width", &"0.0001in"),
|
|
("stroke-width", &"0.0001in"),
|
|
],
|
|
],
|
|
- );
|
|
|
|
|
|
+ )?;
|
|
for elem in self.stuff {
|
|
for elem in self.stuff {
|
|
- elem.draw_svg(&mut buf);
|
|
|
|
|
|
+ elem.draw_svg(buf)?;
|
|
}
|
|
}
|
|
- xml_close(&mut buf, "svg");
|
|
|
|
- buf
|
|
|
|
|
|
+ xml_close(buf, "svg")
|
|
}
|
|
}
|
|
|
|
|
|
/// Add a new drawable thing to this SVG document
|
|
/// Add a new drawable thing to this SVG document
|
|
@@ -94,7 +94,7 @@ impl SVG {
|
|
/// The AsSVG trait represents things which can be rendered to an SVG
|
|
/// The AsSVG trait represents things which can be rendered to an SVG
|
|
/// canvas.
|
|
/// canvas.
|
|
pub trait AsSVG {
|
|
pub trait AsSVG {
|
|
- fn draw_svg(&self, buf: &mut Vec<u8>);
|
|
|
|
|
|
+ fn draw_svg(&self, buf: &mut Write) -> io::Result<()>;
|
|
// This is a bit of a hack to make sure that all of our types live
|
|
// This is a bit of a hack to make sure that all of our types live
|
|
// long enough: it's now on the implementer of AsSVG to box it up
|
|
// long enough: it's now on the implementer of AsSVG to box it up
|
|
// and make sure that we can keep a trait object around.
|
|
// and make sure that we can keep a trait object around.
|
|
@@ -110,7 +110,7 @@ pub struct Line {
|
|
}
|
|
}
|
|
|
|
|
|
impl AsSVG for Line {
|
|
impl AsSVG for Line {
|
|
- fn draw_svg(&self, buf: &mut Vec<u8>) {
|
|
|
|
|
|
+ fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
|
|
if self.points.len() == 0 {
|
|
if self.points.len() == 0 {
|
|
// if there are no later points, then we just draw a
|
|
// if there are no later points, then we just draw a
|
|
// 'point' at the starting position, which is just a tiny
|
|
// 'point' at the starting position, which is just a tiny
|
|
@@ -120,10 +120,10 @@ impl AsSVG for Line {
|
|
buf, "circle",
|
|
buf, "circle",
|
|
&[("cx", &x),
|
|
&[("cx", &x),
|
|
("cy", &y),
|
|
("cy", &y),
|
|
- ("r", &"0.01in"),
|
|
|
|
|
|
+ ("r", &"0.01"),
|
|
("fill", &"black"),
|
|
("fill", &"black"),
|
|
],
|
|
],
|
|
- );
|
|
|
|
|
|
+ )
|
|
} else {
|
|
} else {
|
|
// Otherwise, we draw a path, which mean assembling this
|
|
// Otherwise, we draw a path, which mean assembling this
|
|
// somewhat wonky path field
|
|
// somewhat wonky path field
|
|
@@ -131,17 +131,20 @@ impl AsSVG for Line {
|
|
let path = {
|
|
let path = {
|
|
let mut path = Vec::new();
|
|
let mut path = Vec::new();
|
|
let (x, y) = self.start;
|
|
let (x, y) = self.start;
|
|
- write!(&mut path, "M{} {}", x, y);
|
|
|
|
|
|
+ write!(&mut path, "M{} {}", x, y)?;
|
|
for &(x, y) in self.points.iter() {
|
|
for &(x, y) in self.points.iter() {
|
|
- write!(&mut path, " L{} {}", x, y);
|
|
|
|
|
|
+ write!(&mut path, " L{} {}", x, y)?;
|
|
}
|
|
}
|
|
String::from_utf8(path).unwrap()
|
|
String::from_utf8(path).unwrap()
|
|
};
|
|
};
|
|
|
|
|
|
xml_tag(
|
|
xml_tag(
|
|
buf, "path",
|
|
buf, "path",
|
|
- &[("d", &path), ("stroke", &"black")],
|
|
|
|
- );
|
|
|
|
|
|
+ &[("d", &path),
|
|
|
|
+ ("stroke", &"black"),
|
|
|
|
+ ("fill", &"none"),
|
|
|
|
+ ],
|
|
|
|
+ )
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -181,7 +184,7 @@ pub fn rect(position: (f64, f64), size: (f64, f64)) -> Rect {
|
|
}
|
|
}
|
|
|
|
|
|
impl AsSVG for Rect {
|
|
impl AsSVG for Rect {
|
|
- fn draw_svg(&self, buf: &mut Vec<u8>) {
|
|
|
|
|
|
+ fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
|
|
let (x, y) = self.position;
|
|
let (x, y) = self.position;
|
|
let (w, h) = self.size;
|
|
let (w, h) = self.size;
|
|
xml_tag(
|
|
xml_tag(
|
|
@@ -193,7 +196,7 @@ impl AsSVG for Rect {
|
|
("stroke", &"black"),
|
|
("stroke", &"black"),
|
|
("fill", &"white"),
|
|
("fill", &"white"),
|
|
]
|
|
]
|
|
- );
|
|
|
|
|
|
+ )
|
|
}
|
|
}
|
|
|
|
|
|
fn consume(self) -> Box<AsSVG> {
|
|
fn consume(self) -> Box<AsSVG> {
|
|
@@ -212,7 +215,7 @@ pub fn circle(position: (f64, f64), radius: f64) -> Circle {
|
|
}
|
|
}
|
|
|
|
|
|
impl AsSVG for Circle {
|
|
impl AsSVG for Circle {
|
|
- fn draw_svg(&self, buf: &mut Vec<u8>) {
|
|
|
|
|
|
+ fn draw_svg(&self, buf: &mut Write) -> io::Result<()> {
|
|
let (x, y) = self.position;
|
|
let (x, y) = self.position;
|
|
let r = self.radius;
|
|
let r = self.radius;
|
|
xml_tag(
|
|
xml_tag(
|
|
@@ -223,7 +226,7 @@ impl AsSVG for Circle {
|
|
("stroke", &"black"),
|
|
("stroke", &"black"),
|
|
("fill", &"white"),
|
|
("fill", &"white"),
|
|
]
|
|
]
|
|
- );
|
|
|
|
|
|
+ )
|
|
}
|
|
}
|
|
|
|
|
|
fn consume(self) -> Box<AsSVG> {
|
|
fn consume(self) -> Box<AsSVG> {
|