1 use std::fmt;
2 
3 /// `rgb({r},{g},{b})`
4 #[derive(Copy, Clone, PartialEq)]
5 pub struct Color {
6     pub r: u8,
7     pub g: u8,
8     pub b: u8,
9 }
10 
11 impl fmt::Display for Color {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result12     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13         write!(f, "rgb({},{},{})", self.r, self.g, self.b)
14     }
15 }
16 
rgb(r: u8, g: u8, b: u8) -> Color17 pub fn rgb(r: u8, g: u8, b: u8) -> Color { Color { r, g, b } }
black() -> Color18 pub fn black() -> Color { rgb(0, 0, 0) }
white() -> Color19 pub fn white() -> Color { rgb(255, 255, 255) }
red() -> Color20 pub fn red() -> Color { rgb(255, 0, 0) }
green() -> Color21 pub fn green() -> Color { rgb(0, 255, 0) }
blue() -> Color22 pub fn blue() -> Color { rgb(0, 0, 255) }
23 
24 /// `fill:{self}`
25 #[derive(Copy, Clone, PartialEq)]
26 pub enum Fill {
27     Color(Color),
28     None,
29 }
30 
31 /// `stroke:{self}`
32 #[derive(Copy, Clone, PartialEq)]
33 pub enum Stroke {
34     Color(Color, f32),
35     None,
36 }
37 
38 /// `fill:{fill};stroke:{stroke};fill-opacity:{opacity};`
39 #[derive(Copy, Clone, PartialEq)]
40 pub struct Style {
41     pub fill: Fill,
42     pub stroke: Stroke,
43     pub opacity: f32,
44     pub stroke_opacity: f32,
45 }
46 
47 impl fmt::Display for Style {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result48     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49         write!(f, "{};{};fill-opacity:{};stroke-opacity:{};",
50             self.fill,
51             self.stroke,
52             self.opacity,
53             self.stroke_opacity,
54         )
55     }
56 }
57 
58 impl Style {
default() -> Self59     pub fn default() -> Self {
60         Style {
61             fill: Fill::Color(black()),
62             stroke: Stroke::None,
63             opacity: 1.0,
64             stroke_opacity: 1.0,
65         }
66     }
67 }
68 
69 impl fmt::Display for Fill {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result70     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71         match self {
72             Fill::Color(color) => write!(f, "fill:{}", color),
73             Fill::None => write!(f, "fill:none"),
74         }
75     }
76 }
77 
78 impl fmt::Display for Stroke {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result79     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80         match self {
81             Stroke::Color(color, radius) => write!(f, "stroke:{};stroke-width:{}", color, radius),
82             Stroke::None => write!(f, "stroke:none"),
83         }
84     }
85 }
86 
87 impl Into<Fill> for Color {
into(self) -> Fill88     fn into(self) -> Fill {
89         Fill::Color(self)
90     }
91 }
92 
93 impl Into<Stroke> for Color {
into(self) -> Stroke94     fn into(self) -> Stroke {
95         Stroke::Color(self, 1.0)
96     }
97 }
98 
99 /// `<rect x="{x}" y="{y}" width="{w}" height="{h}" ... />`,
100 #[derive(Copy, Clone, PartialEq)]
101 pub struct Rectangle {
102     pub x: f32,
103     pub y: f32,
104     pub w: f32,
105     pub h: f32,
106     pub style: Style,
107     pub border_radius: f32,
108 }
109 
rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle110 pub fn rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle {
111     Rectangle {
112         x, y, w, h,
113         style: Style::default(),
114         border_radius: 0.0,
115     }
116 }
117 
118 impl Rectangle {
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>119     pub fn fill<F>(mut self, fill: F) -> Self
120     where F: Into<Fill> {
121         self.style.fill = fill.into();
122         self
123     }
124 
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>125     pub fn stroke<S>(mut self, stroke: S) -> Self
126     where S: Into<Stroke> {
127         self.style.stroke = stroke.into();
128         self
129     }
130 
opacity(mut self, opacity: f32) -> Self131     pub fn opacity(mut self, opacity: f32) -> Self {
132         self.style.opacity = opacity;
133         self
134     }
135 
stroke_opacity(mut self, opacity: f32) -> Self136     pub fn stroke_opacity(mut self, opacity: f32) -> Self {
137         self.style.stroke_opacity = opacity;
138         self
139     }
140 
style(mut self, style: Style) -> Self141     pub fn style(mut self, style: Style) -> Self {
142         self.style = style;
143         self
144     }
145 
border_radius(mut self, r: f32) -> Self146     pub fn border_radius(mut self, r: f32) -> Self {
147         self.border_radius = r;
148         self
149     }
150 
offset(mut self, dx: f32, dy: f32) -> Self151     pub fn offset(mut self, dx: f32, dy: f32) -> Self {
152         self.x += dx;
153         self.y += dy;
154         self
155     }
156 
inflate(mut self, dx: f32, dy: f32) -> Self157     pub fn inflate(mut self, dx: f32, dy: f32) -> Self {
158         self.x -= dx;
159         self.y -= dy;
160         self.w += 2.0 * dx;
161         self.h += 2.0 * dy;
162         self
163     }
164 }
165 
166 impl fmt::Display for Rectangle {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result167     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168         write!(f,
169             r#"<rect x="{}" y="{}" width="{}" height="{}" ry="{}" style="{}" />""#,
170             self.x, self.y, self.w, self.h,
171             self.border_radius,
172             self.style,
173         )
174     }
175 }
176 
177 /// `<circle cx="{x}" cy="{y}" r="{radius}" .../>`
178 #[derive(Copy, Clone, PartialEq)]
179 pub struct Circle {
180     pub x: f32,
181     pub y: f32,
182     pub radius: f32,
183     pub style: Style,
184 }
185 
186 impl Circle {
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>187     pub fn fill<F>(mut self, fill: F) -> Self
188     where F: Into<Fill> {
189         self.style.fill = fill.into();
190         self
191     }
192 
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>193     pub fn stroke<S>(mut self, stroke: S) -> Self
194     where S: Into<Stroke> {
195         self.style.stroke = stroke.into();
196         self
197     }
198 
style(mut self, style: Style) -> Self199     pub fn style(mut self, style: Style) -> Self {
200         self.style = style;
201         self
202     }
203 
opacity(mut self, opacity: f32) -> Self204     pub fn opacity(mut self, opacity: f32) -> Self {
205         self.style.opacity = opacity;
206         self
207     }
208 
stroke_opacity(mut self, opacity: f32) -> Self209     pub fn stroke_opacity(mut self, opacity: f32) -> Self {
210         self.style.stroke_opacity = opacity;
211         self
212     }
213 
offset(mut self, dx: f32, dy: f32) -> Self214     pub fn offset(mut self, dx: f32, dy: f32) -> Self {
215         self.x += dx;
216         self.y += dy;
217         self
218     }
219 
inflate(mut self, by: f32) -> Self220     pub fn inflate(mut self, by: f32) -> Self {
221         self.radius += by;
222         self
223     }
224 }
225 
226 impl fmt::Display for Circle {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result227     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228         write!(f,
229             r#"<circle cx="{}" cy="{}" r="{}" style="{}" />""#,
230             self.x, self.y, self.radius,
231             self.style,
232         )
233     }
234 }
235 
236 /// `<path d="..." style="..."/>`
237 #[derive(Clone, PartialEq)]
238 pub struct Polygon {
239     pub points: Vec<[f32; 2]>,
240     pub closed: bool,
241     pub style: Style,
242 }
243 
244 impl fmt::Display for Polygon {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result245     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246         write!(f, r#"<path d="#)?;
247         if self.points.len() > 0 {
248             write!(f, "M {} {} ", self.points[0][0], self.points[0][1])?;
249             for &p in &self.points[1..] {
250                 write!(f, "L {} {} ", p[0], p[1])?;
251             }
252             if self.closed {
253                 write!(f, "Z")?;
254             }
255         }
256         write!(f, r#"" style="{}"/>"#, self.style)
257     }
258 }
259 
polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) -> Polygon260 pub fn polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) ->  Polygon {
261     let mut points = Vec::with_capacity(pts.len());
262     for p in pts {
263         points.push((*p).into());
264     }
265     Polygon {
266         points,
267         closed: true,
268         style: Style::default(),
269     }
270 }
271 
triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon272 pub fn triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon {
273     polygon(&[[x1, y1], [x2, y2], [x3, y3]])
274 }
275 
276 impl Polygon {
open(mut self) -> Self277     pub fn open(mut self) -> Self {
278         self.closed = false;
279         self
280     }
281 
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>282     pub fn fill<F>(mut self, fill: F) -> Self
283     where F: Into<Fill> {
284         self.style.fill = fill.into();
285         self
286     }
287 
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>288     pub fn stroke<S>(mut self, stroke: S) -> Self
289     where S: Into<Stroke> {
290         self.style.stroke = stroke.into();
291         self
292     }
293 
opacity(mut self, opacity: f32) -> Self294     pub fn opacity(mut self, opacity: f32) -> Self {
295         self.style.opacity = opacity;
296         self
297     }
298 
stroke_opacity(mut self, opacity: f32) -> Self299     pub fn stroke_opacity(mut self, opacity: f32) -> Self {
300         self.style.stroke_opacity = opacity;
301         self
302     }
303 
style(mut self, style: Style) -> Self304     pub fn style(mut self, style: Style) -> Self {
305         self.style = style;
306         self
307     }
308 }
309 
310 /// `<path d="M {x1} {y1} L {x2} {y2}" ... />`
311 #[derive(Copy, Clone, PartialEq)]
312 pub struct LineSegment {
313     pub x1: f32,
314     pub x2: f32,
315     pub y1: f32,
316     pub y2: f32,
317     pub color: Color,
318     pub width: f32,
319 }
320 
321 impl fmt::Display for LineSegment {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result322     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323         write!(f,
324             r#"<path d="M {} {} L {} {}" style="stroke:{};stroke-width:{}"/>"#,
325             self.x1, self.y1,
326             self.x2, self.y2,
327             self.color,
328             self.width,
329         )
330     }
331 }
332 
line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment333 pub fn line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment {
334     LineSegment {
335         x1, y1, x2, y2,
336         color: black(),
337         width: 1.0,
338     }
339 }
340 
341 impl LineSegment {
color(mut self, color: Color) -> Self342     pub fn color(mut self, color: Color) -> Self {
343         self.color = color;
344         self
345     }
346 
width(mut self, width: f32) -> Self347     pub fn width(mut self, width: f32) -> Self {
348         self.width = width;
349         self
350     }
351 
offset(mut self, dx: f32, dy: f32) -> Self352     pub fn offset(mut self, dx: f32, dy: f32) -> Self {
353         self.x1 += dx;
354         self.y1 += dy;
355         self.x2 += dx;
356         self.y2 += dy;
357         self
358     }
359 }
360 
361 /// `<path d="..." />`
362 #[derive(Clone, PartialEq)]
363 pub struct Path {
364     pub ops: Vec<PathOp>,
365     pub style: Style,
366 }
367 
368 /// `M {} {} L {} {} ...`
369 #[derive(Copy, Clone, PartialEq)]
370 pub enum PathOp {
371     MoveTo { x: f32, y: f32 },
372     LineTo { x: f32, y: f32 },
373     QuadraticTo { ctrl_x: f32, ctrl_y: f32, x: f32, y: f32 },
374     CubicTo { ctrl1_x: f32, ctrl1_y: f32, ctrl2_x: f32, ctrl2_y: f32, x: f32, y: f32 },
375     Close,
376 }
377 impl fmt::Display for PathOp {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result378     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
379         match *self {
380             PathOp::MoveTo { x, y } => write!(f, "M {} {} ", x, y),
381             PathOp::LineTo { x, y } => write!(f, "L {} {} ", x, y),
382             PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y } => write!(f, "Q {} {} {} {} ", ctrl_x, ctrl_y, x, y),
383             PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y } => write!(f, "C {} {} {} {} {} {} ", ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y),
384             PathOp::Close => write!(f, "Z "),
385         }
386     }
387 }
388 
389 impl fmt::Display for Path {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result390     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391         write!(f, r#"<path d=""#)?;
392         for op in &self.ops {
393             op.fmt(f)?;
394         }
395         write!(f, r#"" style="{}" />"#, self.style)
396     }
397 }
398 
399 impl Path {
move_to(mut self, x: f32, y: f32) -> Self400     pub fn move_to(mut self, x: f32, y: f32) -> Self {
401         self.ops.push(PathOp::MoveTo { x, y });
402         self
403     }
404 
line_to(mut self, x: f32, y: f32) -> Self405     pub fn line_to(mut self, x: f32, y: f32) -> Self {
406         self.ops.push(PathOp::LineTo { x, y });
407         self
408     }
409 
quadratic_bezier_to( mut self, ctrl_x: f32, ctrl_y: f32, x: f32, y: f32, ) -> Self410     pub fn quadratic_bezier_to(
411         mut self,
412         ctrl_x: f32, ctrl_y: f32,
413         x: f32, y: f32,
414     ) -> Self {
415         self.ops.push(PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y });
416         self
417     }
418 
cubic_bezier_to( mut self, ctrl1_x: f32, ctrl1_y: f32, ctrl2_x: f32, ctrl2_y: f32, x: f32, y: f32, ) -> Self419     pub fn cubic_bezier_to(
420         mut self,
421         ctrl1_x: f32, ctrl1_y: f32,
422         ctrl2_x: f32, ctrl2_y: f32,
423         x: f32, y: f32,
424     ) -> Self {
425         self.ops.push(PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y });
426         self
427     }
428 
close(mut self) -> Self429     pub fn close(mut self) -> Self {
430         self.ops.push(PathOp::Close);
431         self
432     }
433 
fill<F>(mut self, fill: F) -> Self where F: Into<Fill>434     pub fn fill<F>(mut self, fill: F) -> Self
435     where F: Into<Fill> {
436         self.style.fill = fill.into();
437         self
438     }
439 
stroke<S>(mut self, stroke: S) -> Self where S: Into<Stroke>440     pub fn stroke<S>(mut self, stroke: S) -> Self
441     where S: Into<Stroke> {
442         self.style.stroke = stroke.into();
443         self
444     }
445 
opacity(mut self, opacity: f32) -> Self446     pub fn opacity(mut self, opacity: f32) -> Self {
447         self.style.opacity = opacity;
448         self
449     }
450 
stroke_opacity(mut self, opacity: f32) -> Self451     pub fn stroke_opacity(mut self, opacity: f32) -> Self {
452         self.style.stroke_opacity = opacity;
453         self
454     }
455 
style(mut self, style: Style) -> Self456     pub fn style(mut self, style: Style) -> Self {
457         self.style = style;
458         self
459     }
460 }
461 
path() -> Path462 pub fn path() -> Path {
463     Path {
464         ops: Vec::new(),
465         style: Style::default(),
466     }
467 }
468 
469 /// `<text x="{x}" y="{y}" ... > {text} </text>`
470 #[derive(Clone, PartialEq)]
471 pub struct Text {
472     pub x: f32, pub y: f32,
473     pub text: String,
474     pub color: Color,
475     pub align: Align,
476     pub size: f32,
477 }
478 
479 impl fmt::Display for Text {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result480     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481         write!(f,
482             r#"<text x="{}" y="{}" style="font-size:{}px;fill:{};{}"> {} </text>"#,
483             self.x, self.y,
484             self.size,
485             self.color,
486             self.align,
487             self.text,
488         )
489     }
490 }
491 
text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text492 pub fn text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text {
493     Text {
494         x, y,
495         text: txt.into(),
496         color: black(),
497         align: Align::Left,
498         size: 10.0,
499     }
500 }
501 
502 impl Text {
color(mut self, color: Color) -> Self503     pub fn color(mut self, color: Color) -> Self {
504         self.color = color;
505         self
506     }
507 
size(mut self, size: f32) -> Self508     pub fn size(mut self, size: f32) -> Self {
509         self.size = size;
510         self
511     }
512 
align(mut self, align: Align) -> Self513     pub fn align(mut self, align: Align) -> Self {
514         self.align = align;
515         self
516     }
517 
offset(mut self, dx: f32, dy: f32) -> Self518     pub fn offset(mut self, dx: f32, dy: f32) -> Self {
519         self.x += dx;
520         self.y += dy;
521         self
522     }
523 }
524 
525 pub struct Comment {
526     pub text: String,
527 }
528 
comment<T: Into<String>>(text: T) -> Comment529 pub fn comment<T: Into<String>>(text: T) -> Comment {
530     Comment {
531         text: text.into()
532     }
533 }
534 
535 impl fmt::Display for Comment {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result536     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537         write!(f, "<!-- {} -->", self.text)
538     }
539 }
540 
541 /// `text-align:{self}`
542 #[derive(Copy, Clone, PartialEq)]
543 pub enum Align {
544     Left, Right, Center
545 }
546 
547 impl fmt::Display for Align {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result548     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549         match *self {
550             Align::Left => write!(f, "text-anchor:start;text-align:left;"),
551             Align::Right => write!(f, "text-anchor:end;text-align:right;"),
552             Align::Center => write!(f, "text-anchor:middle;text-align:center;"),
553         }
554     }
555 }
556 
557 /// `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {y}">`
558 #[derive(Copy, Clone, PartialEq)]
559 pub struct BeginSvg {
560     pub w: f32,
561     pub h: f32,
562 }
563 
564 impl fmt::Display for BeginSvg {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result565     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
566         write!(f,
567             r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {} {}">"#,
568             self.w,
569             self.h,
570         )
571     }
572 }
573 
574 
575 /// `</svg>`
576 #[derive(Copy, Clone, PartialEq)]
577 pub struct EndSvg;
578 
579 impl fmt::Display for EndSvg {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result580     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581         write!(f, "</svg>")
582     }
583 }
584 
585 /// `"    "`
586 pub struct Indentation {
587     pub n: u32,
588 }
589 
indent(n: u32) -> Indentation590 pub fn indent(n: u32) -> Indentation {
591     Indentation { n }
592 }
593 
594 impl Indentation {
push(&mut self)595     pub fn push(&mut self) {
596         self.n += 1;
597     }
598 
pop(&mut self)599     pub fn pop(&mut self) {
600         self.n -= 1;
601     }
602 }
603 
604 impl fmt::Display for Indentation {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result605     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
606         for _ in 0..self.n {
607             write!(f, "    ")?;
608         }
609         Ok(())
610     }
611 }
612 
613 #[test]
foo()614 fn foo() {
615     println!("{}", BeginSvg { w: 800.0, h: 600.0 });
616     println!("    {}",
617         rectangle(20.0, 50.0, 200.0, 100.0)
618             .fill(red())
619             .stroke(Stroke::Color(black(), 3.0))
620             .border_radius(5.0)
621     );
622     println!("    {}", text(25.0, 100.0, "Foo!").size(42.0).color(white()));
623     println!("{}", EndSvg);
624 }
625