Search code examples
imagegofontsdraw

How to add a simple text label to an image in Go?


Given image.RGBA, coordinates, and a line of text, how do I add a simple label with any plain fixed font? E.g. Face7x13 from font/basicfont.

package main

import (
    "image"
    "image/color"
    "image/png"
    "os"
)

func main() {
    img := image.NewRGBA(image.Rect(0, 0, 320, 240))
    x, y := 100, 100
    addLabel(img, x, y, "Test123")
    png.Encode(os.Stdout, img)
}

func addLabel(img *image.RGBA, x, y int, label string) {
     col := color.Black
     // now what?
}

Alignment doesn't really matter, but best if I could write the label above a line which starts at the coordinates.

And I would like to avoid external loadable dependencies like fonts.


Solution

  • The golang.org/x/image/font package just defines interfaces for font faces and drawing text on images.

    You may use the Go implementation of Freetype font rasterizer: github.com/golang/freetype.

    The key type is freetype.Context, it has all the methods you need.

    For a complete example, check out this file: example/freetype/main.go. This example loads a font file, creates and configures freetype.Context, draws text on image and saves the result image to file.

    Let's assume you already have the font file loaded, and a c context configured (see the example how to do that). Then your addLabel() function could look like this:

    func addLabel(img *image.RGBA, x, y int, label string) {
        c.SetDst(img)
        size := 12.0 // font size in pixels
        pt := freetype.Pt(x, y+int(c.PointToFixed(size)>>6))
    
        if _, err := c.DrawString(label, pt); err != nil {
            // handle error
        }
    }
    

    If you don't want to hassle with the freetype package and external font files, the font/basicfont package contains a basic font named Face7x13 whose graphical data is entirely self-contained. This is how you could use that:

    import (
        "golang.org/x/image/font"
        "golang.org/x/image/font/basicfont"
        "golang.org/x/image/math/fixed"
        "image"
        "image/color"
    )
    
    func addLabel(img *image.RGBA, x, y int, label string) {
        col := color.RGBA{200, 100, 0, 255}
        point := fixed.Point26_6{fixed.I(x), fixed.I(y)}
    
        d := &font.Drawer{
            Dst:  img,
            Src:  image.NewUniform(col),
            Face: basicfont.Face7x13,
            Dot:  point,
        }
        d.DrawString(label)
    }
    

    This is how this addLabel() function can be used: the code below creates a new image, draws the "Hello Go" text on it and saves it in a file named hello-go.png:

    func main() {
        img := image.NewRGBA(image.Rect(0, 0, 300, 100))
        addLabel(img, 20, 30, "Hello Go")
    
        f, err := os.Create("hello-go.png")
        if err != nil {
            panic(err)
        }
        defer f.Close()
        if err := png.Encode(f, img); err != nil {
            panic(err)
        }
    }
    

    Note the above code also requires the "image/png" package import.

    Also note that the y coordinate given will be the bottom line of the text. So if you want to draw a line to the top left corner, you have to use x = 0 and y = 13 (13 is the height of this Face7x13 font). If you wish, you could build this into the addLabel() function by subtracting 13 from the y coordinate, so that the passed y coordinate would be the top coordinate at which the text will be drawn.

    There is also an additional self-contained font in the golang.org/x/image/font/inconsolata package with regular and bold style, to use them, you only need to specify a different Face in addLabel():

    import "golang.org/x/image/font/inconsolata"
    
            // To use regular Inconsolata font family:
            Face: inconsolata.Regular8x16,
    
            // To use bold Inconsolata font family:
            Face: inconsolata.Bold8x16,