summaryrefslogtreecommitdiffstats
path: root/third_party/rust/svg_fmt/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/svg_fmt/src')
-rw-r--r--third_party/rust/svg_fmt/src/layout.rs43
-rw-r--r--third_party/rust/svg_fmt/src/lib.rs5
-rw-r--r--third_party/rust/svg_fmt/src/svg.rs624
3 files changed, 672 insertions, 0 deletions
diff --git a/third_party/rust/svg_fmt/src/layout.rs b/third_party/rust/svg_fmt/src/layout.rs
new file mode 100644
index 0000000000..7dd6bcf75c
--- /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;
+ }
+}
diff --git a/third_party/rust/svg_fmt/src/lib.rs b/third_party/rust/svg_fmt/src/lib.rs
new file mode 100644
index 0000000000..476adcab58
--- /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::*;
diff --git a/third_party/rust/svg_fmt/src/svg.rs b/third_party/rust/svg_fmt/src/svg.rs
new file mode 100644
index 0000000000..ff987d998e
--- /dev/null
+++ b/third_party/rust/svg_fmt/src/svg.rs
@@ -0,0 +1,624 @@
+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,
+ pub stroke_opacity: f32,
+}
+
+impl fmt::Display for Style {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{};{};fill-opacity:{};stroke-opacity:{};",
+ self.fill,
+ self.stroke,
+ self.opacity,
+ self.stroke_opacity,
+ )
+ }
+}
+
+impl Style {
+ pub fn default() -> Self {
+ Style {
+ fill: Fill::Color(black()),
+ stroke: Stroke::None,
+ opacity: 1.0,
+ stroke_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 stroke_opacity(mut self, opacity: f32) -> Self {
+ self.style.stroke_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 stroke_opacity(mut self, opacity: f32) -> Self {
+ self.style.stroke_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 stroke_opacity(mut self, opacity: f32) -> Self {
+ self.style.stroke_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 stroke_opacity(mut self, opacity: f32) -> Self {
+ self.style.stroke_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
+ }
+}
+
+pub struct Comment {
+ pub text: String,
+}
+
+pub fn comment<T: Into<String>>(text: T) -> Comment {
+ Comment {
+ text: text.into()
+ }
+}
+
+impl fmt::Display for Comment {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<!-- {} -->", self.text)
+ }
+}
+
+/// `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);
+}