2D rendering for different output (raster, pdf, svg)

draw2d

Coverage GoDoc BuyMeaBeer

Package draw2d is a go 2D vector graphics library with support for multiple outputs such as images (draw2d), pdf documents (draw2dpdf), opengl (draw2dgl) and svg (draw2dsvg). There's also a Postscript reader that uses draw2d. draw2d is released under the BSD license. See the documentation for more details.

geometrypostscript

Click on an image above to get the pdf, generated with exactly the same draw2d code. The first image is the output of samples/geometry. The second image is the result of samples/postcript, which demonstrates that draw2d can draw postscript files into images or pdf documents with the ps package.

Features

Operations in draw2d include stroking and filling polygons, arcs, Bézier curves, drawing images and text rendering with truetype fonts. All drawing operations can be transformed by affine transformations (scale, rotation, translation).

Package draw2d follows the conventions of the HTML Canvas 2D Context for coordinate system, angles, etc...

Installation

Install golang. To install or update the package draw2d on your system, run:

Stable release

go get -u gopkg.in/llgcode/draw2d.v1

or Current release

go get -u github.com/llgcode/draw2d

Quick Start

The following Go code generates a simple drawing and saves it to an image file with package draw2d:

package main

import (
	"github.com/llgcode/draw2d/draw2dimg"
	"image"
	"image/color"
)

func main() {
	// Initialize the graphic context on an RGBA image
	dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
	gc := draw2dimg.NewGraphicContext(dest)

	// Set some properties
	gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
	gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
	gc.SetLineWidth(5)

	// Draw a closed shape
	gc.BeginPath() // Initialize a new path
	gc.MoveTo(10, 10) // Move to a position to start the new path
	gc.LineTo(100, 50)
	gc.QuadCurveTo(100, 10, 10, 10)
	gc.Close()
	gc.FillStroke()

	// Save to file
	draw2dimg.SaveToPngFile("hello.png", dest)
}

The same Go code can also generate a pdf document with package draw2dpdf:

package main

import (
	"github.com/llgcode/draw2d/draw2dpdf"
	"image/color"
)

func main() {
	// Initialize the graphic context on an RGBA image
	dest := draw2dpdf.NewPdf("L", "mm", "A4")
	gc := draw2dpdf.NewGraphicContext(dest)

	// Set some properties
	gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
	gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
	gc.SetLineWidth(5)

	// Draw a closed shape
	gc.MoveTo(10, 10) // should always be called first for a new path
	gc.LineTo(100, 50)
	gc.QuadCurveTo(100, 10, 10, 10)
	gc.Close()
	gc.FillStroke()

	// Save to file
	draw2dpdf.SaveToPdfFile("hello.pdf", dest)
}

There are more examples here: https://github.com/llgcode/draw2d/tree/master/samples

Drawing on opengl is provided by the draw2dgl package.

Testing

The samples are run as tests from the root package folder draw2d by:

go test ./...

Or if you want to run with test coverage:

go test -cover ./... | grep -v "no test"

This will generate output by the different backends in the output folder.

Acknowledgments

Laurent Le Goff wrote this library, inspired by Postscript and HTML5 canvas. He implemented the image and opengl backend with the freetype-go package. Also he created a pure go Postscript interpreter, which can read postscript images and draw to a draw2d graphic context. Stani Michiels implemented the pdf backend with the gofpdf package.

Packages using draw2d

  • ps: Postscript interpreter written in Go
  • gonum/plot: drawing plots in Go
  • go.uik: a concurrent UI kit written in pure go.
  • smartcrop: content aware image cropping
  • karta: drawing Voronoi diagrams
  • chart: basic charts in Go
  • hilbert: package for drawing Hilbert curves

References

