Paul Bradley • Solutions Architect & Software Developer


Published:

Go Graphics - Designing Book Covers Programmatically With Golang

midjourney ai - a dark blue samurai pulling out his blade

Table of Contents
  1. Introduction
  2. Install the go graphics package
  3. Defining the variables
  4. The main function
  5. Painting colour
  6. Loading and Inserting Images
  7. Typesetting the title
  8. Typesetting the author’s name
  9. Saving the book cover to a PNG file

 Introduction

I turned my article using SQLite to generate documentation into a PDF which I then shared on LinkedIn.

To make it stand out, I designed a front cover. As I may decide to share other articles, I wanted the front covers to follow the same theme. Keeping the front covers formulaic in their design is easy when you generate the images programmatically.

This article covers how I wrote a Go command-line utility to generate the cover. I’ll also include links to resources to help you find fonts and images to use in your designs.

 Install the go graphics package

We’ll leverage Michael Fogleman’s Go Graphics package in this example. This package will do lots of the graphics heavy lifting.

To install the package, use the following command from the terminal:

go get -u github.com/fogleman/gg

 Defining the variables

We start by defining a canvas variable. The NewContext function returns a new image with the specified height and width. The canvas variable uses a context allowing us to render additional items into the image.

We then define some colour variables that we’ll use within the code. We are using the colour.RGBA function to specify the red, green and blue values needed for each colour. The fourth value passed into the colour.RGBA function is the opacity of the colour, where 255 equals 100% opacity.

Finally, we set the height in pixels of the title area. This value allows us to offset and position the central image.

var canvas = gg.NewContext(1787, 2555)                // size of the image canvas

var colorBlue = color.RGBA{60, 92, 153, 255}          // blue with full transparency
var colorWhite = color.RGBA{255, 255, 255, 255}       // white with full transparency
var colorTitleText = (color.RGBA{255, 255, 255, 230}) // white with a reduced transparency
var colorAuthorText = (color.RGBA{237, 89, 47, 255})  // dark grey with full transparency
var heightTitleArea = 920                             // height in pixels of the title area

 The main function

The main function is shown below. As you can see, we use four custom functions before calling the SavePNG function, which saves the image to a file on disk.

Firstly, a function paints a solid colour for a given rectangle, allowing us to draw the blue and white rectangles. Secondly, a function loads an image from the disk and defines where to place it on the current canvas. This function allows us to position the main central image and supplementary images for the creative commons logos and the QR code.

Finally, there are functions to set the title text and the author text.

paintColorRectangle(colorBlue, 0, 0, canvas.Width(), heightTitleArea)
paintColorRectangle(colorWhite, 0, heightTitleArea, canvas.Width(), canvas.Height())

loadAndInsertImage("undraw_data_processing_yrrv.png", 0, heightTitleArea+100)
setTitleText("USING SQLITE TO CREATE DOCUMENTATION & SOFTWARE CONFIGURATIONS", 150, 135)
setAuthorText("PAUL BRADLEY", 125)

loadAndInsertImage("cc.xlarge.png", 750, 2300)
loadAndInsertImage("by.xlarge.png", 920, 2300)
loadAndInsertImage("qrcode.png", 1500, 2300)

err = canvas.SavePNG("cover.png")
return err

 Painting colour

The paintColorRectangle function accepts five parameters. The first is the colour we want to paint, followed by four numbers representing the rectangle’s upper left and lower right coordinates.

The canvas’s coordinate system denotes that 0,0 is the upper left corner. Bear this in mind: the coordinates will increase as you move down and right from the top left corner.

We use the SetColor function to define the colour passed and then use DrawRectangle to specify the coordinates. Finally, we use the Fill command to flood fill and paint the defined area with the designated colour.

func paintColorRectangle(c color.Color, x, y, w, h int) {
    canvas.SetColor(c)
    canvas.DrawRectangle(float64(x), float64(y), float64(w), float64(h))
    canvas.Fill()
}

 Loading and Inserting Images

Our loadAndInsertImage function accepts three parameters. The first is the image file we want to paste into the canvas. The last two parameters are numbers defining the upper left location where the image should be pasted onto the canvas.

Firstly, we attempt to load the image file and check for any errors. We print an error message and exit the program if an error is detected.

If no error was detected, we use the DrawImage function to paste the image onto our canvas.

func loadAndInsertImage(imgFile string, x, y int) {
    backgroundImage, err := gg.LoadImage(imgFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        os.Exit(1)
    }
    canvas.DrawImage(backgroundImage, x, y)
}

There are a couple of places you can legally get images to include on your book cover. These include:

 Typesetting the title

To typeset the main title, we use our setTitleText function, which accepts three parameters. The first parameter is the text we want as the main title.

The second parameter is a number which represents a margin value in pixels. The margin defines how close to the image edge the text should be allowed to go.

The last parameter is a number which defines the font size in pixels.

func setTitleText(title string, margin int, size int) {
    // load the font file and set the font size
    if err := canvas.LoadFontFace("RobotoCondensed-Bold.ttf",
        float64(size)); err != nil {
            fmt.Fprintf(os.Stderr, "%s\n", err)
            os.Exit(1)
    }

    // set the font text color and output the title text
    // using a defined margin - centering the text bounding
    // area within the title area
    canvas.SetColor(colorTitleText)
    canvas.DrawStringWrapped(title,
        float64(canvas.Width())/2,
        float64(heightTitleArea)/2,
        0.5,
        0.5,
        float64(canvas.Width()-(margin*2)),
        1.5, // line height
        gg.AlignLeft)
}

We start by using the LoadFontFace function to load the font file from the disk and specify the font size. Next, we check that the font file was correctly loaded. We print out the error message and exit the program if an error is detected. If you need to find font files, then Google Fonts is a good source, and Google allows you to download the font files.

Once the font file has loaded, we set the colour of the text using SetColor.

The DrawStringWrapped function word-wraps the specified string to the given max width and then draws it at the specified anchor point using the given line spacing and text alignment.

Typesetting the author’s name

Outputting the author’s name is a little easier as we don’t have to use word wrapping. We load the font file as before. Set the colour and then use the DrawString function to output the given string at the defined position.

func setAuthorText(author string, size int) {
    if err := canvas.LoadFontFace("BebasNeue-Regular.ttf", float64(size)); err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        os.Exit(1)
    }

    canvas.SetColor(colorAuthorText)
    canvas.DrawString(author, float64(140), float64(2420))
}

 Saving the book cover to a PNG file

Finally, we save the canvas image to a disk file using the PNG image format. The function below shows you how to create a disk file. It also demonstrates how to Encode the image as a PNG file

func SavePNG(img image.Image, filename string) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    if err := png.Encode(f, img); err != nil {
        return err
    }
    if err := f.Close(); err != nil {
        return err
    }
    return nil
}