SVGo: The Go Language library for SVG generation


Table of Contents

Design
SVGo: graphic elements + style in functions
Hello, SVG
Using types
The Read/Parse/Draw Pattern
Using the Library
Sketching
Generative art
Drawing in a web server
Web Fonts
Adding behavior with scripting
IT Architecture tools
Pictures from Internet data
Library Reference
Structure, Scripting, Metadata, Transformation and Links
Shapes
Paths
Lines
Image and Text
Color
Gradients
Utility
Bibliography

The library is designed to closely follow the semantics of the standard, where that the developer/designer works directly with SVG objects (function names strive to correspond directly with SVG elements) applying standard CSS styles to the elements using Go functions as the logical unit of work. A design goal of the library is to produce standard, clean, readable code that is close to tuned, hand-crafted code that makes full use of a variety of SVG elements, without resorting to representing all objects as paths.

In many ways the API and environment is inspired by Processing [2], and during early development, Processing sketches were translated to use SVGo to test usability and API design decisions.



SVGo runs anywhere the Go language is supported, and is completely portable across these architectures and operating systems: x86-64, x86, and ARM, on FreeBSD, Linux, Mac OS X, and Windows) [3]. The library uses the Go standard libraries for formatting, I/O, operating system, XML, and strings with no other dependencies. Hosted on Github [4], SVGo is tracked on the Go Dashboard [5], and as of this writing, is ranked in the top 25 of the most installed packages of all time.

There are 49 public functions organized in these groups:

  • shapes, paths, and lines (14)

  • structure and metadata (10)

  • transformations (10)

  • images (1)

  • text (3)

  • color (2)

  • gradients (2)

  • grouping (2)

  • links (3)

  • utility (1)

  • scripting (1)

A typical function call in SVGo looks like this:

canvas.Rect(100, 200, 250, 125, "fill:none;stroke:red")

Where canvas refers to a structure which contains a Go io.Writer [6] that specifies the destination for the generated SVG. The io.Writer interface is designed so that the destination can be files, web servers, network connections, etc.

Rect is the SVG object, (in Go, functions beginning with an upper-case character are public), followed by a set of integer parameters (100, 200, 250, 125). The final optional parameter is a string in CSS [7] format that applies style to the object. The generated SVG for the call above is:

<rect x="100" y="200" width="250" height="125" style="fill:none;stroke:red"/>

If the final parameter is omitted, the SVG implementation's default style is applied. The optional parameter(s) may be a single string, which generates a single SVG style attribute, or Go variadic parameters---a series strings which represent name, value pairs which generate XML attribute names and values applied to the SVG element. For example:

canvas.Rect(100, 200, 250, 125, `id="redbox"`, `fill="red"`)

generates:

<rect x="100" y="200" width="250" height="125" id="redbox" fill="red"/>

This alternative form is useful if the standard API needs to augmented by adding attributes and values. It is often useful to apply a global style to a graphic elements, for example those generated in a loop:

canvas.Gstyle("fill:blue;stroke:red")
for x,y := 10,10; y < 500; y+=50 {
    canvas.Rect(x, y, 60, 40)
}
canvas.Gend()

Here is a complete Go program which generates SVG to the standard output file:

package main

import (
    "os"
    "github.com/ajstarks/svgo"
)
func main() {
    width := 500
    height := 500
    canvas := svg.New(os.Stdout)    
    canvas.Start(width, height)
    canvas.Rect(0, 0, width, height)
    canvas.Ellipse(width/2, height, width/2,height/3,"fill:rgb(44,77,232)")             
    canvas.Text(width/2, height/2, "Hello, World", "fill:white;font-size:48pt;text-anchor:middle")
    canvas.End()
}

The program begins with standard Go code to create a standalone application, and imports the SVGo library along with the Go standard operating system library, os. To install an external library on your system, use the goinstall program to fetch the library from a code repository with the command goinstall github.com/ajstarks/svgo. Once installed, you refer to the library with the same name.

