Bug 1547290 - Vendoring changes. r=kvark"
authorNicolas Silva <nsilva@mozilla.com>
Mon, 29 Apr 2019 17:06:41 +0200
changeset 471892 e82b3333dfaaddf9c440d3ea3274393ff60d38e6
parent 471891 7fb543b7509fe6ea69d90a6762080379a07bfb7a
child 471893 2d94a6f65792aff0b4638dd8a94ed23213c27626
push id35942
push userarchaeopteryx@coole-files.de
push dateTue, 30 Apr 2019 11:33:43 +0000
treeherdermozilla-central@83950e038314 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1547290
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1547290 - Vendoring changes. r=kvark" Differential Revision: https://phabricator.services.mozilla.com/D29004
Cargo.lock
third_party/rust/svg_fmt/.cargo-checksum.json
third_party/rust/svg_fmt/Cargo.toml
third_party/rust/svg_fmt/README.md
third_party/rust/svg_fmt/src/layout.rs
third_party/rust/svg_fmt/src/lib.rs
third_party/rust/svg_fmt/src/svg.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2810,16 +2810,21 @@ dependencies = [
  "size_of_test 0.0.1",
  "smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
  "to_shmem 0.0.1",
 ]
 
 [[package]]
+name = "svg_fmt"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "syn"
 version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -3340,16 +3345,17 @@ dependencies = [
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "plane-split 0.13.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.60.0",
  "webrender_build 0.0.1",
  "wr_malloc_size_of 0.0.1",
  "ws 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -3809,16 +3815,17 @@ dependencies = [
 "checksum smallbitvec 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1764fe2b30ee783bfe3b9b37b2649d8d590b3148bb12e0079715d4d5c673562e"
 "checksum smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "622df2d454c29a4d89b30dc3b27b42d7d90d6b9e587dbf8f67652eb7514da484"
 "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
 "checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970"
 "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
 "checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da"
 "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
+"checksum svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"
 "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
 "checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
 "checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
 "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
 "checksum target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0ab4982b8945c35cc1c46a83a9094c414f6828a099ce5dcaa8ee2b04642dcb"
 "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
 "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
 "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
new file mode 100644
--- /dev/null
+++ b/third_party/rust/svg_fmt/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{"Cargo.toml":"ceb0db86bf7dc037a5f1c60d721e011c997af4d0b3039969eebf4ec7963ef1d8","README.md":"efffe3ac4d3b72a3786c842724f78721f65047c2420a51362355b73e6c5f879d","src/layout.rs":"dc1378e2911912b59e826b31e30306ecc47cf2f12bada184c7c73a21c3a15acf","src/lib.rs":"983b5686a05b093bf405418fe42eb662f72deb9f05c30c6ea122c477683ad405","src/svg.rs":"6c04d9bcee8a2e5724c6e3ec77526b52433529270e627bd8e693224c197923e2"},"package":"c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/svg_fmt/Cargo.toml
@@ -0,0 +1,24 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "svg_fmt"
+version = "0.4.0"
+authors = ["Nicolas Silva <nical@fastmail.com>"]
+description = "Very simple debugging utilities to dump shapes in SVG format."
+documentation = "https://docs.rs/svg_fmt/"
+keywords = ["2d", "graphics", "svg"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/nical/rust_debug"
+
+[dependencies]
new file mode 100644
--- /dev/null
+++ b/third_party/rust/svg_fmt/README.md
@@ -0,0 +1,28 @@
+# svg_fmt
+
+A set of simple types using `Display` formatters `{}` to easily write in the SVG format.
+This can be useful to dump information in a visual way when debugging.
+
+The crate is very small (and has no dependency).
+
+## Example
+
+```rust
+use svg_fmt::*;
+
+println!("{}", BeginSvg { w: 800.0, h: 600.0 });
+println!("    {}",
+    rectangle(20.0, 50.0, 200.0, 100.0)
+        .fill(Fill::Color(red()))
+        .stroke(Stroke::Color(black(), 3.0))
+        .border_radius(5.0)
+);
+println!("    {}",
+    text(25.0, 100.0, "Hi!")
+        .size(42.0)
+        .color(white())
+);
+println!("{}", EndSvg);
+
+```
+
new file mode 100644
--- /dev/null
+++ b/third_party/rust/svg_fmt/src/layout.rs
@@ -0,0 +1,43 @@
+use crate::svg::{Rectangle, rectangle};
+
+#[derive(Copy, Clone, Debug)]
+pub struct VerticalLayout {
+    pub x: f32,
+    pub y: f32,
+    pub start_y: f32,
+    pub width: f32,
+}
+
+impl VerticalLayout {
+    pub fn new(x: f32, y: f32, width: f32) -> Self {
+        VerticalLayout {
+            x,
+            y,
+            start_y: y,
+            width,
+        }
+    }
+
+    pub fn advance(&mut self, by: f32) {
+        self.y += by;
+    }
+
+    pub fn push_rectangle(&mut self, height: f32) -> Rectangle {
+        let rect = rectangle(self.x, self.y, self.width, height);
+
+        self.y += height;
+
+        rect
+    }
+
+    pub fn total_rectangle(&self) -> Rectangle {
+        rectangle(
+            self.x, self.start_y,
+            self.width, self.y,
+        )
+    }
+
+    pub fn start_here(&mut self) {
+        self.start_y = self.y;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rust/svg_fmt/src/lib.rs
@@ -0,0 +1,5 @@
+mod svg;
+mod layout;
+
+pub use svg::*;
+pub use layout::*;
new file mode 100644
--- /dev/null
+++ b/third_party/rust/svg_fmt/src/svg.rs
@@ -0,0 +1,586 @@
+use std::fmt;
+
+/// `rgb({r},{g},{b})`
+#[derive(Copy, Clone, PartialEq)]
+pub struct Color {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+}
+
+impl fmt::Display for Color {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "rgb({},{},{})", self.r, self.g, self.b)
+    }
+}
+
+pub fn rgb(r: u8, g: u8, b: u8) -> Color { Color { r, g, b } }
+pub fn black() -> Color { rgb(0, 0, 0) }
+pub fn white() -> Color { rgb(255, 255, 255) }
+pub fn red() -> Color { rgb(255, 0, 0) }
+pub fn green() -> Color { rgb(0, 255, 0) }
+pub fn blue() -> Color { rgb(0, 0, 255) }
+
+/// `fill:{self}`
+#[derive(Copy, Clone, PartialEq)]
+pub enum Fill {
+    Color(Color),
+    None,
+}
+
+/// `stroke:{self}`
+#[derive(Copy, Clone, PartialEq)]
+pub enum Stroke {
+    Color(Color, f32),
+    None,
+}
+
+/// `fill:{fill};stroke:{stroke};fill-opacity:{opacity};`
+#[derive(Copy, Clone, PartialEq)]
+pub struct Style {
+    pub fill: Fill,
+    pub stroke: Stroke,
+    pub opacity: f32,
+}
+
+impl fmt::Display for Style {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{};{};fill-opacity:{};",
+            self.fill,
+            self.stroke,
+            self.opacity,
+        )
+    }
+}
+
+impl Style {
+    pub fn default() -> Self {
+        Style {
+            fill: Fill::Color(black()),
+            stroke: Stroke::None,
+            opacity: 1.0,
+        }
+    }
+}
+
+impl fmt::Display for Fill {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Fill::Color(color) => write!(f, "fill:{}", color),
+            Fill::None => write!(f, "fill:none"),
+        }
+    }
+}
+
+impl fmt::Display for Stroke {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Stroke::Color(color, radius) => write!(f, "stroke:{};stroke-width:{}", color, radius),
+            Stroke::None => write!(f, "stroke:none"),
+        }
+    }
+}
+
+impl Into<Fill> for Color {
+    fn into(self) -> Fill {
+        Fill::Color(self)
+    }
+}
+
+impl Into<Stroke> for Color {
+    fn into(self) -> Stroke {
+        Stroke::Color(self, 1.0)
+    }
+}
+
+/// `<rect x="{x}" y="{y}" width="{w}" height="{h}" ... />`,
+#[derive(Copy, Clone, PartialEq)]
+pub struct Rectangle {
+    pub x: f32,
+    pub y: f32,
+    pub w: f32,
+    pub h: f32,
+    pub style: Style,
+    pub border_radius: f32,
+}
+
+pub fn rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle {
+    Rectangle {
+        x, y, w, h,
+        style: Style::default(),
+        border_radius: 0.0,
+    }
+}
+
+impl Rectangle {
+    pub fn fill<F>(mut self, fill: F) -> Self
+    where F: Into<Fill> {
+        self.style.fill = fill.into();
+        self
+    }
+
+    pub fn stroke<S>(mut self, stroke: S) -> Self
+    where S: Into<Stroke> {
+        self.style.stroke = stroke.into();
+        self
+    }
+
+    pub fn opacity(mut self, opacity: f32) -> Self {
+        self.style.opacity = opacity;
+        self
+    }
+
+    pub fn style(mut self, style: Style) -> Self {
+        self.style = style;
+        self
+    }
+
+    pub fn border_radius(mut self, r: f32) -> Self {
+        self.border_radius = r;
+        self
+    }
+
+    pub fn offset(mut self, dx: f32, dy: f32) -> Self {
+        self.x += dx;
+        self.y += dy;
+        self
+    }
+
+    pub fn inflate(mut self, dx: f32, dy: f32) -> Self {
+        self.x -= dx;
+        self.y -= dy;
+        self.w += 2.0 * dx;
+        self.h += 2.0 * dy;
+        self
+    }
+}
+
+impl fmt::Display for Rectangle {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f,
+            r#"<rect x="{}" y="{}" width="{}" height="{}" ry="{}" style="{}" />""#,
+            self.x, self.y, self.w, self.h,
+            self.border_radius,
+            self.style,
+        )
+    }
+}
+
+/// `<circle cx="{x}" cy="{y}" r="{radius}" .../>`
+#[derive(Copy, Clone, PartialEq)]
+pub struct Circle {
+    pub x: f32,
+    pub y: f32,
+    pub radius: f32,
+    pub style: Style,
+}
+
+impl Circle {
+    pub fn fill<F>(mut self, fill: F) -> Self
+    where F: Into<Fill> {
+        self.style.fill = fill.into();
+        self
+    }
+
+    pub fn stroke<S>(mut self, stroke: S) -> Self
+    where S: Into<Stroke> {
+        self.style.stroke = stroke.into();
+        self
+    }
+
+    pub fn style(mut self, style: Style) -> Self {
+        self.style = style;
+        self
+    }
+
+    pub fn opacity(mut self, opacity: f32) -> Self {
+        self.style.opacity = opacity;
+        self
+    }
+
+
+    pub fn offset(mut self, dx: f32, dy: f32) -> Self {
+        self.x += dx;
+        self.y += dy;
+        self
+    }
+
+    pub fn inflate(mut self, by: f32) -> Self {
+        self.radius += by;
+        self
+    }
+}
+
+impl fmt::Display for Circle {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f,
+            r#"<circle cx="{}" cy="{}" r="{}" style="{}" />""#,
+            self.x, self.y, self.radius,
+            self.style,
+        )
+    }
+}
+
+/// `<path d="..." style="..."/>`
+#[derive(Clone, PartialEq)]
+pub struct Polygon {
+    pub points: Vec<[f32; 2]>,
+    pub closed: bool,
+    pub style: Style,
+}
+
+impl fmt::Display for Polygon {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, r#"<path d="#)?;
+        if self.points.len() > 0 {
+            write!(f, "M {} {} ", self.points[0][0], self.points[0][1])?;
+            for &p in &self.points[1..] {
+                write!(f, "L {} {} ", p[0], p[1])?;
+            }
+            if self.closed {
+                write!(f, "Z")?;
+            }
+        }
+        write!(f, r#"" style="{}"/>"#, self.style)
+    }
+}
+
+pub fn polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) ->  Polygon {
+    let mut points = Vec::with_capacity(pts.len());
+    for p in pts {
+        points.push((*p).into());
+    }
+    Polygon {
+        points,
+        closed: true,
+        style: Style::default(),
+    }
+}
+
+pub fn triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon {
+    polygon(&[[x1, y1], [x2, y2], [x3, y3]])
+}
+
+impl Polygon {
+    pub fn open(mut self) -> Self {
+        self.closed = false;
+        self
+    }
+
+    pub fn fill<F>(mut self, fill: F) -> Self
+    where F: Into<Fill> {
+        self.style.fill = fill.into();
+        self
+    }
+
+    pub fn stroke<S>(mut self, stroke: S) -> Self
+    where S: Into<Stroke> {
+        self.style.stroke = stroke.into();
+        self
+    }
+
+    pub fn opacity(mut self, opacity: f32) -> Self {
+        self.style.opacity = opacity;
+        self
+    }
+
+    pub fn style(mut self, style: Style) -> Self {
+        self.style = style;
+        self
+    }
+}
+
+/// `<path d="M {x1} {y1} L {x2} {y2}" ... />`
+#[derive(Copy, Clone, PartialEq)]
+pub struct LineSegment {
+    pub x1: f32,
+    pub x2: f32,
+    pub y1: f32,
+    pub y2: f32,
+    pub color: Color,
+    pub width: f32,
+}
+
+impl fmt::Display for LineSegment {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f,
+            r#"<path d="M {} {} L {} {}" style="stroke:{};stroke-width:{}"/>"#,
+            self.x1, self.y1,
+            self.x2, self.y2,
+            self.color,
+            self.width,
+        )
+    }
+}
+
+pub fn line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment {
+    LineSegment {
+        x1, y1, x2, y2,
+        color: black(),
+        width: 1.0,
+    }
+}
+
+impl LineSegment {
+    pub fn color(mut self, color: Color) -> Self {
+        self.color = color;
+        self
+    }
+
+    pub fn width(mut self, width: f32) -> Self {
+        self.width = width;
+        self
+    }
+
+    pub fn offset(mut self, dx: f32, dy: f32) -> Self {
+        self.x1 += dx;
+        self.y1 += dy;
+        self.x2 += dx;
+        self.y2 += dy;
+        self
+    }
+}
+
+/// `<path d="..." />`
+#[derive(Clone, PartialEq)]
+pub struct Path {
+    pub ops: Vec<PathOp>,
+    pub style: Style,
+}
+
+/// `M {} {} L {} {} ...`
+#[derive(Copy, Clone, PartialEq)]
+pub enum PathOp {
+    MoveTo { x: f32, y: f32 },
+    LineTo { x: f32, y: f32 },
+    QuadraticTo { ctrl_x: f32, ctrl_y: f32, x: f32, y: f32 },
+    CubicTo { ctrl1_x: f32, ctrl1_y: f32, ctrl2_x: f32, ctrl2_y: f32, x: f32, y: f32 },
+    Close,
+}
+impl fmt::Display for PathOp {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            PathOp::MoveTo { x, y } => write!(f, "M {} {} ", x, y),
+            PathOp::LineTo { x, y } => write!(f, "L {} {} ", x, y),
+            PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y } => write!(f, "Q {} {} {} {} ", ctrl_x, ctrl_y, x, y),
+            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),
+            PathOp::Close => write!(f, "Z "),
+        }
+    }
+}
+
+impl fmt::Display for Path {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, r#"<path d=""#)?;
+        for op in &self.ops {
+            op.fmt(f)?;
+        }
+        write!(f, r#"" style="{}" />"#, self.style)
+    }
+}
+
+impl Path {
+    pub fn move_to(mut self, x: f32, y: f32) -> Self {
+        self.ops.push(PathOp::MoveTo { x, y });
+        self
+    }
+
+    pub fn line_to(mut self, x: f32, y: f32) -> Self {
+        self.ops.push(PathOp::LineTo { x, y });
+        self
+    }
+
+    pub fn quadratic_bezier_to(
+        mut self,
+        ctrl_x: f32, ctrl_y: f32,
+        x: f32, y: f32,
+    ) -> Self {
+        self.ops.push(PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y });
+        self
+    }
+
+    pub fn cubic_bezier_to(
+        mut self,
+        ctrl1_x: f32, ctrl1_y: f32,
+        ctrl2_x: f32, ctrl2_y: f32,
+        x: f32, y: f32,
+    ) -> Self {
+        self.ops.push(PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y });
+        self
+    }
+
+    pub fn close(mut self) -> Self {
+        self.ops.push(PathOp::Close);
+        self
+    }
+
+    pub fn fill<F>(mut self, fill: F) -> Self
+    where F: Into<Fill> {
+        self.style.fill = fill.into();
+        self
+    }
+
+    pub fn stroke<S>(mut self, stroke: S) -> Self
+    where S: Into<Stroke> {
+        self.style.stroke = stroke.into();
+        self
+    }
+
+    pub fn opacity(mut self, opacity: f32) -> Self {
+        self.style.opacity = opacity;
+        self
+    }
+
+    pub fn style(mut self, style: Style) -> Self {
+        self.style = style;
+        self
+    }
+}
+
+pub fn path() -> Path {
+    Path {
+        ops: Vec::new(),
+        style: Style::default(),
+    }
+}
+
+/// `<text x="{x}" y="{y}" ... > {text} </text>`
+#[derive(Clone, PartialEq)]
+pub struct Text {
+    pub x: f32, pub y: f32,
+    pub text: String,
+    pub color: Color,
+    pub align: Align,
+    pub size: f32,
+}
+
+impl fmt::Display for Text {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f,
+            r#"<text x="{}" y="{}" style="font-size:{}px;fill:{};{}"> {} </text>"#,
+            self.x, self.y,
+            self.size,
+            self.color,
+            self.align,
+            self.text,
+        )
+    }
+}
+
+pub fn text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text {
+    Text {
+        x, y,
+        text: txt.into(),
+        color: black(),
+        align: Align::Left,
+        size: 10.0,
+    }
+}
+
+impl Text {
+    pub fn color(mut self, color: Color) -> Self {
+        self.color = color;
+        self
+    }
+
+    pub fn size(mut self, size: f32) -> Self {
+        self.size = size;
+        self
+    }
+
+    pub fn align(mut self, align: Align) -> Self {
+        self.align = align;
+        self
+    }
+
+    pub fn offset(mut self, dx: f32, dy: f32) -> Self {
+        self.x += dx;
+        self.y += dy;
+        self
+    }
+}
+
+/// `text-align:{self}`
+#[derive(Copy, Clone, PartialEq)]
+pub enum Align {
+    Left, Right, Center
+}
+
+impl fmt::Display for Align {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            Align::Left => write!(f, "text-anchor:start;text-align:left;"),
+            Align::Right => write!(f, "text-anchor:end;text-align:right;"),
+            Align::Center => write!(f, "text-anchor:middle;text-align:center;"),
+        }
+    }
+}
+
+/// `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {y}">`
+#[derive(Copy, Clone, PartialEq)]
+pub struct BeginSvg {
+    pub w: f32,
+    pub h: f32,
+}
+
+impl fmt::Display for BeginSvg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f,
+            r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {} {}">"#,
+            self.w,
+            self.h,
+        )
+    }
+}
+
+
+/// `</svg>`
+#[derive(Copy, Clone, PartialEq)]
+pub struct EndSvg;
+
+impl fmt::Display for EndSvg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "</svg>")
+    }
+}
+
+/// `"    "`
+pub struct Indentation {
+    pub n: u32,
+}
+
+pub fn indent(n: u32) -> Indentation {
+    Indentation { n }
+}
+
+impl Indentation {
+    pub fn push(&mut self) {
+        self.n += 1;
+    }
+
+    pub fn pop(&mut self) {
+        self.n -= 1;
+    }
+}
+
+impl fmt::Display for Indentation {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        for _ in 0..self.n {
+            write!(f, "    ")?;
+        }
+        Ok(())
+    }
+}
+
+#[test]
+fn foo() {
+    println!("{}", BeginSvg { w: 800.0, h: 600.0 });
+    println!("    {}",
+        rectangle(20.0, 50.0, 200.0, 100.0)
+            .fill(red())
+            .stroke(Stroke::Color(black(), 3.0))
+            .border_radius(5.0)
+    );
+    println!("    {}", text(25.0, 100.0, "Foo!").size(42.0).color(white()));
+    println!("{}", EndSvg);
+}