HTML for PDF reports in Go
Tags: go, docker, programming, productivity
Published: 2021-03-29 17:46:04

As a developer, I sometimes need to create PDF reports for my applications.

Creating those entirely by programming can be cumbersome and every library is a bit different. Finally, getting things to look like the designer wanted can be challenging.

Wouldn't it be nice if we can make it look like the design without spending a ton of time on it?

Designers and frontenders can usually do HTML and CSS, so it makes sense to utilize HTML.

But websites usually don't look great when printed out and are not designed for multiple pages.

We have come up with a solution, which we believe addresses all of the above.

## Meet UniHTML combined with UniPDF

[UniHTML](https://github.com/unidoc/unihtml) is a new plugin for [UniPDF](https://github.com/unidoc/unipdf), one of our flagship libraries at [UniDoc](https://unidoc.io).

It is a container-based solution with a Go driver, as per the schematic:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ix9po3fyu0d9spzjipt1.png)

The Docker image is [publicly available](https://hub.docker.com/repository/docker/unidoccloud/unihtml) on Docker Hub.

The UniPDF creator package enables [creating flexible PDF reports](https://www.unidoc.io/post/creating-pdf-reports-in-golang) and [invoices](https://www.unidoc.io/post/simple-invoices). The UniHTML container-based module has a flexible web-rendering engine and in unison with UniPDF this brings together the capability to add full HTML support to UniPDF report generation.

## Trying it out

Let's try it out.

### Step 1. Create a free metered API key.

This is easy, simply signing up on https://cloud.unidoc.io for an account and creating a metered API key in the UI.

For step-by-step instructions on this, see:
- [How to sign up for UniCloud](https://help.unidoc.io/article/142-how-to-sign-up-for-unicloud)
- [How to generate a metered API key](https://help.unidoc.io/article/141-metered-license-api-key)


### Step 2. Get the UniHTML container running
```
$ docker run -p 8080:8080 -e UNIDOC_METERED_API_KEY=mymeteredkey unidoccloud/unihtml
Unable to find image 'unidoccloud/unihtml:latest' locally
latest: Pulling from unidoccloud/unihtml
6e640006d1cd: Pull complete
1a3def68b0c4: Pull complete
5b1718db67b4: Pull complete
8d4c41b870b6: Pull complete
b1a4436c2bab: Pull complete
3c3af5a4fff5: Pull complete
29863d0ede88: Pull complete
Digest: sha256:c1c69af194358179d836a648f07f71af07ed0c968938abe3a3e2550e49980728
Status: Downloaded newer image for unidoccloud/unihtml:latest
[INFO]  server.go:173 Listening private API on: :8081
[INFO]  server.go:164 Listening public API on: :8080
```

### Step 3. Running an example.

Inspired by the blog post [Creating beautiful HTML tables with CSS](https://dev.to/dcodeyt/creating-beautiful-html-tables-with-css-428l) we put together the following HTML file to illustrate a PDF report with HTML tables.

#### sample.html
```html
<html>
<head>
<style>

.styled-table {
    border-collapse: collapse;
    margin: 25px 0;
    font-size: 0.9em;
    font-family: sans-serif;
    min-width: 400px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
}

.styled-table thead tr {
    background-color: #009879;
    color: #ffffff;
    text-align: left;
}

.styled-table th,
.styled-table td {
    padding: 12px 15px;
}


.styled-table tbody tr {
    border-bottom: 1px solid #dddddd;
}

.styled-table tbody tr:nth-of-type(even) {
    background-color: #f3f3f3;
}

.styled-table tbody tr:last-of-type {
    border-bottom: 2px solid #009879;
}


.styled-table tbody tr.active-row {
    font-weight: bold;
    color: #009879;
}

</style>
</head>

<table class="styled-table">
    <thead>
        <tr>
            <th>Name</th>
            <th>Points</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Dom</td>
            <td>6000</td>
        </tr>
        <tr class="active-row">
            <td>Melissa</td>
            <td>5150</td>
        </tr>
        <!-- and so on... -->
    </tbody>
</table>

</html>
```

#### example.go
```go
package main

import (
	"fmt"
	"os"

	"github.com/unidoc/unihtml"
	"github.com/unidoc/unipdf/v3/common/license"
	"github.com/unidoc/unipdf/v3/creator"
)

func main() {
	// Set the UniDoc license.
	if err := license.SetMeteredKey("my api key goes here"); err != nil {
		fmt.Printf("Err: setting metered key failed: %v\n", err)
		os.Exit(1)
	}

	// Establish connection with the UniHTML Server.
	if err := unihtml.Connect(":8080"); err != nil {
		fmt.Printf("Err:  Connect failed: %v\n", err)
		os.Exit(1)
	}

	// Get new PDF Creator.
	c := creator.New()

	// AddTOC enables Table of Contents generation.
	c.AddTOC = true

	chapter := c.NewChapter("Points")

	// Read the content of the sample.html file and load it to the conversion.
	htmlDocument, err := unihtml.NewDocument("sample.html")
	if err != nil {
		fmt.Printf("Err: NewDocument failed: %v\n", err)
		os.Exit(1)
	}

	// Draw the html document file in the context of the creator.
	if err = chapter.Add(htmlDocument); err != nil {
		fmt.Printf("Err: Draw failed: %v\n", err)
		os.Exit(1)
	}

	if err = c.Draw(chapter); err != nil {
		fmt.Printf("Err: Draw failed: %v\n", err)
		os.Exit(1)
	}


	// Write the result file to PDF.
	if err = c.WriteToFile("sample.pdf"); err != nil {
		fmt.Printf("Err: %v\n", err)
		os.Exit(1)
	}
}
```

## Results
Running the result:
```
$ go run example.go
```
creates `sample.pdf` which looks like:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23mc8oiez26pzefs8elz.png)
We note that we also have the table of contents
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgyxkvzy2v89yjzxcaqo.png)

and the bookmarks that link to each chapter in the PDF.

Headers and footers can also be created easily.

## Conclusion

UniHTML brings the ease of HTML to PDF report creation in UniPDF with a full rendering engine.  This will make the process of PDF report generation really easy for teams that already have a HTML design and need to add professional PDF reports, where a plain PDF print-out of a website is not sufficient.