In the main function, variables represent the SVG canvas' width and height, and the destination for the generated SVG. Next, the SVG document begins, and the first call of Rect creates a black rectangle (note the lack of style string), followed by a blue ellipse, and white text centered on the canvas. The program generates this SVG:

<?xml version="1.0"?>
<!-- Generated by SVGo -->
<svg width="500" height="500"
xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="500" height="500" />
<ellipse cx="250" cy="500" rx="250" ry="166" style="fill:rgb(44,77,232)"/>
<text x="250" y="250" style="fill:white;font-size:48pt;text-anchor:middle">Hello, World</text>
</svg>

The general philosophy of SVGo is to decompose pictures into components, applying style and transformations, treating SVG as the "assembly language" for graphics, with the SVGo program as the compiler.


A common pattern is to read data in XML or JSON, and produce a graphical representation in SVG. This pattern works for local files and is easily adapted for data fetched over the network from services that provide APIs like Flickr and Twitter.

Go's standard xml and json packages support unmarshaling input data into data structures which are then processed to make pictures. A typical program that uses this pattern:

Go's standard library includes a pseudo random number generator, which can be used to create variable visual effects. By loading a program in goplay, you can quickly try random iterations, and also tweak variables to alter the picture. For example, in the program below, you can alter the available colors, iterations, and maximum stroke width. Note how the program is structured: a single function, randarc embodies the picture, with its behavior controlled by its parameters and variables. In this case the picture is designed to scale with the overall SVG dimensions by tying the parameters to width and height. Also note how Go's Sprintf function is used dynamically create the CSS style.

package main

import (
    "os"
    "rand"
    "time"
    "fmt"
    "github.com/ajstarks/svgo"
)

const arcfmt = "stroke:%s;stroke-opacity:%.2f;stroke-width:%dpx;fill:none"
var colors = []string{"red", "green", "blue", "white", "gray"}

func randarc(canvas *svg.SVG, aw, ah, sw int, f1, f2 bool) {
    begin := rand.Intn(aw)
    arclength := rand.Intn(aw)
    end := begin + arclength
    baseline := ah / 2
    al := arclength / 2
    cl := len(colors)
    canvas.Arc(begin, baseline, al, al, 0, f1, f2, end, baseline,
        fmt.Sprintf("stroke:%s;stroke-opacity:%.2f;stroke-width:%dpx;fill:none",
            colors[rand.Intn(cl)], rand.Float64(), rand.Intn(sw)))
}

func main() {
    rand.Seed(time.Nanoseconds() % 1e9)

    width := 300
    height := 300
    aw := width / 2
    maxstroke := height / 10
    iterations := 50
    canvas := svg.New(os.Stdout)
    canvas.Start(width, height)
    canvas.Rect(0, 0, width, height)
    for i := 0; i < iterations; i++ {
        randarc(canvas, aw, height, maxstroke, false, true)
        randarc(canvas, aw, height, maxstroke, false, false)
    }
    canvas.End()
}

Even simple loops can be used to make interesting pictures. In this case the "tweakable" items are α and αi (controls the transparency), the text to be displayed ("s"), the rotation angle, θ with its interval θi

package main

import (
    "os"
    "fmt"
    "github.com/ajstarks/svgo"
)

func main() {
    width := 200
    height := 200
    α := 1.0
    αi := 0.03
    θi := 30.0
    h2 := height/2
    w2 := width/2

    canvas := svg.New(os.Stdout)
    canvas.Start(width, height)
    canvas.Rect(0, 0, width, height)
    canvas.Gstyle("font-family:serif;fill:white;font-size:72pt")
    canvas.Gtransform(fmt.Sprintf("translate(%d,%d)", w2, h2))

    for θ := 0.0; θ <= 360.0; θ += θi {
        canvas.Text(0, 0, "s", 
            fmt.Sprintf(`transform="rotate(%.3f)"`, θ),
            fmt.Sprintf(`fill-opacity="%.3f"`,α))
        α -= αi
    }
    canvas.Gend()
    canvas.Gend()
    canvas.End()
}

