All formats

pdfcpu

LanguageGo
LicenseApache-2.0
Repositorygithub.com/pdfcpu/pdfcpu
Documentationpdfcpu.io, API on pkg.go.dev
Depends onnothing outside Go (pure Go, no cgo)
Read / writeyes / yes: validate, optimize, merge, split, rotate, trim, encrypt, watermark, stamp, forms, attachments, images, fonts; no text extraction or rendering

pdfcpu processes existing PDF files in pure Go. The api package has one function per operation, usually in two forms: XxxFile(ctx, inFile, outFile, ...) working on paths and Xxx(ctx, rs io.ReadSeeker, w io.Writer, ...) working on streams. The same operations are available as the pdfcpu command-line tool. It validates files against the spec, optimizes them (removes duplicate objects, writes object streams), handles AES-256 encryption, adds text, image and PDF watermarks and stamps, and fills, locks and exports AcroForms through JSON.

pdfcpu doesn’t extract text or render pages; use ledongthuc/pdf or go-fitz for that. Its JSON-based create command builds simple documents, but it isn’t a layout library.

In current versions every api function takes a context.Context as the first argument. Pages are numbered from 1; page selections are strings like "1-3", "5", "even", "!2" (all except page 2). Passing nil as the configuration uses the defaults.

Installation#

Shell
go get github.com/pdfcpu/pdfcpu@latest
# command-line tool
go install github.com/pdfcpu/pdfcpu/cmd/pdfcpu@latest

Basic usage: merge PDF files#

Go
package main

import (
	"context"
	"log"

	"github.com/pdfcpu/pdfcpu/pkg/api"
)

func main() {
	ctx := context.Background()
	inFiles := []string{"a.pdf", "b.pdf", "c.pdf"}
	// false: no blank divider page between merged files
	if err := api.MergeCreateFile(ctx, inFiles, "merged.pdf", false, nil); err != nil {
		log.Fatal(err)
	}
	n, err := api.PageCountFile(ctx, "merged.pdf")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("merged.pdf has %d pages", n)
}

Command line: pdfcpu merge merged.pdf a.pdf b.pdf c.pdf.

How to split a PDF#

Go
// one file per page, written to the existing directory "out"
err := api.SplitFile(ctx, "in.pdf", "out", 1, nil)

// split before pages 3 and 7: pages 1-2, 3-6, 7-end
err = api.SplitByPageNrFile(ctx, "in.pdf", "out", []int{3, 7}, nil)

// copy selected pages into a new file
err = api.CollectFile(ctx, "in.pdf", "selection.pdf", []string{"1-3", "5"}, nil)

How to rotate pages#

Go
// rotate pages 1-3 by 90 degrees clockwise; nil selects all pages
err := api.RotateFile(ctx, "in.pdf", "rotated.pdf", 90, []string{"1-3"}, nil)

How to encrypt and decrypt a PDF#

Go
import "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"

conf := model.NewAESConfiguration("user", "owner", 256)
if err := api.EncryptFile(ctx, "in.pdf", "encrypted.pdf", conf); err != nil {
	log.Fatal(err)
}

dconf := model.NewDefaultConfiguration()
dconf.OwnerPW = "owner"
if err := api.DecryptFile(ctx, "encrypted.pdf", "decrypted.pdf", dconf); err != nil {
	log.Fatal(err)
}

How to read PDF metadata#

Go
import "os"

f, err := os.Open("in.pdf")
if err != nil {
	log.Fatal(err)
}
defer f.Close()

info, err := api.PDFInfo(ctx, f, "in.pdf", nil, false, nil)
if err != nil {
	log.Fatal(err)
}
log.Printf("PDF %s, %d pages, title %q, author %q, producer %q, encrypted %v, tagged %v",
	info.Version, info.PageCount, info.Title, info.Author, info.Producer, info.Encrypted, info.Tagged)

Command line: pdfcpu info in.pdf.

How to add a text watermark#

Go
// description string: font, size, position, opacity, rotation, ...
err := api.AddTextWatermarksFile(ctx, "in.pdf", "watermarked.pdf", nil, true,
	"CONFIDENTIAL", "font:Helvetica, points:48, rot:45, op:0.3, fillc:.8 0 0", nil)

onTop true makes a stamp (over the content); false makes a watermark (behind it).

How to fill a PDF form#

Export the form to JSON, edit the values, and fill:

Go
// writes the fields and their current values
err := api.ExportFormFile(ctx, "form.pdf", "form.json", nil)

// ... edit "value" entries in form.json ...

err = api.FillFormFile(ctx, "form.pdf", "form.json", "filled.pdf", nil)

api.ListFormFields lists field names; api.LockFormFieldsFile makes fields read-only.

Tips#