Owner
llgcode
Software Engineer
llgcode
Comments
  • Thread safety

    Thread safety

    I am using draw2d inside a webservers HTTP handler getting crashes due to thread safety issues inside the font cache:

    `fatal error: concurrent map read and map write

    goroutine 27939 [running]: runtime.throw(0xb0976d, 0x21) /usr/local/go/src/runtime/panic.go:566 +0x95 fp=0xc468f1cdd0 sp=0xc468f1cdb0 runtime.mapaccess1_fast32(0xa5f9a0, 0xc467ed10b0, 0xc400000034, 0x29) /usr/local/go/src/runtime/hashmap_fast.go:21 +0x1a4 fp=0xc468f1cdf8 sp=0xc468f1cdd0 github.com/llgcode/draw2d/draw2dbase.FetchGlyph(0xfc8420, 0xc464f8b270, 0xc47561b8c0, 0x29, 0x3fd2000000000034, 0xc022900000000000) /home/chp/project/src/github.com/llgcode/draw2d/draw2dbase/text.go:16 +0xd4 fp=0xc468f1ce88 sp=0xc468f1cdf8 github.com/llgcode/draw2d/draw2dimg.(*GraphicContext).StrokeStringAt(0xc464f8b270, 0xc47ef57048, 0x1, 0x40564d8000000000, 0x40542a0000000000, 0x401f700000000000) /home/chp/project/src/github.com/llgcode/draw2d/draw2dimg/ftgc.go:166 +0x1c1 fp=0xc468f1cf10 sp=0xc468f1ce88 main.MapRender(0x1d5b7, 0xc46351420d, 0x5, 0x100, 0x100, 0x404a44a4985365c0, 0x402ab80000000000, 0x404a465a79921640, 0x402ac34000000000, 0xc463514258, ...) /home/chp/project/src/project/maprender.go:1256 +0x3790 fp=0xc468f1d860 sp=0xc468f1cf10 main.(*MapRenderHandler).ServeHTTP(0xff8868, 0xfc1760, 0xc47561b740, 0xc4635143c0) /home/chp/project/src/project/web.go:376 +0x10e0 fp=0xc468f1db90 sp=0xc468f1d860 `

    I already did a fork and fixed the issue, but this not fork/pull-request compatible due to the self-referential imports: https://github.com/ChristophPech/draw2d

  • Default font cache

    Default font cache

    In font.go, the default font cache stored in the global variable fontCache is not synchronized. Consequently, any program performing concurrent drawings in separate goroutines is exposed to potential data races. Plus the program is exposed to future crashes with new versions of the golang compiler, as the golang spec doesn't guarantee how maps should behave in case of concurrent reads and writes.

    This may be resolved by documenting it (i.e., pushing the need for synchronization onto the library client) or using a sync.Mutex in font.go.

  • How to use on Google App Engine?

    How to use on Google App Engine?

    How to use on Google App Engine?
    
    I tried, and it won't compile.
    

    Original issue reported on code.google.com by [email protected] on 4 Mar 2012 at 2:45

  • Move generic text functions to draw2dbase

    Move generic text functions to draw2dbase

    This change moves all the draw2dimg text methods to draw2dbase and integrates them into StackGraphicContext. This reduces repeated code from draw2dimg and draw2dgl. This should be helpful for adding caches too.

  • PDF GraphicContext

    PDF GraphicContext

    PDF GraphicContext using this project https://bitbucket.org/zombiezen/gopdf/
    
    

    Original issue reported on code.google.com by [email protected] on 16 Apr 2012 at 7:44

  • Alpha fills seem broken

    Alpha fills seem broken

    Hi all,

    trying to use this library for some basic drawing but alpha fills seem broken, with entirely unexpected results. using the base2dimg backend (haven't tested with others)

    the following code produces the following results

    package main
    
    import (
    	"image"
    	"image/color"
    
    	"github.com/llgcode/draw2d/draw2dimg"
    	"github.com/llgcode/draw2d/draw2dkit"
    )
    
    func main() {
    	darkBlue := color.RGBA{0x48, 0x4d, 0x61, 0xff}
    	alphaOrange := color.RGBA{0xce, 0x82, 0x3f, 0x1f}
    
    	canvas := image.NewRGBA(image.Rect(0, 0, 256, 256))
    	gc := draw2dimg.NewGraphicContext(canvas)
    
    	// draw background 
    	gc.SetFillColor(darkBlue)
    	draw2dkit.Rectangle(gc, 0, 0, 256, 256)
    	gc.Fill()
    
    	gc.SetFillColor(alphaOrange)
    	gc.SetStrokeColor(alphaOrange)
    
    	// layer a transparent orange rectangle (filled)
    	draw2dkit.Rectangle(gc, 64, 64, 128, 128)
    	gc.Fill()
    
    	// layer a transparent orange rectangle (stroked)
    	draw2dkit.Rectangle(gc, 128, 128, 192, 192)
    	gc.Stroke()
    
    	draw2dimg.SaveToPngFile("alpha-test.png", canvas)
    }
    

    alpha-test

    the green colour is entirely unexpected, stroke draws the correct colour but fill does its own thing

  • SubdivideQuad and TraceQuad index out of range errors

    SubdivideQuad and TraceQuad index out of range errors

    I didn't look for the root cause but the following workaround avoided the issue for me:

    *** curve.go.org	2017-07-05 10:42:20.266078941 +0200
    --- curve.go.patched	2017-07-05 10:42:26.770106185 +0200
    ***************
    *** 86,91 ****
    --- 86,94 ----
      // c1 and c2 parameters are the resulting curves
      func SubdivideQuad(c, c1, c2 []float64) {
      	// First point of c is the first point of c1
    + 	if len(c1) < 2 {
    + 		return
    + 	}
      	c1[0], c1[1] = c[0], c[1]
      	// Last point of c is the last point of c2
      	c2[4], c2[5] = c[4], c[5]
    ***************
    *** 114,119 ****
    --- 117,125 ----
      
      	for i >= 0 {
      		c = curves[i*6:]
    + 		if len(c) < 6 {
    + 			return
    + 		}
      		dx = c[4] - c[0]
      		dy = c[5] - c[1]
    
    
  • Move/Shift Entire Path

    Move/Shift Entire Path

    A "Path.Shift" method would be very useful. This method would shift every point on the path by an x modifier and a y modifier. I'd imagine it'd look like this:

    // Shift moves every point in the path by x and y.
    func (p *Path) Shift(x, y float64) {
        if len(p.Points) % 2 != 0 {
            panic("Invalid Path (odd number of points)")
        }
        for i := 0;i < len(p.Points);i += 2 {
            p.Points[i] += x
            p.Points[i+1] += y
        }
    }
    

    This would make it easy to save paths and move/reuse them later instead of rebuilding them all the time.

  • draw2dimg error when origin not at (0,0)

    draw2dimg error when origin not at (0,0)

    Changing:

            dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))                                                                                                                                                                                                                                                                   
    

    ...to:

            dest := image.NewRGBA(image.Rect(0, 50, 297, 210.0))                                                                                                                                                                                                                                                                   
    

    ...causes the android logo's legs to be corrupted: http://oi59.tinypic.com/2s0lsfs.jpg

    See gist for code:

    https://gist.github.com/stephenwithav/a4b995cdda566c6a8cbc

  • Bounding box ingores kerning

    Bounding box ingores kerning

    a recent update in bounding box computation made its width reduced: it looks like the old behaviour was more correct since only the antialias pixels are outside, see the attached picture

    drawtextdiff

    working bb 13548be874707c45a83f9c2c282d76d671c7acf8 (right) tested bb 0d961cd2997a2d0c1c91645ab5e552bf60d4b49c (left)

  • SVG support

    SVG support

    I saw some discussion loosely related to SVG in #74 and #79, but am currently not seeing any other mentions of SVG in the README, issues or wiki.

    Is there currently any plans to support SVG output? I noticed a couple of people in the linked issues talking about starting work on it, have any of these made any progress?

    SVG output feels like a very natural fit for this library.

  • Support EPS (encapsulated postscript) output

    Support EPS (encapsulated postscript) output

    It would likely not be much more effort given that pdf output is already supported.

    Even just regular postscript output would be very useful and even simpler.

  • SVG file is enormous

    SVG file is enormous

    I have been using this library with go-nexrad, and I am able to generate PNG and SVG files. However, the SVG files that are generated are enormous, with a less than 10MB PNG file becoming a 28MB SVG file with the same options. Here is the code:

    func render(out string, radials []*archive2.Message31, label string) {
    
    	width := float64(imageSize)
    	height := float64(imageSize)
    
    	SVGcanvas := draw2dsvg.NewSvg()
    	SVGcanvas.Width = strconv.Itoa(int(width)) + "px"
    	SVGcanvas.Height = strconv.Itoa(int(width)) + "px"
    	//fmt.Println(canvas.Width)
    	//fmt.Println(canvas.Height)
    	//draw.Draw(canvas, canvas.Bounds(), image.Black, image.ZP, draw.Src)
    
    	SVGgc := draw2dsvg.NewGraphicContext(SVGcanvas)
    
    	xc := width / 2
    	yc := height / 2
    	pxPerKm := width / 2 / 460
    	firstGatePx := float64(radials[0].ReflectivityData.DataMomentRange) / 1000 * pxPerKm
    	gateIntervalKm := float64(radials[0].ReflectivityData.DataMomentRangeSampleInterval) / 1000
    	gateWidthPx := gateIntervalKm * pxPerKm
    
    	t := time.Now()
    	log.Println("rendering radials")
    	// valueDist := map[float32]int{}
    
    	for _, radial := range radials {
    		// round to the nearest rounded azimuth for the given resolution.
    		// ex: for radial 20.5432, round to 20.5
    		azimuthAngle := float64(radial.Header.AzimuthAngle) - 90
    		if azimuthAngle < 0 {
    			azimuthAngle = 360.0 + azimuthAngle
    		}
    		azimuthSpacing := radial.Header.AzimuthResolutionSpacing()
    		azimuth := math.Floor(azimuthAngle)
    		if math.Floor(azimuthAngle+azimuthSpacing) > azimuth {
    			azimuth += azimuthSpacing
    		}
    		startAngle := azimuth * (math.Pi / 180.0)      /* angles are specified */
    		endAngle := azimuthSpacing * (math.Pi / 180.0) /* clockwise in radians           */
    
    		// start drawing gates from the start of the first gate
    		distanceX, distanceY := firstGatePx, firstGatePx
    		SVGgc.SetLineWidth(gateWidthPx + 1)
    		SVGgc.SetLineCap(draw2d.ButtCap)
    
    		var gates []float32
    		switch product {
    		case "vel":
    			gates = radial.VelocityData.ScaledData()
    		case "sw":
    			gates = radial.SwData.ScaledData()
    		case "rho":
    			gates = radial.RhoData.ScaledData()
    		default:
    			gates = radial.ReflectivityData.ScaledData()
    		}
    
    		numGates := len(gates)
    		for i, v := range gates {
    			if v != archive2.MomentDataBelowThreshold {
    
    				//fmt.Println(gateWidthPx)
    				if i == 0 {
    					SVGgc.SetLineWidth(0)
    				} else if i > 0 {
    					SVGgc.SetLineWidth(gateWidthPx + 1)
    				}
    
    				// valueDist[v] += 1
    
    				SVGgc.MoveTo(xc+math.Cos(startAngle)*distanceX, yc+math.Sin(startAngle)*distanceY)
    
    				// make the gates connect visually by extending arcs so there is no space between adjacent gates.
    				if i == 0 {
    					SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle-.001, endAngle+.001)
    				} else if i == numGates-1 {
    					SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle)
    				} else {
    					SVGgc.ArcTo(xc, yc, distanceX, distanceY, startAngle, endAngle+.001)
    				}
    
    				SVGgc.SetStrokeColor(colorSchemes[product][colorScheme](v))
    				SVGgc.Stroke()
    			}
    
    			distanceX += gateWidthPx
    			distanceY += gateWidthPx
    			azimuth += radial.Header.AzimuthResolutionSpacing()
    		}
    	}
    
    	// Save to file
    	draw2dsvg.SaveToSvgFile(out, SVGcanvas)
    	fmt.Println("Finished in", time.Since(t))
    }
    

    The full file can be found in my fork of the project here.

    The reason I think the SVG is so large is because it is generating the file very inefficiently, possibly by trying to render every pixel instead of just a start and end point. I have tried setting the DPI with SVGgc.setDPI(), but that hasn't worked.

    If you have any idea about why the file is so large, or any idea of how to fix it, I would greatly appreciate your input. Hopefully you won't have to go through the entire go-nexrad project to understand this, I have included the code block that I am almost certain is causing the issue, and is the part that uses your library.

    If you would like a screen recording of me generating the file and showing the file size with both PNG and SVG, please let me know, if you have difficulty building the project and replicating the issue yourself, if that is needed.

  • Text Stroke LineCap and LineJoin

    Text Stroke LineCap and LineJoin

    Hello author and everyone,

    When I draw a text string with stroke (using StrokeStringAt), font Roboto-Medium (loaded with truetype package), found the stroke looks strange which does not cover the text, see image below:

    image

    In the image, there is an "i" and a "t" which has strange stroke, it does not fully "connected". I am expecting the stroke to cover the text, should looks connected.

    I tried SetLineCap but looks like it doesn't work, and found there is an issue on that: https://github.com/llgcode/draw2d/issues/155. How do I correctly make it stroke the text?

    Regards, Phuc Ta

  • Draw line using a gradient color

    Draw line using a gradient color

    Hello,

    I am using draw2d to draw some graphics using go and it's going great. Thank you for the library! I have a usecase where I would need the line I am drawing to use a gradient color instead of a solid color. As an example the line could start green and end red. Can this be done? I've been looking around for a way to achieve this but so far I have come up short. Thank you in advance for any answer.

  • support reading of svg format

    support reading of svg format

    It would be nice to be able to read / parse svg format.

    Sorry if this is already capture elsewhere in this repo, but this could also be a good place to organize some thoughts about this. For example:

    1. Is it theoretically possible now?
    2. If not, why not?
    3. What would have to happen to support it?