Here is "s", "v", and "g":

Here is the result of drawing the letter "i", and varying the angle: 10, 30, 45, 90 degrees

Other comparative examples are found at the FORM+CODE website [10].

The Go standard library includes the http package, which facilites writing web servers and applications, and its use of the io.Writer interface allows you to flexibly specify the destination of the generated SVG, so that it's as easy to serve SVG in a web server as it is writing to the screen.

This code generates colored shapes based on a URL. The main function creates two handlers which match /circle/ and /rect/ URLs, and then the program listens for requests. The corresponding circle and rect functions then create shapes based on a style passed in the URL. After running the program, opening: http://localhost:1958/circle/green displays a green circle in the web browser.

package main

import (
    "http"
    "log"
    "strings"
    "github.com/ajstarks/svgo"
)

const defaultstyle = "fill:rgb(127,0,0)"

func main() {
    http.Handle("/circle/", http.HandlerFunc(circle))
    http.Handle("/rect/", http.HandlerFunc(rect))
    err := http.ListenAndServe(1958, nil)
    if err != nil {
    log.Println("ListenAndServe:", err)
    }
}

func circle(w http.ResponseWriter, req *http.Request) {
  w.Header().Set("Content-Type", "image/svg+xml")
  s := svg.New(w)
  s.Start(500, 500)
  s.Title("Circle")
  s.Circle(250, 250, 125, shapestyle(req.URL.Path))
  s.End()
}

func rect(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "image/svg+xml")
    s := svg.New(w)
    s.Start(500, 500)
    s.Title("Rectangle")
    s.Rect(250, 250, 100, 200, shapestyle(req.URL.Path))
    s.End()
}

func shapestyle(path string) string {
    i := strings.LastIndex(path, "/") + 1
    if i > 0 && len(path[i:]) > 0 {
        return "fill:" + path[i:]
    }
    return defaultstyle
}

SVGo includes the Script function that applies scripting to graphics elements. The following program uses Go's composite literals to specify data for polygons, displays the polygons, and applies JavaScript to the graphics elements to make them draggable [12].

func main() {
    // HTML5 logo data from 
    // "Understanding and Optimizing Web Graphics", Session 508, 
    // Dean Jackson, Apple WWDC 2011
    //
    // Draggable elements via Jeff Schiller's dragsvg Javascript library

    // shield
    var sx = []int{71, 30, 481, 440, 255}
    var sy = []int{460, 0, 0, 460, 512}
    // highlight
    var hx = []int{256, 405, 440, 256}
    var hy = []int{472, 431, 37, 37}
    // "five"
    var fx = []int{181, 176, 392, 393, 396, 397, 114, 115, 129, 325, 318, 256, 192, 188, 132, 139, 256, 371, 372, 385, 387, 371}
    var fy = []int{208, 150, 150, 138, 109, 94, 94, 109, 265, 265, 338, 355, 338, 293, 293, 382, 414, 382, 372, 223, 208, 208}

    canvas := svg.New(os.Stdout)
    width := 512
    height := 512

    // begin the document with the onload event, and namespace for dragging
    canvas.Start(width, height, `onload="initializeDraggableElements();"`, `xmlns:drag="http://www.codedread.com/dragsvg"`)
    canvas.Rect(0, 0, width, height)                                               // black background
    canvas.Script("application/javascript", "http://www.codedread.com/dragsvg.js") // reference the drag script
    canvas.Polygon(sx, sy, `drag:enable="true"`, canvas.RGB(227, 79, 38))          // draggable shield
    canvas.Polygon(fx, fy, `drag:enable="true"`, canvas.RGB(219, 219, 219))        // draggable five
    canvas.Polygon(hx, hy, `drag:enable="true"`, canvas.RGBA(255, 255, 255, 0.3))  // draggable highlight
    canvas.End()
}


