Create PDF Reports

This guide shows you to create a PDF document with UniPDF. With this knowledge, You can perform further PDF functionalities in your application.

Before you begin

You should get your API key from your UniCloud account.

If this is your first time using UniPDF SDK, follow this guide to set up a local development environment.

Project Setup

Clone the Project Repository

In your terminal, clone the examples repository. It contains the Go code we will be using for this guide.

git clone https://github.com/unidoc/unipdf-examples.git

Change into the repository directory and navigate to the report folder.

cd unipdf-examples/report

Configure Environment Variables

Replace the UNIDOC_LICENSE_API_KEY with your API credentials from your UniCloudaccount.

Linux/Mac

export UNIDOC_LICENSE_API_KEY=PUT_YOUR_API_KEY_HERE

Windows

set UNIDOC_LICENSE_API_KEY=PUT_YOUR_API_KEY_HERE

Code Review

/*
* This example showcases PDF report generation with unidoc's creator package.
* The output is saved as unidoc-report.pdf which illustrates some of the features
* of the creator.
*/
/*
* NOTE: This example depends on github.com/boombuler/barcode, MIT licensed,
* and github.com/unidoc/unichart, MIT licensed,
* and the Roboto font (Roboto-Bold.ttf, Roboto-Regular.ttf), Apache-2 licensed.
*/
package main
import (
"fmt"
"image"
"math"
"os"
"time"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/qr"
"github.com/unidoc/unichart"
"github.com/unidoc/unichart/dataset"
"github.com/unidoc/unipdf/v4/common"
"github.com/unidoc/unipdf/v4/common/license"
"github.com/unidoc/unipdf/v4/creator"
"github.com/unidoc/unipdf/v4/model"
)
func init() {
// Make sure to load your metered License API key prior to using the library.
// If you need a key, you can sign up and create a free one at https://cloud.unidoc.io
err := license.SetMeteredKey(os.Getenv(`UNIDOC_LICENSE_API_KEY`))
if err != nil {
panic(err)
}
}
func main() {
err := RunPdfReport("unidoc-report.pdf")
if err != nil {
panic(err)
}
}
func RunPdfReport(outputPath string) error {
robotoFontRegular, err := model.NewPdfFontFromTTFFile("./Roboto-Regular.ttf")
if err != nil {
return err
}
robotoFontPro, err := model.NewPdfFontFromTTFFile("./Roboto-Bold.ttf")
if err != nil {
return err
}
c := creator.New()
c.SetPageMargins(50, 50, 100, 70)
// Generate the table of contents.
c.AddTOC = true
toc := c.TOC()
hstyle := c.NewTextStyle()
hstyle.Color = creator.ColorRGBFromArithmetic(0.2, 0.2, 0.2)
hstyle.FontSize = 28
toc.SetHeading("Table of Contents", hstyle)
lstyle := c.NewTextStyle()
lstyle.FontSize = 14
toc.SetLineStyle(lstyle)
logoImg, err := c.NewImageFromFile("./unidoc-logo.png")
if err != nil {
return err
}
logoImg.ScaleToHeight(25)
logoImg.SetPos(58, 20)
DoDocumentControl(c, robotoFontRegular, robotoFontPro)
DoFeatureOverview(c, robotoFontRegular, robotoFontPro)
// Setup a front page (always placed first).
c.CreateFrontPage(func(args creator.FrontpageFunctionArgs) {
DoFirstPage(c, robotoFontRegular, robotoFontPro)
})
// Draw a header on each page.
c.DrawHeader(func(block *creator.Block, args creator.HeaderFunctionArgs) {
// Draw the header on a block. The block size is the size of the page's top margins.
block.Draw(logoImg)
})
// Draw footer on each page.
c.DrawFooter(func(block *creator.Block, args creator.FooterFunctionArgs) {
// Draw the on a block for each page.
p := c.NewStyledParagraph()
p.SetText("unidoc.io")
p.SetFont(robotoFontRegular)
p.SetFontSize(8)
p.SetPos(50, 20)
p.SetFontColor(creator.ColorRGBFrom8bit(63, 68, 76))
block.Draw(p)
strPage := fmt.Sprintf("Page %d of %d", args.PageNum, args.TotalPages)
p = c.NewStyledParagraph()
p.SetText(strPage)
p.SetFont(robotoFontRegular)
p.SetFontSize(8)
p.SetPos(300, 20)
p.SetFontColor(creator.ColorRGBFrom8bit(63, 68, 76))
block.Draw(p)
})
err = c.WriteToFile(outputPath)
if err != nil {
return err
}
return nil
}
// Generates the front page.
func DoFirstPage(c *creator.Creator, fontRegular *model.PdfFont, fontBold *model.PdfFont) {
helvetica, _ := model.NewStandard14Font("Helvetica")
helveticaBold, _ := model.NewStandard14Font("Helvetica-Bold")
p := c.NewStyledParagraph()
p.SetText("UniDoc")
p.SetFont(helvetica)
p.SetFontSize(48)
p.SetMargins(85, 0, 150, 0)
p.SetFontColor(creator.ColorRGBFrom8bit(56, 68, 77))
c.Draw(p)
p = c.NewStyledParagraph()
p.SetText("Example Report")
p.SetFont(helveticaBold)
p.SetFontSize(30)
p.SetMargins(85, 0, 0, 0)
p.SetFontColor(creator.ColorRGBFrom8bit(45, 148, 215))
c.Draw(p)
t := time.Now().UTC()
dateStr := t.Format("1 Jan, 2006 15:04")
p = c.NewStyledParagraph()
p.SetText(dateStr)
p.SetFont(helveticaBold)
p.SetFontSize(12)
p.SetMargins(90, 0, 5, 0)
p.SetFontColor(creator.ColorRGBFrom8bit(56, 68, 77))
c.Draw(p)
}
// Document control page.
func DoDocumentControl(c *creator.Creator, fontRegular *model.PdfFont, fontBold *model.PdfFont) {
ch := c.NewChapter("Document control")
ch.SetMargins(0, 0, 40, 0)
heading := ch.GetHeading()
heading.SetFont(fontRegular)
heading.SetFontSize(18)
heading.SetFontColor(creator.ColorRGBFrom8bit(72, 86, 95))
sc := ch.NewSubchapter("Issuer details")
heading = sc.GetHeading()
heading.SetFont(fontRegular)
heading.SetFontSize(18)
heading.SetFontColor(creator.ColorRGBFrom8bit(72, 86, 95))
issuerTable := c.NewTable(2)
issuerTable.SetMargins(0, 0, 30, 0)
pColor := creator.ColorRGBFrom8bit(72, 86, 95)
bgColor := creator.ColorRGBFrom8bit(56, 68, 67)
p := c.NewStyledParagraph()
p.SetText("Issuer")
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell := issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetBackgroundColor(bgColor)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("UniDoc")
p.SetFont(fontRegular)
p.SetFontSize(10)
p.SetFontColor(pColor)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("Address")
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetBackgroundColor(bgColor)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("Klapparstig 16, 101 Reykjavik, Iceland")
p.SetFont(fontRegular)
p.SetFontSize(10)
p.SetFontColor(pColor)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("Email")
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell = issuerTable.NewCell()
cell.SetBackgroundColor(bgColor)
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("sales@unidoc.io")
p.SetFont(fontRegular)
p.SetFontSize(10)
p.SetFontColor(pColor)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("Web")
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetBackgroundColor(bgColor)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("unidoc.io")
p.SetFont(fontRegular)
p.SetFontSize(10)
p.SetFontColor(pColor)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("Author")
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetBackgroundColor(bgColor)
cell.SetContent(p)
p = c.NewStyledParagraph()
p.SetText("UniDoc report generator")
p.SetFont(fontRegular)
p.SetFontSize(10)
p.SetFontColor(pColor)
cell = issuerTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
sc.Add(issuerTable)
// 1.2 - Document history
sc = ch.NewSubchapter("Document History")
sc.SetMargins(0, 0, 5, 0)
heading = sc.GetHeading()
heading.SetFont(fontRegular)
heading.SetFontSize(18)
heading.SetFontColor(pColor)
histTable := c.NewTable(3)
histTable.SetMargins(0, 0, 30, 50)
histCols := []string{"Date Issued", "UniDoc Version", "Type/Change"}
for _, histCol := range histCols {
p = c.NewStyledParagraph()
p.SetText(histCol)
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell = histTable.NewCell()
cell.SetBackgroundColor(bgColor)
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(creator.CellHorizontalAlignmentCenter)
cell.SetVerticalAlignment(creator.CellVerticalAlignmentMiddle)
cell.SetContent(p)
}
dateStr := common.ReleasedAt.Format("1 Jan, 2006 15:04")
histVals := []string{dateStr, common.Version, "First issue"}
for _, histVal := range histVals {
p = c.NewStyledParagraph()
p.SetText(histVal)
p.SetFont(fontRegular)
p.SetFontSize(10)
p.SetFontColor(pColor)
cell = histTable.NewCell()
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetHorizontalAlignment(creator.CellHorizontalAlignmentCenter)
cell.SetVerticalAlignment(creator.CellVerticalAlignmentMiddle)
cell.SetContent(p)
}
sc.Add(histTable)
err := c.Draw(ch)
if err != nil {
panic(err)
}
}
// Chapter giving an overview of features.
// TODO: Add code snippets and show more styles and options.
func DoFeatureOverview(c *creator.Creator, fontRegular *model.PdfFont, fontBold *model.PdfFont) {
// Ensure that the chapter starts on a new page.
c.NewPage()
ch := c.NewChapter("Feature overview")
chapterFont := fontRegular
chapterFontColor := creator.ColorRGBFrom8bit(72, 86, 95)
chapterFontSize := 18.0
normalFont := fontRegular
normalFontColor := creator.ColorRGBFrom8bit(72, 86, 95)
normalFontSize := 10.0
bgColor := creator.ColorRGBFrom8bit(56, 68, 67)
heading := ch.GetHeading()
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p := c.NewStyledParagraph()
p.SetText("This chapter demonstrates a few of the features of UniDoc that can be used for report generation.")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 0)
ch.Add(p)
// Paragraphs.
sc := ch.NewSubchapter("Paragraphs")
heading = sc.GetHeading()
heading.SetMargins(0, 0, 20, 0)
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p = c.NewStyledParagraph()
p.SetText("Paragraphs are used to represent text, as little as a single character, a word or " +
"multiple words forming multiple sentences. UniDoc handles automatically wrapping those across lines and pages, making " +
"it relatively easy to work with. They can also be left, center, right aligned or justified as illustrated below:")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 0)
sc.Add(p)
// Example paragraphs:
loremTxt := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt" +
"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
"aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore" +
"eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
"mollit anim id est laborum."
alignments := []creator.TextAlignment{creator.TextAlignmentLeft, creator.TextAlignmentCenter,
creator.TextAlignmentRight, creator.TextAlignmentJustify}
for j := 0; j < 4; j++ {
p = c.NewStyledParagraph()
p.SetText(loremTxt)
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(20, 0, 10, 10)
p.SetTextAlignment(alignments[j%4])
sc.Add(p)
}
sc = ch.NewSubchapter("Tables")
// Mock table: Priority table.
priTable := c.NewTable(2)
priTable.SetMargins(40, 40, 10, 0)
// Column headers:
tableCols := []string{"Priority", "Items fulfilled / available"}
for _, tableCol := range tableCols {
p = c.NewStyledParagraph()
p.SetText(tableCol)
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell := priTable.NewCell()
cell.SetBackgroundColor(bgColor)
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
}
items := [][]string{
[]string{"High", "52/80"},
[]string{"Medium", "32/100"},
[]string{"Low", "10/90"},
}
for _, lineItems := range items {
for _, item := range lineItems {
p = c.NewStyledParagraph()
p.SetText(item)
p.SetFont(fontBold)
p.SetFontSize(10)
p.SetFontColor(creator.ColorWhite)
cell := priTable.NewCell()
cell.SetBackgroundColor(bgColor)
cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
cell.SetContent(p)
}
}
sc.Add(priTable)
sc = ch.NewSubchapter("Images")
heading = sc.GetHeading()
heading.SetMargins(0, 0, 20, 0)
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p = c.NewStyledParagraph()
p.SetText("Images can be loaded from multiple file formats, example from a PNG image:")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 5)
sc.Add(p)
// Show logo.
img, err := c.NewImageFromFile("./unidoc-logo.png")
if err != nil {
panic(err)
}
img.ScaleToHeight(50)
sc.Add(img)
sc = ch.NewSubchapter("QR Codes / Barcodes")
heading = sc.GetHeading()
heading.SetMargins(0, 0, 20, 0)
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p = c.NewStyledParagraph()
p.SetText("Example of a QR code generated with package github.com/boombuler/barcode:")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 5)
sc.Add(p)
qrCode, _ := makeQrCodeImage("HELLO", 40, 5)
img, err = c.NewImageFromGoImage(qrCode)
if err != nil {
panic(err)
}
img.SetWidth(40)
img.SetHeight(40)
sc.Add(img)
sc = ch.NewSubchapter("Graphing / Charts")
heading = sc.GetHeading()
heading.SetMargins(0, 0, 20, 0)
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p = c.NewStyledParagraph()
p.SetText("Graphs can be generated via packages such as github.com/unidoc/unichart as illustrated " +
"in the following plot:")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 0)
sc.Add(p)
chart := &unichart.PieChart{
Values: []dataset.Value{
{Value: 70, Label: "Compliant"},
{Value: 30, Label: "Non-Compliant"},
},
}
chart.SetWidth(175)
chart.SetHeight(175)
// Create unipdf chart component.
chartComponent := creator.NewChart(chart)
sc.Add(chartComponent)
sc = ch.NewSubchapter("Headers and footers")
heading = sc.GetHeading()
heading.SetMargins(0, 0, 20, 0)
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p = c.NewStyledParagraph()
p.SetText("Convenience functions are provided to generate headers and footers, see: " +
"https://godoc.org/github.com/unidoc/unipdf/creator#Creator.DrawHeader and " +
"https://godoc.org/github.com/unidoc/unipdf/creator#Creator.DrawFooter " +
"They both set a function that accepts a block which the header/footer is drawn on for each page. " +
"More information is provided in the arguments, allowing to skip header/footer on specific pages and " +
"showing page number and count.")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 0)
sc.Add(p)
sc = ch.NewSubchapter("Table of contents generation")
heading = sc.GetHeading()
heading.SetMargins(0, 0, 20, 0)
heading.SetFont(chapterFont)
heading.SetFontSize(chapterFontSize)
heading.SetFontColor(chapterFontColor)
p = c.NewStyledParagraph()
p.SetText("A convenience function is provided to generate table of contents " +
"as can be seen on https://godoc.org/github.com/unidoc/unipdf/creator#Creator.CreateTableOfContents and " +
"in our example code on unidoc.io.")
p.SetFont(normalFont)
p.SetFontSize(normalFontSize)
p.SetFontColor(normalFontColor)
p.SetMargins(0, 0, 5, 0)
sc.Add(p)
c.Draw(ch)
}
// Helper function to make the QR code image with a specified oversampling factor.
// The oversampling specifies how many pixels/point. Standard PDF resolution is 72 points/inch.
func makeQrCodeImage(text string, width float64, oversampling int) (image.Image, error) {
qrCode, err := qr.Encode(text, qr.M, qr.Auto)
if err != nil {
return nil, err
}
pixelWidth := oversampling * int(math.Ceil(width))
qrCode, err = barcode.Scale(qrCode, pixelWidth, pixelWidth)
if err != nil {
return nil, err
}
return qrCode, nil
}
view raw report/pdf_report.go delivered with ❤ by emgithub

Lines 14-31 import the UniPDF packages and other required dependencies.

Lines 33-40 authenticate your request with your UNIDOC_LICENSE_API_KEY with the init function.

Lines 42-47 call the RunPDFReport function with an input, which is the path to the PDF you want to create. In this guide, we will save the output to unidoc-report.pdf.

From line 49, we will create the RunPDFreport function in detail.

Lines 50-58 loads the .tff font file into a PDFFont type that can be used in text styling functions. For this example, we will be using the Roboto font (bold and regular).

Lines 60-62 create a new instance of the PDF creator using the .New() method, set the page margins in 4D (left, right, top, bottom) with the setPDFMargin function.

Line 63-122 generates a table of contents for the created PDF with the .AddTOC method. After which, we set a particular page as the front page of the PDF, drawing a header and footer on each created output page using AddHeader and AddFooter functions, respectively. Finally, we save the output to a specific file in the path.

Lines 125-152 style the front page with font, colour and margins. The front page can include paragraphs and headers.

Run the code

Run this command to create the PDF. This will also get all the required dependencies to run the program.

go run pdf_report.go

Sample Output

Open the created PDF in any PDF viewer, and you should get a 5-paged PDF like this.

PDF report

Got any Questions?

We're here to help you.