Create a PDF Report with Charts and Graphs in Golang

Introduction

In the modern business landscape, generating high-quality PDF reports is crucial for organizations. Reports are essential for conveying important information, analyzing data, and making informed decisions. However, creating professional-looking reports that contain charts and graphs can be a challenging and time-consuming task. This is where the power of Golang and PDF libraries comes into play.

This blog post aims to explore the process of creating PDF reports with charts and graphs using Golang. We will specifically focus on UniPDF, a popular Golang library that provides powerful features for generating PDFs. By leveraging UniPDF’s capabilities, developers can streamline the report generation process and produce visually appealing reports efficiently.

Why Use a Go PDF Library?

Before diving into the specifics of PDF report generation with Golang, let’s understand why using a Go PDF library is advantageous. Golang, or Go, is a programming language known for its simplicity, efficiency, and excellent support for concurrent programming. When it comes to PDF report generation, Golang offers several benefits:

  • Simplicity: Golang has a clean and concise syntax, making it easy to learn and understand. This simplicity translates to faster development cycles for PDF report generation.

  • Efficiency: Golang’s performance is outstanding, enabling rapid processing of large-scale reports. It is well-suited for handling computationally intensive tasks, making it an ideal choice for generating PDF reports efficiently.

  • Concurrent Programming: Golang has native support for concurrency, allowing developers to leverage the power of parallel processing. This feature is particularly useful for generating multiple reports simultaneously, improving overall performance.

When compared to alternatives like Java’s iText or Adobe’s C libraries, Go PDF libraries offer unique advantages. Golang’s simplicity and efficiency, combined with its concurrent programming capabilities, make it an excellent choice for PDF report generation.

Understanding the Need for Dynamic Content in PDF Reports

Static reports with fixed content have limited flexibility and can quickly become outdated. To address this limitation, dynamic content plays a crucial role in PDF reports. Dynamic content allows for personalized and up-to-date information to be included in reports. For example, reports can include data from databases, such as customer information or real-time statistics, making them more relevant and valuable.

UniPDF, our chosen library for PDF report generation, provides extensive support for dynamic content. By integrating with databases and other data sources, UniPDF enables the creation of personalized reports with ease. Let’s dive into the features of UniPDF that support both code-generated reports and template-based reports.

UniPDF: Code-Generated Reports and Template-Based Reports

UniPDF offers flexibility in generating PDF reports by supporting two approaches: code-generated reports and template-based reports.

  • Code-Generated Reports: With UniPDF, developers can programmatically create PDF reports by writing code. This approach is ideal for highly customizable reports or scenarios where the report content is generated on the fly.

  • Template-Based Reports: UniPDF also provides a template report markup language, allowing developers to define report templates with placeholders for dynamic content. This approach is useful when the structure of the report remains constant, but the content varies for different recipients.

UniPDF offers an easy-to-use template report markup language, which simplifies the process of creating template-based reports. The UniPDF documentation provides several examples of report templates to help developers get started quickly.

Generating a PDF Report Using UniPDF

Generating a PDF report with UniPDF is a straightforward process, thanks to the available examples and documentation. UniPDF’s GitHub repository contains numerous example scripts that showcase the library’s capabilities.

To begin generating a PDF report using UniPDF, you need to follow these steps:

  1. Install UniPDF: Start by installing the UniPDF and Unichart libraries in your Golang environment. You can locate the installation instructions on the GitHub repositories for UniPDF and UniChart.

  2. Explore the Examples: UniPDF’s GitHub repository offers a variety of example scripts that cover different aspects of PDF report generation. Take the time to explore these examples and understand the possibilities.

  3. Initialize the Creator Object: To create a new PDF document, you need to initialize a creator object. The creator package in UniPDF provides the necessary functions and methods to build the PDF document.

  4. Add Content to the Report: Once the creator object is initialized, you can start adding content to the PDF report. This includes text, paragraphs, images, and charts/graphs. UniPDF provides functions to handle these elements efficiently.

  5. Save and Export: After adding all the desired content, you save the PDF document and export it as a final report file. By default, UniPDF exports the file in the plain PDF format, but it also allows you to specify the PDF/A format if required.

By following these steps, you can generate a PDF report using UniPDF. Let’s now break down the code required for creating a PDF report and discuss the purpose of each package involved.

Code Breakdown

To illustrate the process of creating a PDF report with UniPDF, let’s break down the code into several sections and explain their significance:

package main