SVGo is used to create a set of visualization tools for use in IT architecture and related disciplines. The tools operate from the principle that precise and consistent graphical views are generated from standard data representations in XML. Separation of data and view allows for independent analysis of data and the generation of alternative and combined/composite views with little extra effort. In all cases the user is not concerned with the details of layout, style, the tools automatically handle these aspects.

In addition to a general-purpose grid-based layout diagramming tool described below, SVGo has been used to develop tools to visualize timelines, server configurations, proportional maps, technology stacks, portfolio views, and roadmaps.


All tools have a common command line structure, and generate SVG to standard output. The common practice is to manage collections of visualizations from a project directory, and generate the visualization sets via batch files.

The Internet is full of interesting data that you use to make pictures, and most services include an API to get at the data. Using the same pattern described above, the developer/designer can retrieve the data from the network and transform the data to interesting pictures.

Integer values are used for coordinates (x,y) and dimensions (w, h). Where applicable, a final variadic argument specifies the style to be applied to the object. The style strings follow the CSS standard; name:value pairs delimited by semicolons, or a series of name="value" pairs. For example: "fill:none; opacity:0.3" or fill="none" opacity="0.3"

The Offcolor type is used to specify the offset, color, and opacity of stop colors in linear and radial gradients:

type Offcolor struct {
    Offset  uint8
    Color   string
    Opacity float
}

New(w io.Writer) *SVG Constructor, Specify the output destination.

Start(w int, h int, attributes ...string) begin the SVG document with the width w and height h. Optionally add additional elements (such as additional namespaces or scripting events).

Startview(w, h, minx, miny, vw, vh int) begin the SVG document with the width w, height h, with a viewBox at minx, miny, vw, vh.

End() end the SVG document

Script(scriptype string, data ...string) Script defines a script with a specified type, (for example "application/javascript"). if the first variadic parameter is a link, use only the link reference. Otherwise, treat variadic arguments as the text of the script (marked up as CDATA). If no data is specified, simply close the script element.

Gstyle(s string) begin a group, with the specified style.

Gtransform(s string) begin a group, with the specified transform, end with Gend().

Translate(x, y int) begins coordinate translation to (x,y), end with Gend().


Scale(n float64) scales the coordinate system by n, end with Gend().


ScaleXY(x, y float64) scales the coordinate system by x, y. End with Gend().


SkewX(a float64) SkewX skews the x coordinate system by angle a, end with Gend().


SkewY(a float64) SkewY skews the y coordinate system by angle a, end with Gend().


SkewXY(ax, ay float64) SkewXY skews x and y coordinate systems by ax, ay respectively, end with Gend().


Rotate(r float64) rotates the coordinate system by n degrees, end with Gend().


TranslateRotate(x, y int, r float64) translates the coordinate system to (x,y), then rotates to r degrees, end with Gend().

RotateTranslate(x, y int, r float64) rotates the coordinate system r degrees, then translates to (x,y), end with Gend().

Gid(s string) begin a group, with the specified id.

Gend() end the group (must be paired with Gstyle, Gtransform, Gid).

Def() begin a definition block.

DefEnd() end a definition block.

Mask(string, x int, y int, w int, h int, s ...string) creates a mask with a specified id, dimension, and optional style.

MaskEnd() ends the Mask element.

Desc(s string) specify the text of the description.

Title(s string) specify the text of the title.

Link(href string, title string) begin a link named "href", with the specified title.

LinkEnd() end the link.

Use(x int, y int, link string, s ...string) place the object referenced at link at the location x, y.

Circle(x int, y int, r int, s ...string) draw a circle, centered at x,y with radius r.


Ellipse(x int, y int, w int, h int, s ...string) draw an ellipse, centered at x,y with radii w, and h.


