-
-
Notifications
You must be signed in to change notification settings - Fork 103
Canvas & Context
The library specifies two main structures for handling drawing. One is Canvas
and the other is Context
. We can think of the first as the drawing target and the second as the drawer. That is, Context
will keep a context of drawing parameters and a set of functions to perform drawing operations and keep a state (such as color, position, ...). It will draw to a Renderer
interface, which is implemented by Canvas
but also by the individual renderers (rasterizer, PDF, EPS, ...). That means we can draw directly to any given renderer, including Canvas
which is really just an array of records of previous drawing commands. From Canvas
we can draw to any other renderer any number of times by reproducing the drawing commands.
Typical usage of a canvas is to accumulate drawing commands and to redirect those commands to another renderer. The commands are accumulated in a stack, adding layers for every call to the implemented functions for the Renderer
interface (see Renderers).
// Create a new canvas with width and height in millimeters
c := canvas.New(width, height)
// ... drawing operations
// Set the z-index of the drawing operations to change the drawing order
c.SetZIndex(-10)
// ... drawing operations
// Change the canvas' size to fit all drawn objects with a 5 millimeter margin
c.Fit(5.0)
// Write to multiple renderers: a PNG renderer at 96 DPI and an SVG renderer
renderers.Write("output.png", c, canvas.DPI(96.0))
renderers.Write("output.svg", c)
// Or to specify additional renderer settings
renderers.Write("output.pdf", c, &pdf.Options{
Compress: false,
})
Besides implementing the Renderer
interface as well, it provides functions for path construction too (see Paths). The context keeps a rendering state, including draw style (fill color, stroke color, stroke width, stroke capper, stroke joiner, dash dashes, fill rule), a view, and a coordinate view. We can change the drawing style using the following functions:
SetFill(ipaint interface{}) // accepts color, gradient, or pattern
SetFillColor(col color.Color)
SetFillGradient(gradient Gradient)
SetFillPattern(pattern Pattern)
SetStroke(ipaint interface{}) // accepts color, gradient, or pattern
SetStrokeColor(col color.Color)
SetStrokeGradient(gradient Gradient)
SetStrokePattern(pattern Pattern)
SetStrokeWidth(width float64)
SetStrokeCapper(capper Capper)
SetStrokeJoiner(joiner Joiner)
SetDashes(offset float64, dashes ...float64)
SetFillRule(rule FillRule)
Push() // push the style on the stack
Pop() // pop a style off the stack
ResetStyle()
There are two types of transformations that happen for each drawing action: the view transformation and the coordinate view transformation. As the name suggests, the latter transforms only the coordinate at which we will be drawing a path/text/image, and will not actually transform the path/text/image itself. This is useful for creating subregions in your canvas in different coordinate systems. In contrast, the view transformation will transform the coordinate and the object itself. We can change these transformations using the following functions:
SetCoordRect(rect Rect, width, height float64)
SetCoordSystem(coordSystem CoordSystem) // e.g. CartesianI or CartesianIV
SetCoordView(coordView Matrix)
SetView(view Matrix)
ComposeView(view Matrix)
Translate(x, y float64)
Rotate(rot float64)
RotateAbout(rot, x, y float64)
Scale(sx, sy float64)
ScaleAbout(sx, sy, x, y float64)
ReflectX()
ReflectXAbout(x float64)
ReflectY()
ReflectYAbout(y float64)
ResetView()
Furthermore, for path construction the following functions are provided:
MoveTo(x, y float64)
LineTo(x, y float64)
QuadTo(cpx, cpy, x, y float64)
CubeTo(cpx1, cpy1, cpx2, cpy2, x, y float64)
ArcTo(rx, ry, rot float64, large, sweep bool, x, y float64)
Arc(rx, ry, rot, theta0, theta1 float64)
Close()
Fill()
FillStroke()
Stroke()
Finally, to draw path/text/image objects, we can use:
DrawPath(x, y float64, paths ...*Path)
DrawText(x, y float64, texts ...*Text)
DrawImage(x, y float64, img image.Image, resolution Resolution)
FitImage(img image.Image, rect Rect, fit ImageFit) // draw image in rect
f, err := os.Create("output.pdf")
if err != nil {
panic(err)
}
defer f.Close()
// Create a PDF renderer
pdf := pdf.New(f, 100, 100)
defer pdf.Close()
// Setup a context around the renderer
ctx := canvas.NewContext(pdf)
// Set style
ctx.SetFillColor(canvas.Lightblue)
ctx.SetStrokeColor(canvas.Blue)
ctx.SetStrokeWidth(0.5)
ctx.SetDashes(0.0, 2.0, 1.0) // dash of 2mm and gap of 1mm, starting at offset 0mm in the path
// Construct path
ctx.MoveTo(10, 50)
ctx.LineTo(30, 80)
ctx.QuadTo(60, 80, 90, 50)
ctx.Close()
// Fill and stroke the path
ctx.FillStroke()
// Draw an image
ctx.DrawImage(30, 10, img, canvas.DPI(96.0))