import (
	"bytes"
	"fmt"
	"log"
	"os"

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

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() {
	c := creator.New()

    err := addContent(c)
	if err != nil {
		log.Fatalf("Error adding content to the PDF: %v", err)
	}

	err = c.WriteToFile("output.pdf")
	if err != nil {
		log.Fatalf("Error saving PDF: %v", err)
	}

	fmt.Println("PDF report generated successfully.")
}

func addContent(c *creator.Creator) error {
	// Add text and paragraphs to the PDF report
	err := addText(c)
	if err != nil {
		return err
	}

	// Add charts and graphs to the PDF report
	err = addCharts(c)
	if err != nil {
		return err
	}

	// Add images to the PDF report
	err = addImages(c)
	if err != nil {
		return err
	}

	return nil

}

In the code above, we import the necessary packages from UniPDF and initialize the creator object. We also define functions for adding content, including text, charts/graphs, and images to the PDF report. These functions can be expanded based on the specific requirements of your report.

By organizing the code into separate functions, you can maintain a modular and structured approach to PDF report generation. This allows for better code readability, maintainability, and extensibility.

Adding Text and Paragraphs

One of the essential components of a PDF report is textual content. UniPDF provides functions to add text and paragraphs to the report efficiently. Let’s explore the process of adding text and paragraphs to a new page of the PDF document.

// Add text and paragraphs to the PDF report using creator.NewParagraph and related functions
func addText(c *creator.Creator) error {
	// Define font colors and sizes for chapter and normal text
	chapterFontColor := creator.ColorRGBFrom8bit(72, 86, 95)
	chapterFontSize := 18.0
	normalFontColor := creator.ColorRGBFrom8bit(72, 86, 95)
	normalFontSize := 10.0

	// Create a new chapter titled "Text"
	ch := c.NewChapter("Text")
	ch.GetHeading().SetFontSize(chapterFontSize)
	ch.GetHeading().SetColor(chapterFontColor)

	// Create a new paragraph and set its font size, color, and margins
	p := c.NewParagraph("This chapter demonstrates a few of the features of UniDoc that can be used for report generation.")
	p.SetFontSize(normalFontSize)
	p.SetColor(normalFontColor)
	p.SetMargins(0, 0, 5, 0)

	// Add the paragraph to the chapter
	ch.Add(p)

	// Create a new subchapter titled "Paragraphs"
	sc := ch.NewSubchapter("Paragraphs")
	sc.GetHeading().SetMargins(0, 0, 20, 0)
	sc.GetHeading().SetFontSize(chapterFontSize)
	sc.GetHeading().SetColor(chapterFontColor)

	// Create a new paragraph and set its font size, color, and margins
	p = c.NewParagraph("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.")
	p.SetFontSize(normalFontSize)
	p.SetColor(normalFontColor)
	p.SetMargins(0, 0, 15, 0)
	sc.Add(p)

	// Create another paragraph
	p = c.NewParagraph("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.")
	p.SetFontSize(normalFontSize)
	p.SetColor(normalFontColor)
	p.SetMargins(20, 0, 20, 0)
	sc.Add(p)
	sc.Add(p)

	// The chapter is drawn in the file
	c.Draw(ch)

	return nil
}

In the code snippet above, we create a new page using c.NewPage() and initialize a paragraph using creator.NewParagraph(). We set the text alignment to center, add the desired text, and specify the font size. Finally, we use c.Draw(p, page) to draw the paragraph on the page.

You can further enhance the text formatting by adjusting parameters such as font type, font color, and line spacing.

For more complex reports, consider using UniPDF’s report templates that facilitate easier creation of PDFs with minimal to no coding. You can find a plethora of ready-to-use examples on the UniPDF GitHub repository, which showcase how to generate comprehensive reports with diverse structures and layouts. These templates greatly simplify the report generation process, especially for those with non-programming backgrounds.

Adding Charts and Graphs

Charts and graphs are an integral part of PDF reports. They help visualize data and make it easier to understand complex information. UniPDF provides support for adding charts and graphs to PDF reports. Let’s explore the process of adding charts and graphs to a new page of the PDF document.

// Add charts and graphs to the PDF report using the unichart library
func addCharts(c *creator.Creator) error {
	ch := c.NewChapter("Charts and Graphs")
	ch.SetMargins(0, 0, 10, 0)
	ch.GetHeading().SetFontSize(18)
	ch.GetHeading().SetColor(creator.ColorRGBFrom8bit(72, 86, 95))

	// Creating a chapter description
	p := c.NewParagraph("Here are some examples of tables and charts created with UniDoc.")
	p.SetFontSize(10)
	p.SetColor(creator.ColorRGBFrom8bit(72, 86, 95))
	p.SetMargins(0, 0, 5, 0)
	ch.Add(p)

	// Create table.
	table := c.NewTable(4)
	table.SetColumnWidths(0.1, 0.3, 0.4, 0.2)
	table.SetMargins(0, 0, 20, 0)

	// Draw the header and subheader cells
	drawCell(c, table, "Header", creator.CellHorizontalAlignmentCenter, creator.ColorWhite, creator.ColorBlue, 4)
	drawCell(c, table, "This is the subheader", creator.CellHorizontalAlignmentCenter, creator.ColorBlack, creator.ColorWhite, 4)
	table.SetHeaderRows(1, 2)

	// Draw table content.
	for i := 0; i < 8; i++ {
		num := i + 1

		color := creator.ColorBlack
		bgColor := creator.ColorWhite
		if num%2 == 0 {
			color = creator.ColorRGBFromHex("#fefefe")
			bgColor = creator.ColorRGBFromHex("#999")
		}

		drawCell(c, table, "UniDoc", creator.CellHorizontalAlignmentLeft, color, bgColor, 1)
		drawCell(c, table, fmt.Sprintf("Product #%d", num), creator.CellHorizontalAlignmentLeft, color, bgColor, 1)
		drawCell(c, table, fmt.Sprintf("Description #%d", num), creator.CellHorizontalAlignmentCenter, color, bgColor, 1)
		drawCell(c, table, fmt.Sprintf("$%d", num*10), creator.CellHorizontalAlignmentRight, color, bgColor, 1)
	}
	ch.Add(table)

	// Create a pie chart using the unichart library
	graph := &unichart.PieChart{
		Values: []dataset.Value{
			{Value: 60, Label: "Compliant"},
			{Value: 30, Label: "Non-Compliant"},
			{Value: 10, Label: "Others"},
		},
	}

	graph.SetWidth(170)
	graph.SetHeight(170)

	chartComponent := creator.NewChart(graph)

	// Add the chart component to the chapter
	ch.Add(chartComponent)

	// Draw the chapter
	if err := c.Draw(ch); err != nil {
		panic(err)
	}

	return nil
}

// Helper function to create and style a table cell with custom content.
func drawCell(c *creator.Creator, table *creator.Table, text string, align creator.CellHorizontalAlignment, color creator.Color, bgColor creator.Color, colspan int) {
	p := c.NewStyledParagraph()
	chunk := p.Append(text)
	chunk.Style.Color = color

	cell := table.MultiColCell(colspan)
	cell.SetBackgroundColor(bgColor)
	cell.SetBorder(creator.CellBorderSideAll, creator.CellBorderStyleSingle, 1)
	cell.SetHorizontalAlignment(align)
	cell.SetContent(p)
}

Adding Images

The inclusion of images in your PDF documents can significantly enrich their content and provide a visual context that complements textual information. UniPDF offers a straightforward process for seamlessly integrating images into your PDF reports. Here’s a step-by-step example to guide you in adding images to your PDF documents using the UniPDF library.

Please note that in this example, you’ll use your own images to demonstrate the process:

// Add images to the PDF report using creator.NewImageFromFile and related functions
func addImages(c *creator.Creator) error {
    // Create a new chapter titled "Images"
    ch := c.NewChapter("Images")
    ch.SetMargins(0, 0, 20, 15)
    ch.GetHeading().SetFontSize(18)
    ch.GetHeading().SetColor(creator.ColorRGBFrom8bit(72, 86, 95))

    // Create a new table with two columns
    table := c.NewTable(2)
    table.SetMargins(0, 0, 0, 10)

    // Create left division for the first image
    divLeft := c.NewDivision()
    img, err := c.NewImageFromFile("./unidoc-logo.png")
    if err != nil {
    	panic(err)
    }
    img.SetMargins(0, 0, 20, 0)
    img.ScaleToHeight(50)
    divLeft.Add(img)

    // Create right division for the second image
    divRight := c.NewDivision()
    imgGo, err := c.NewImageFromFile("./golang-img.png")
    if err != nil {
    	panic(err)
    }
    imgGo.SetMargins(0, 0, 20, 0)
    imgGo.ScaleToHeight(80)
    divRight.Add(imgGo)

    // Create left column and add the left division
    colLeft := table.NewCell()
    colLeft.SetHorizontalAlignment(creator.CellHorizontalAlignmentLeft)
    colLeft.SetContent(divLeft)

    // Create right column and add the right division
    colRight := table.NewCell()
    colRight.SetHorizontalAlignment(creator.CellHorizontalAlignmentRight)
    colRight.SetContent(divRight)

    ch.Add(table)

    // Draw the chapter
    if err := c.Draw(ch); err != nil {
    	panic(err)
    }

    return nil
}

Output

Conclusion

In this blog post, we explored the process of creating PDF reports with charts and graphs using Golang. We discussed the importance of high-quality PDF reports for organizations and highlighted the challenges involved. By utilizing Golang’s simplicity, efficiency, and support for concurrent programming, along with the UniPDF library, developers can generate visually appealing PDF reports with ease.

We covered the advantages of using Go PDF libraries and compared them to alternative solutions. We emphasized the significance of dynamic content in PDF reports and how UniPDF supports both code-generated and template-based reports. We provided an overview of the steps involved in generating a PDF report with UniPDF, and we broke down the code required for creating the report.

Additionally, we briefly mentioned other Golang libraries for PDF report generation and highlighted their unique features. It’s important to choose the library that suits your specific needs and project requirements.