Polygon(x []int, y []int, s ...string) draw a series of line segments using an array of x, y coordinates.


Rect(x int, y int, w int, h int, s ...string) draw a rectangle with upper left-hand corner at x,y, with width w, and height h.


CenterRect(x int, y int, w int, h int, s ...string) draw a rectangle with its center at x,y, with width w, and height h.


Roundrect(x int, y int, w int, h int, rx int, ry int, s ...string) draw a rounded rectangle with upper the left-hand corner at x,y, with width w, and height h. The radii for the rounded portion is specified by rx (width), and ry (height).


Square(x int, y int, s int, style ...string) draw a square with upper left corner at x,y with sides of length s.


Path(p string, s ...style) draw the arbitrary path as specified in p, according to the style specified in s.


Arc(sx int, sy int, ax int, ay int, r int, large bool, sweep bool, ex int, ey int, s ...string) draw an elliptical arc beginning coordinate at sx,sy, ending coordinate at ex, ey. The width and height of the arc are specified by ax, ay, the x axis rotation is r.

If sweep is true, then the arc will be drawn in a "positive-angle" direction (clockwise), if false, the arc is drawn counterclockwise.

if large is true, the arc sweep angle is greater than or equal to 180 degrees, otherwise the arc sweep is less than 180 degrees.


Bezier(sx int, sy int, cx int, cy int, px int, py int, ex int, ey int, s ...string) draw a cubic bezier curve, beginning at sx,sy, ending at ex,ey with control points at cx,cy and px,py.


Qbez(sx int, sy int, cx int, cy int, ex int, ey int, s...string) draws a quadratic bezier curve, with optional style beginning at sx,sy, ending at ex, sy, with the control point at cx, cy.


Line(x1 int, y1 int, x2 int, y2 int, s ...string) draw a line segment between x1,y1 and x2,y2.


Polyline(x []int, y []int, s ...string) draw a polygon using coordinates specified in x,y arrays.


RGB(r int, g int, b int) string creates a style string for the fill color designated by the (r)ed, g(reen), (b)lue components.

RGBA(r int, g int, b int, a float64) string as above, but includes the color's opacity as a value between 0.0 (fully transparent) and 1.0 (opaque).

LinearGradient(id string, x1, y1, x2, y2 uint8, sc []Offcolor) constructs a linear color gradient identified by id, along the vector defined by (x1,y1), and (x2,y2). The stop color sequence defined in sc. Coordinates are expressed as percentages.


RadialGradient(id string, cx, cy, r, fx, fy uint8, sc []Offcolor) constructs a radial color gradient identified by id, centered at (cx,cy), with a radius of r. (fx, fy) define the location of the focal point of the light source. The stop color sequence defined in sc. Coordinates are expressed as percentages.


[1] The Go Programming Language

[2] Processing

[3] Go supported architectures

[4] SVGo Github link

[5] Go Package Dashboard

[6] io.Writer

[7] Styling with CSS

[8] Slice type -- Go Programming Language Specification

[9] Processing Development Environment (PDE)

[10] Reas, Casey and McWilliams, Chandler, FORM+CODE in Design, Art, and Architecture - Code examples: Repeat: Embedded Iteration, Parameterize: Wave

[11] Google Web Fonts API

[12] Schiller, Jeff, Draggable SVG (Web)

[13] Batik toolkit, SVG Rasterizer

[14] See, Richard et al., SVG Scenarios using Microsoft Office Visio 2003

[15] Introducing JSON

[16] Coudal Partners' Layer Tennis Presented by Adobe Creative Suite | Tennis HQ.

[17] Layer Tennis Season Three, Match 2: Aaron Scamihorn vs Nick Campbel

[18] Layer Tennis Season 3 Recap

[19] Gallery of SVGo Examples

[20] Proportional Maps: an alternative to pie charts

[21] Layer Tennis Remix

[22] SVGo Reference Poster (SVG)