Flatten PDF Forms

This guide will show you how to flatten form fields in a PDF document.

Form flattening is the process of rendering dynamic form fields into static fields in the PDF document, just like any other text or image in the document. When a PDF document is flattened, its annotations will be removed; the document can no longer be edited.

You can also partially flatten form fields and non-URL annotations in PDF forms with UniPDF.

Before you begin

You should get your API key from your UniCloud account.

If you are using the UniPDF SDK for the first time, follow this guide to set up a local development environment.

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

Navigate to the forms folder in the unipdf-examples directory.

cd unipdf-examples/forms

How it works

/*
* Flatten form data in PDF files, moving to content stream from annotations, so cannot be edited.
* Note: Works for forms that have been filled in an editor and have the appearance streams generated.
*
* Run as: go run pdf_form_flatten.go <outputdir> <pdf files...>
*/
package main
import (
"fmt"
"os"
"path/filepath"
"sort"
"github.com/unidoc/unipdf/v3/annotator"
"github.com/unidoc/unipdf/v3/common/license"
"github.com/unidoc/unipdf/v3/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() {
if len(os.Args) < 3 {
fmt.Printf("Usage: go run pdf_form_flatten.go <outputdir> <input1.pdf> [input2.pdf] ...\n")
os.Exit(1)
}
outputDir := os.Args[1]
fails := map[string]string{}
failKeys := []string{}
processed := 0
for i := 2; i < len(os.Args); i++ {
inputPath := os.Args[i]
name := filepath.Base(inputPath)
outputPath := filepath.Join(outputDir, fmt.Sprintf("flattened_%s", name))
err := flattenPdf(inputPath, outputPath)
if err != nil {
fmt.Printf("%s - Error: %v\n", inputPath, err)
fails[inputPath] = err.Error()
failKeys = append(failKeys, inputPath)
}
processed++
}
fmt.Printf("Total %d processed / %d failures\n", processed, len(failKeys))
sort.Strings(failKeys)
for _, k := range failKeys {
fmt.Printf("%s: %v\n", k, fails[k])
}
}
// flattenPdf flattens annotations and forms moving the appearance stream to the page contents so cannot be
// modified.
func flattenPdf(inputPath, outputPath string) error {
f, err := os.Open(inputPath)
if err != nil {
return err
}
defer f.Close()
pdfReader, err := model.NewPdfReader(f)
if err != nil {
return err
}
fieldAppearance := annotator.FieldAppearance{OnlyIfMissing: true}
err = pdfReader.FlattenFields(true, fieldAppearance)
if err != nil {
return err
}
// AcroForm field is no longer needed.
opt := &model.ReaderToWriterOpts{
SkipAcroForm: true,
}
// Generate a PdfWriter instance from existing PdfReader.
pdfWriter, err := pdfReader.ToWriter(opt)
if err != nil {
return err
}
// Write to file.
err = pdfWriter.WriteToFile(outputPath)
return err
}

Lines 10–19 import the UniPDF packages and other required dependencies.

The init function in lines 21-28 authenticates your request with your UNIDOC_LICENSE_API_KEY.

The main function in lines 30–60 validates your input and passes the inputPathand outputPath as arguments to the flattenPdf function.

The flattenPdf function in lines 64–96 reads the input PDF and flatten its annotations and form fields such that the PDF document cannot be modified.

Run the code

Run this command to flatten form fields in a PDF document. This will also install the required dependencies to run the program.

Sample output

The output PDF document is flattened and cannot be modified.

Got any Questions?

We're here to help you.