Integrating Charts and Graphs into PDFs using Golang PDF Library
Introduction
In the dynamic world of business and data analysis, presenting information in a visually appealing manner is crucial to effectively communicate insights. While raw data may convey the story, charts and graphs breathe life into the numbers, making patterns and trends more accessible to stakeholders and decision-makers. Integrating charts and graphs into PDFs using Golang PDF Library can be a game-changer for developers seeking to enhance their document generation capabilities.
In this article, we will explore how to leverage the power of Golang and the UniDoc PDF library to create stunning PDF documents with embedded charts and graphs. We will cover the necessary steps, code snippets, and provide external links to the UniDoc GitHub repository for reference.
Why Golang PDF Library?
Before we dive into the technicalities, let’s briefly discuss why the Golang PDF library from UniDoc is an excellent choice for this task.
Open-Source and Active Community: The UniDoc library is open-source, actively maintained, and supported by a vibrant community of developers. This ensures continuous improvement and support for new features.
Rich Feature Set: The library boasts a wide range of functionalities, enabling developers to generate complex and professional PDFs with ease.
Golang’s Efficiency: Golang is known for its efficiency and performance, making it an ideal language for working with large datasets and generating PDFs swiftly.
Now, let’s dive into the implementation and see how we can integrate charts and graphs into PDFs using Golang.
Getting Started
To follow along with the code examples, make sure you have Golang installed on your system. You can download it from the official Golang website
Next, we need to install the UniDoc PDF library. Open your terminal or command prompt and execute the following command:
go get github.com/unidoc/unipdf/v3/...
go get github.com/unidoc/unichart
This will download and install the necessary packages to work with the UniDoc PDF library.
Creating a Simple PDF
Let’s start by creating a basic PDF document using the UniDoc PDF library. We’ll then progress to add charts and graphs to it.
package main
import (
"fmt"
"log"
"os"
"github.com/unidoc/unipdf/v3/common/license"
"github.com/unidoc/unipdf/v3/creator"
)
func init() {
// Initialize the UniDoc licensing API with your metered license key.
// If you don't have one, sign up for a free key at https://cloud.unidoc.io
err := license.SetMeteredKey(os.Getenv("UNIDOC_LICENSE_API_KEY"))
if err != nil {
panic(err)
}
}
func main() {
// Create a new PDF creator instance.
c := creator.New()
// Create a new chapter named "Text".
ch := c.NewChapter("Text")
// Create a new paragraph with Lorem Ipsum text.
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.")
// Set top margin for the paragraph.
p.SetMargins(0, 0, 20, 0)
// Add the paragraph to the chapter.
ch.Add(p)
// Draw the chapter in the PDF.
c.Draw(ch)
// Save the generated PDF to a file named "simple.pdf".
err := c.WriteToFile("simple.pdf")
if err != nil {
log.Fatalf("Error saving PDF: %v", err)
}
// Print a success message to indicate the PDF generation is successful.
fmt.Println("Simple PDF generated successfully.")
}
In the above code snippet, we create a simple PDF with a single page and add a text element to it.
Generating Charts and Graphs
Now comes the exciting part – generating charts and graphs! In this section, we will demonstrate how to create a bar chart and a line graph using the UniDoc PDF library. We’ll use fabricated data for illustration purposes.
package main
import (
"fmt"
"image/color"
"log"
"os"
"github.com/unidoc/unichart"
"github.com/unidoc/unichart/dataset"
"github.com/unidoc/unichart/dataset/sequence"
"github.com/unidoc/unichart/render"
"github.com/unidoc/unipdf/v3/common/license"
"github.com/unidoc/unipdf/v3/creator"
)
func init() {
// Initialize the UniDoc licensing API with your metered license key.
// If you don't have one, sign up for a free key at https://cloud.unidoc.io
err := license.SetMeteredKey(os.Getenv("UNIDOC_LICENSE_API_KEY"))
if err != nil {
panic(err)
}
}
func main() {
// Create a new PDF creator instance.
c := creator.New()
// Define styles for the bar chart bars.
redStyle := render.Style{
FillColor: color.RGBA{R: 240, G: 12, B: 39, A: 255}, // Red fill color
StrokeColor: color.RGBA{R: 240, G: 12, B: 39, A: 255}, // Red stroke color
StrokeWidth: 0, // No stroke
}
blueStyle := render.Style{
FillColor: color.RGBA{R: 7, G: 114, B: 205, A: 255}, // Blue fill color
StrokeColor: color.RGBA{R: 7, G: 114, B: 205, A: 255}, // Blue stroke color
StrokeWidth: 0, // No stroke
}
// Create a bar chart instance.
chart := &unichart.BarChart{
Title: "Sales", // Chart title
BarWidth: 40, // Width of each bar
BarSpacing: 30, // Spacing between bars
Bars: []dataset.Value{
{Value: 5.25, Style: redStyle, Label: "January"}, // Bar 1 with red style
{Value: 4.88, Style: blueStyle, Label: "February"}, // Bar 2 with blue style
{Value: 4.74, Style: redStyle, Label: "March"}, // Bar 3 with red style
{Value: 3.22, Style: blueStyle, Label: "April"}, // Bar 4 with blue style
{Value: 2.86, Style: redStyle, Label: "May"}, // Bar 5 with red style
{Value: 2.27, Style: blueStyle, Label: "June"}, // Bar 6 with blue style
},
}
chart.SetHeight(360) // Set chart height
// Calculate the maximum value in the dataset for custom Y-axis range.
var max float64
for _, bar := range chart.Bars {
if max < bar.Value {
max = bar.Value
}
}
// Create a continuous range for Y-axis and set its min and max.
rng := &sequence.ContinuousRange{}
rng.SetMin(0) // Minimum value on Y-axis
rng.SetMax(max) // Maximum value on Y-axis
chart.YAxis.Range = rng
// Draw the chart component.
chartComponent := creator.NewChart(chart)
err := c.Draw(chartComponent)
if err != nil {
log.Fatalf("Error Drawing BarChart: %v", err)
}
// Save the generated PDF to a file named "BarChart.pdf".
err = c.WriteToFile("BarChart.pdf")
if err != nil {
log.Fatalf("Error saving PDF: %v", err)
}
// Print a success message to indicate the PDF generation is successful.
fmt.Println("Simple BarChart PDF generated successfully.")
}
The above code generates a bar chart representing sales data for six months. We set the chart’s title, data, colors, width, height, and other visual properties. The chart is then added to the PDF page.
package main
import (
"fmt"
"image/color"
"log"
"os"
"github.com/unidoc/unichart"
"github.com/unidoc/unichart/dataset"
"github.com/unidoc/unichart/dataset/sequence"
"github.com/unidoc/unichart/render"
"github.com/unidoc/unipdf/v3/common/license"
"github.com/unidoc/unipdf/v3/creator"
)
func init() {
// Initialize the UniDoc licensing API with your metered license key.
// If you don't have one, sign up for a free key at https://cloud.unidoc.io
err := license.SetMeteredKey(os.Getenv("UNIDOC_LICENSE_API_KEY"))
if err != nil {
panic(err)
}
}
func main() {
// Create a new PDF creator instance.
c := creator.New()
// Define a style for the line chart.
blueStyle := render.Style{
StrokeColor: color.RGBA{R: 7, G: 114, B: 205, A: 255}, // Blue stroke color
StrokeWidth: 0, // No stroke
}
// Create a line chart instance.
chart := &unichart.Chart{
Title: "Traffic for six days", // Chart title
XAxis: unichart.XAxis{
Name: "Days", // X-axis label
},
YAxis: unichart.YAxis{
Name: "Values", // Y-axis label
Range: &sequence.ContinuousRange{
Min: 0, // Minimum value on Y-axis
Max: 3, // Maximum value on Y-axis
},
},
Series: []dataset.Series{
dataset.ContinuousSeries{
XValues: []float64{1, 2, 3, 4, 5, 6}, // X values for data points
YValues: []float64{0.50, 1.4, 2, 2.5, 1.8, 2.3}, // Y values for data points
Style: blueStyle, // Style for the data points
},
},
}
chart.SetHeight(300) // Set chart height
// Create a UniPDF chart component.
chartComponent := creator.NewChart(chart)
// Draw the chart component.
if err := c.Draw(chartComponent); err != nil {
log.Fatalf("failed to draw the lineChart: %v", err)
}
// Save the generated PDF to a file named "lineChart.pdf".
err := c.WriteToFile("lineChart.pdf")
if err != nil {
log.Fatalf("Error saving PDF: %v", err)
}
// Print a success message to indicate the PDF generation is successful.
fmt.Println("Simple lineChart PDF generated successfully.")
}
Similarly, the above code generates a line graph representing website traffic for six days. We set the graph’s title, data, colors, width, height, and other visual properties before adding it to the PDF page.
Integrating Charts and Graphs into the Simple PDF
Now that we have separate PDFs containing the bar chart and line graph, let’s merge them into the simple PDF created earlier.
package main
import (
"fmt"
"image"
"log"
"os"
"github.com/unidoc/unipdf/v3/common/license"
"github.com/unidoc/unipdf/v3/creator"
"github.com/unidoc/unipdf/v3/model"
"github.com/unidoc/unipdf/v3/render"
)
func init() {
// Initialize the UniDoc licensing API with your metered license key.
// If you don't have one, sign up for a free key at https://cloud.unidoc.io
err := license.SetMeteredKey(os.Getenv("UNIDOC_LICENSE_API_KEY"))
if err != nil {
panic(err)
}
}
func main() {
// Output folder and input PDF files.
outputDir := "./"
inputFiles := []string{"BarChart.pdf", "Chart.pdf"}
// Extract images from input PDF files.
images, err := extractImagesFromPDFs(outputDir, inputFiles)
if err != nil {
log.Fatalf("Error extracting images: %v", err)
}
// Add images to a new PDF.
addImagesToPdf("simple.pdf", "simple.pdf", images, 0)
}
func extractImagesFromPDFs(outputDir string, inputFiles []string) ([]image.Image, error) {
device := render.NewImageDevice()
var images []image.Image
for _, inputFilename := range inputFiles {
// Open the input PDF file.
reader, f, err := model.NewPdfReaderFromFile(inputFilename, nil)
if err != nil {
return nil, fmt.Errorf("could not create reader: %v", err)
}
defer f.Close()
pageNumber := 1 // Render only the first page.
// Get the page.
page, err := reader.GetPage(pageNumber)
if err != nil {
return nil, fmt.Errorf("could not retrieve page: %v", err)
}
// Adjust the media box size.
mediaBox, err := page.GetMediaBox()
mediaBox.Lly = (mediaBox.Ury - mediaBox.Lly) / 2
page.MediaBox = mediaBox
// Render the page to an image.
image, err := device.Render(page)
if err != nil {
return nil, fmt.Errorf("image rendering error: %v", err)
}
images = append(images, image)
}
return images, nil
}
func addImagesToPdf(inputPath string, outputPath string, images []image.Image, pageNum int) error {
// Read the input PDF file.
f, err := os.Open(inputPath)
if err != nil {
return err
}
defer f.Close()
// Create a PDF reader.
pdfReader, err := model.NewPdfReader(f)
if err != nil {
return err
}
// Get the number of pages in the original PDF.
numPages, err := pdfReader.GetNumPages()
if err != nil {
return err
}
// Create a Creator object for the new PDF.
c := creator.New()
ch := c.NewChapter("Charts")
ch.SetMargins(0, 0, 100, 0)
// Load the pages from the original PDF and add images.
for i := 0; i < numPages; i++ {
page, err := pdfReader.GetPage(i + 1)
if err != nil {
return err
}
err = c.AddPage(page)
if err != nil {
return err
}
for _, img := range images {
imgDoc, err := c.NewImageFromGoImage(img)
if err != nil {
fmt.Errorf("Error obtaining the images: %v", err)
}
imgDoc.ScaleToWidth(300)
//imgDoc.SetPos(xPos, yPos)
ch.Add(imgDoc)
}
}
c.Draw(ch)
// Write the new PDF with the images.
err = c.WriteToFile(outputPath)
if err != nil {
fmt.Errorf("Error writing the file: %v", err)
}
return err
}
The above code loads the simple PDF, bar chart PDF, and line graph PDF. It then merges all the pages from each PDF into a new PDF, creating a final document with the charts and graphs integrated.
Conclusion
In this article, we explored how to integrate charts and graphs into PDFs using the Golang PDF library from UniDoc. We started by creating a simple PDF, then generated a bar chart and a line graph with fabricated data. Finally, we learned how to merge these charts and graphs into the original PDF, creating a visually appealing and informative document.
By leveraging the power of Golang and the UniDoc PDF library, developers can enhance their data reporting and presentation capabilities. Whether you’re working on a business report, a data analysis summary, or a dashboard, integrating charts and graphs into your PDFs can make a significant impact on how your information is perceived.