1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
use super::backend::*; use std::io::{Error, Write}; pub struct SvgBackend<W> { pub out: W, pub float_precision: usize } impl <W: Write> SvgBackend<W> { pub fn new(mut out: W) -> Result<SvgBackend<W>, Error> { write!(out, "<svg xmlns=\"http://www.w3.org/2000/svg\">\n")?; Ok(SvgBackend { out: out, float_precision: 2, }) } } impl <W: Write> DrawBackend for SvgBackend<W> { type Error = Error; fn apply(&mut self, command: Command) -> Result<(), Error>{ use super::backend::Command::*; match command { StartShape(options) => write!(&mut self.out, r#" <path fill-rule="evenodd" d=""#), MoveTo { x , y } => write!(&mut self.out, "M{:.p$},{:.p$} ", x, y, p=self.float_precision), LineTo { x, y } => write!(&mut self.out, "L{:.p$},{:.p$} ", x, y, p=self.float_precision), CubicCurveTo { cx1, cy1, cx2, cy2, x, y, } => unimplemented!(), QuadraticCurveTo { cx, cy, x, y, } => unimplemented!(), ArcTo { rx, ry, rotation, large_arc, sweep, x, y, } => unimplemented!(), CloseShape => writeln!(&mut self.out, r#"z"/>"#), EndShape => writeln!(&mut self.out, r#""/>"#), } } fn close(mut self) -> Result<(), Error> { write!(&mut self.out, "</svg>") } } #[cfg(test)] mod test { use super::super::*; use super::*; fn run_in_canvas<F>(f: F) -> String where F: FnOnce(&mut Canvas<SvgBackend<&mut Vec<u8>>>) -> Result<(), std::io::Error> { let mut buffer: Vec<u8> = Vec::new(); { let mut canvas = Canvas::new(SvgBackend::new(&mut buffer).unwrap()); f(&mut canvas).unwrap(); canvas.close().unwrap(); } return String::from_utf8(buffer).unwrap(); } #[test] fn empty() { assert_eq!(run_in_canvas(|canvas| { Ok(()) }).trim(), r#" <svg xmlns="http://www.w3.org/2000/svg"> </svg> "#.trim()) } #[test] fn empty_polygon() { assert_eq!(run_in_canvas(|canvas| { canvas.draw_closed_polygon(&[], DrawOptions::default())?; Ok(()) }).trim(), r#" <svg xmlns="http://www.w3.org/2000/svg"> </svg> "#.trim()) } #[test] fn triangle_polygon() { assert_eq!(run_in_canvas(|canvas| { canvas.draw_closed_polygon(&[(0.0, 0.0), (0.0, 50.0), (50.0, 0.0)], DrawOptions::default())?; Ok(()) }).trim(), r#" <svg xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M0,0 L0,50 L50,0 z"/> </svg> "#.trim()) } #[test] fn triangle_polygon_reversed() { assert_eq!(run_in_canvas(|canvas| { canvas.draw_closed_polygon(&[(50.0, 0.0), (0.0, 50.0), (0.0, 0.0)], DrawOptions::default())?; Ok(()) }).trim(), r#" <svg xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M0,0 L0,50 L50,0 z"/> </svg> "#.trim()) } #[test] fn triangle_polygon_reversed_with_holes() { assert_eq!(run_in_canvas(|canvas| { canvas.draw_holy_polygon( vec![&[(50.0, 0.0), (0.0, 50.0), (0.0, 0.0)] as &[_]], vec![&[(30.0, 10.0), (10.0, 30.0), (10.0, 10.0)] as &[_]], DrawOptions::default() ) }).trim(), r#" <svg xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M50,0 L0,0 L0,50 L50,0 M30,10 L10,30 L10,10 L30,10 "/> </svg> "#.trim()) } }