Rasterx is an SVG 2.0 path compliant rasterizer that can use either the golang vector or a derivative of the freetype anti-aliaser.
Rasterx is an SVG 2.0 path compliant rasterizer that can use either the golang vector or a derivative of the freetype anti-aliaser.

rasterx Rasterx is a golang rasterizer that implements path stroking functions capable of SVG 2.0 compliant 'arc' joins and explicit loop closing. Pat

Nov 1, 2022
Go Language Library for SVG generation
Go Language Library for SVG generation

SVGo: A Go library for SVG generation The library generates SVG as defined by the Scalable Vector Graphics 1.1 Specification (http://www.w3.org/TR/SVG

Jan 6, 2023
gensvg generates SVG to an io.Writer
gensvg generates SVG to an io.Writer

gensvg: A Go library for SVG generation The library generates SVG as defined by the Scalable Vector Graphics 1.1 Specification

Dec 28, 2022
A simple API written in Go that creates badges in SVG format, based on the requested route.

A simple API written in Go that creates badges in SVG format, based on the requested route. Those graphics can be used to style README.md files, or to add tags to webpages.

Jul 2, 2021
Very simple SVG to PNG converter library using the Inkscape.

svg2png Description Very simple SVG to PNG converter library using the Inkscape.

Jan 11, 2022
Snippit - Creates syntax-highlighted code snippets in png or svg format
Snippit - Creates syntax-highlighted code snippets in png or svg format

snippit creates syntax-highlighted code snippets in png or svg format. Installat

Oct 10, 2022
Go-binsize-treemap - Go binary size SVG treemap

?? Go binary size SVG treemap Make treemap breakdown of Go executable binary $ g

Dec 21, 2022
Image processing library and rendering toolkit for Go.

blend Image processing library and rendering toolkit for Go. (WIP) Installation: This library is compatible with Go1. go get github.com/phrozen/blend

Nov 11, 2022
Go Graphics - 2D rendering in Go with a simple API.
Go Graphics - 2D rendering in Go with a simple API.

Go Graphics gg is a library for rendering 2D graphics in pure Go. Installation go get -u github.com/fogleman/gg Alternatively, you may use gopkg.in t

Dec 29, 2022
Canvas is a Go drawing library based on OpenGL or using software rendering that is very similar to the HTML5 canvas API
Canvas is a Go drawing library based on OpenGL or using software rendering that is very similar to the HTML5 canvas API

Go canvas Canvas is a pure Go library that provides drawing functionality as similar as possible to the HTML5 canvas API. It has nothing to do with HT

Jan 3, 2023
An experiment in rendering images with Slack custom emojis.
An experiment in rendering images with Slack custom emojis.

emojimage An experiment in rendering images with Slack custom emojis. Example Usage 1. Initializing your workspace First, you'll need to upload 1,332

Mar 12, 2022
Draw graphs through GO MOD GRAPH output
Draw graphs through GO MOD GRAPH output

go-mod-graph-chart build chart by go mod graph output Install $ go get -u github.com/PaulXu-cn/go-mod-graph-chart/gmchart Usage

Dec 14, 2022
a tool to output images as RGB ANSI graphics on the terminal
a tool to output images as RGB ANSI graphics on the terminal

imgcat Tool to output images in the terminal. Built with bubbletea install homebrew brew install trashhalo/homebrew-brews/imgcat prebuilt packages Pr

Dec 28, 2022
A command to output longified ascii art.

longify A command to output longified ascii art. Inspired by Tweet from @sheepla: https://twitter.com/Sheeeeepla/status/1522199846870196225 Installati

Sep 12, 2022
Cairo in Go: vector to SVG, PDF, EPS, raster, HTML Canvas, etc.
Cairo in Go: vector to SVG, PDF, EPS, raster, HTML Canvas, etc.

Canvas is a common vector drawing target that can output SVG, PDF, EPS, raster images (PNG, JPG, GIF, ...), HTML Canvas through WASM, and OpenGL. It h

Dec 25, 2022
goldmark-pdf is a renderer for goldmark that allows rendering to PDF.
goldmark-pdf is a renderer for goldmark that allows rendering to PDF.

A PDF renderer for the goldmark markdown parser.

Dec 27, 2022
🎨 Terminal color rendering library, support 8/16 colors, 256 colors, RGB color rendering output, support Print/Sprintf methods, compatible with Windows.
🎨 Terminal color rendering library, support 8/16 colors, 256 colors, RGB color rendering output, support Print/Sprintf methods, compatible with Windows.

?? Terminal color rendering library, support 8/16 colors, 256 colors, RGB color rendering output, support Print/Sprintf methods, compatible with Windows. GO CLI 控制台颜色渲染工具库,支持16色,256色,RGB色彩渲染输出,使用类似于 Print/Sprintf,兼容并支持 Windows 环境的色彩渲染

Dec 30, 2022
A minimalist Go PDF writer in 1982 lines. Draws text, images and shapes. Helps understand the PDF format. Used in production for reports.
A minimalist Go PDF writer in 1982 lines. Draws text, images and shapes. Helps understand the PDF format. Used in production for reports.

one-file-pdf - A minimalist PDF generator in <2K lines and 1 file The main idea behind this project was: "How small can I make a PDF generator for it

Dec 11, 2022
Convert scanned image PDF file to text annotated PDF file
Convert scanned image PDF file to text annotated PDF file

Jisui (自炊) This tool is PoC (Proof of Concept). Jisui is a helper tool to create e-book. Ordinary the scanned book have not text information, so you c

Dec 11, 2022