gopdfrab

package module
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

README

gopdfrab

codecov Go Reference Mentioned in Awesome Go

PDF/A processing for go!

Verify and convert PDF documents with a small, predictable open source library.

Disclaimer

This project is at an early stage and under active development. The API is not final and will change heavily until the first proper release.

Features

  • PDF/A verification
  • PDF/A conversion

Current Progress

PDF/A-1b Validation

PDF/A-1b (ISO 19005-1:2005) verification is implemented and passes both reference test corpora in full:

Clause Area Status
6.1.2 File header
6.1.3 File trailer
6.1.4 Cross-reference table
6.1.5 Document information dictionary
6.1.6 String objects
6.1.7 Stream objects
6.1.8 Indirect objects
6.1.10 Filters
6.1.11 Embedded files
6.1.12 Architectural limits
6.1.13 Optional content
6.2.2 Output intent
6.2.3 Device colour spaces
6.2.4 Image dictionaries
6.2.5–6.2.7 XObjects
6.2.8 ExtGState
6.2.9 Rendering intent
6.2.10 Operators
6.3.2 Font programs
6.3.3 Composite fonts
6.3.4 Font embedding
6.3.5 Font subsets
6.3.6 Font metrics
6.3.7 Font encoding
6.4 Transparency
6.5 Annotations
6.6 Actions
6.7.2 XMP metadata structure
6.7.3 Info/XMP synchronisation
6.7.5 XMP packet header
6.7.8 Extension schemas
6.7.9 XMP well-formedness
6.7.11 PDF/A identifier
6.9 Interactive forms
PDF/A-1b Conversion

PDF/A-1b conversion is fully implemented. See Converting to PDF/A below.

Getting Started

A full example can be found under main/main.go

Add gopdfrab
go get github.com/voidrab/gopdfrab
Import gopdfrab
import (
  "github.com/voidrab/gopdfrab"
)
Initialize a Document
doc, err := gopdfrab.Open(path)
if err != nil {
  log.Fatal(err)
}
PDF/A Validation
v, err := doc.Verify(gopdfrab.PDFA_1B)
if err != nil {
  log.Println(err)
}

if v.Valid {
  fmt.Println("Document is PDF/A-1b compliant")
} else {
  fmt.Println("Document is not PDF/A-1b compliant")
  fmt.Println("Issues:")
  for i, v := range v.Issues {
    fmt.Printf("#%v: %v\n", i+1, v)
  }
}

Finally, close doc.

doc.Close()
Verify a File

Verify opens, verifies, and closes a file.

result, err := gopdfrab.Verify(path, gopdfrab.PDFA_1B)
if err != nil {
    log.Fatal(err)
}
fmt.Println(result.Valid)
Verifying In-Memory Data

VerifyBytes is Verify for an in-memory PDF.

result, err := gopdfrab.VerifyBytes(data, gopdfrab.PDFA_1B)
Verifying Multiple Files

VerifyAll opens, verifies, and closes a batch of files concurrently.

results, err := gopdfrab.VerifyAll(paths, gopdfrab.PDFA_1B)
if err != nil {
    log.Fatal(err)
}
for _, r := range results {
    if r.Err != nil {
        log.Println(r.Path, r.Err)
        continue
    }
    fmt.Println(r.Path, r.Result.Valid)
}
Inspecting Issues

Each PDFError in v.Issues exposes the Check that flagged it, along with its page and underlying messages.

for _, issue := range v.Issues {
    c := issue.Check()
    fmt.Println(c.Clause(), c.Subclause(), c.Name(), c.Description())
    fmt.Println(issue.Page(), issue.Messages())
}

Result has helpers for grouping and summarizing issues:

fmt.Println(v.Summary())          // human-readable report, one line per Check
v.Checks()                        // distinct Checks violated, sorted by clause
v.IssuesByCheck()                 // map[Check][]PDFError
v.IssuesOnPage(1)                 // issues found on page 1 (0 = document-level)
Document Helpers
ok, err := doc.IsPDFA()           // shorthand for Verify(A_1B).Valid

part, level, err := doc.ClaimedConformance() // e.g. "1", "B" — what the file claims, not whether it's valid

xmp, err := doc.XMPMetadata()     // raw XMP packet bytes, decoded to UTF-8
Converting to PDF/A

Convert produces a PDF/A conformant rewrite. It runs pre-emptive fixups, then a verify/fix loop, and rasterizes pages as a last resort when no in-place fixer can repair them.

cr, err := gopdfrab.Convert(path, gopdfrab.PDFA_1B)
if err != nil {
    log.Fatal(err)
}

if err := cr.Save("out.pdf"); err != nil {
    log.Fatal(err)
}

fmt.Println(cr.Iterations)      // how many verify/fixup passes it took
fmt.Println(cr.Result.Valid)    // true if the output is fully PDF/A conformant
Converting an Open Document
cr, err := doc.Convert(gopdfrab.PDFA_1B)
Converting In-Memory Data

ConvertBytes is Convert for an in-memory PDF.

cr, err := gopdfrab.ConvertBytes(data, gopdfrab.PDFA_1B)
Converting Multiple Files

ConvertAll opens, converts, and closes a batch of files concurrently.

results, err := gopdfrab.ConvertAll(paths, gopdfrab.PDFA_1B)
if err != nil {
    log.Fatal(err)
}
for _, r := range results {
    if r.Err != nil {
        log.Println(r.Path, r.Err)
        continue
    }
    fmt.Println(r.Path, r.Result.Result.Valid) // r.Result is a ConvertResult
}
Inspecting Residuals

Even though Convert always returns its best attempt, the result may still carry residual issues if no automatic remediation — including the raster last resort — fully resolved them.

residual := cr.Residual()
for _, iss := range residual {
    check := iss.Check()
    fmt.Println(check.Clause(), check.Name())
    fmt.Println(iss.Page(), iss.Messages())
}

Selective Check Profiles

Verification can be narrowed to a specific set of rules using Verify.

Start from the full profile and remove checks
p := gopdfrab.PDFA_1B.
    RemoveCheck(gopdfrab.Checks.Structure.FileHeaderSignature).
    RemoveCheck(gopdfrab.Checks.Font.SimpleNotEmbedded)

res, err := doc.Verify(p)
Start from an empty profile and add checks
p := gopdfrab.PDFA_1B.Clear().
    AddCheck(
        gopdfrab.Checks.Transparency.ImageWithSoftMask,
        gopdfrab.Checks.Metadata.PDFAIdentifierMissing,
    )

res, err := doc.Verify(p)
Available check groups
Registry field Spec area
Checks.Structure 6.1.x — file header, trailer, xref, object framing, limits
Checks.Colour 6.2.2 OutputIntent, 6.2.3.x device colours, 6.2.9–10
Checks.Image 6.2.4–6.2.7 image/form/PostScript XObjects
Checks.Transparency 6.2.8 transfer functions, 6.4 soft masks/blend modes/alpha
Checks.Font 6.3.x embedding, subsets, metrics, encoding
Checks.Annotation 6.5.x annotation types and dictionaries
Checks.Action 6.6.x action types and additional actions
Checks.Metadata 6.7.x XMP metadata, extension schemas, PDF/A identifier
Checks.Form 6.9 interactive forms

Use gopdfrab.AllChecks() to enumerate all registered checks with their names, descriptions, and clause numbers. gopdfrab.CheckByClause("6.3.4", 1) and gopdfrab.ChecksForClause("6.3.4") look up checks by clause directly.

Performance

gopdfrab's PDF/A-1b verification performance is (unfairly) measured against the Java-based veraPDF and PDFBox Preflight on the combined Isartor + veraPDF corpora (773 files); see benchmarks/README.md for methodology.

Benchmark gopdfrab vs veraPDF gopdfrab vs PDFBox Preflight
Startup time 149x faster 22x faster
Single file (cold, median file) 160x faster 50x faster
Batch throughput 20x faster 15x faster
Batch peak memory 11x smaller 15x smaller
Deployment footprint 9x smaller 3x smaller

Absolute numbers from the same run: the full 773-file batch verifies in 0.29 s (2749 files/s) at 67 MB peak RSS, and a cold single-file verification of the median corpus file takes ~5 ms including process startup.

Due to JVM startup overhead, startup time and cold single-file verification are significantly slower for veraPDF and Preflight.

Isartor Compatibility

The Isartor test suite is the old reference test suite for PDF/A-1b document compatibility before the veraPDF project was initiated. If you require PDF/A-1b compatibility based on Isartor for your application, use the Legacy_1B profile.

Licensing

This work is dual-licensed under GNU AGPL 3.0 and our commercial license. Get in touch for more information about our commercial licensing options.

Contributing

Contributions are welcome! Whether it's a bug report, a failing test file, a new check, or a performance improvement — all of it helps.

  • Bug reports & questions — open an issue.
  • Code changes — fork the repo, make your change on a feature branch, and open a pull request. Please keep pull requests focused: one concern per PR.
  • New checks — if you have a PDF document that contains properties which aren't covered yet, open an issue first so we can agree on the approach before you write the code.
  • Test files — if you have PDFs that expose edge cases or regressions, attach them to an issue.

All contributions are made under the AGPL.

Documentation

Overview

Convert and verify PDF files for PDF/A conformance.

Index

Constants

View Source
const (
	A_1B      = pdf.A_1B
	Undefined = pdf.Undefined
)

PDF/A conformance levels.

Variables

View Source
var (
	// PDFA_1B is the canonical PDF/A-1b profile
	PDFA_1B = pdf.PDFA_1B
	// Legacy_1B is stricter in some areas and compatible with the original Isartor PDF/A-1b test suite.
	Legacy_1B = pdf.Legacy_1B
)

PDF/A profiles.

View Source
var Checks = pdf.Checks

Checks is the registry of every selectable PDF/A check, grouped by area.

Functions

This section is empty.

Types

type Check

type Check = pdf.Check

func AllChecks

func AllChecks() []Check

AllChecks returns every registered check with its name, description, and clause number.

func CheckByClause

func CheckByClause(clause string, subclause int) (Check, bool)

CheckByClause looks up the registered check for a specific (clause, subclause) pair.

func ChecksForClause

func ChecksForClause(clause string) []Check

ChecksForClause returns every registered check under the given clause.

type ConvertResult added in v0.2.0

type ConvertResult = convert.ConvertResult

func Convert added in v0.2.0

func Convert(path string, p *Profile) (ConvertResult, error)

Convert reads the PDF at path and attempts to produce a PDF/A-1b conformant rewrite.

func ConvertBytes added in v0.2.0

func ConvertBytes(data []byte, p *Profile) (ConvertResult, error)

ConvertBytes is Convert for an in-memory PDF.

type Document

type Document struct {
	// contains filtered or unexported fields
}

Document represents an open PDF file.

func Open

func Open(path string) (*Document, error)

Open initializes the PDF document at path.

func (*Document) ClaimedConformance

func (d *Document) ClaimedConformance() (part, conformance string, err error)

ClaimedConformance returns the PDF/A part and conformance level the document's XMP metadata claims, read from the pdfaid namespace. This reflects what the file claims, not whether it actually validates — use Verify or IsPDFA to check actual compliance.

func (*Document) Close

func (d *Document) Close() error

Close ensures the file handle is released.

func (*Document) Convert added in v0.2.0

func (d *Document) Convert(p *Profile) (ConvertResult, error)

Convert converts d, an already-open document, attempting to produce a PDF/A-1b conformant rewrite.

func (*Document) GetMetadata

func (d *Document) GetMetadata() (map[string]string, error)

GetMetadata extracts info from the Info dictionary.

func (*Document) GetPageCount

func (d *Document) GetPageCount() (int, error)

GetPageCount retrieves the page count.

func (*Document) GetVersion

func (d *Document) GetVersion() (string, error)

GetVersion extracts the PDF version from the document header.

func (*Document) IsPDFA

func (d *Document) IsPDFA() (bool, error)

IsPDFA reports whether the document is valid PDF/A-1b. It is equivalent to calling Verify(PDFA_1B) and checking the result's Valid field.

func (*Document) Verify

func (d *Document) Verify(p *Profile) (Result, error)

Verify verifies d against the checks enabled in profile p.

func (*Document) XMPMetadata

func (d *Document) XMPMetadata() ([]byte, error)

XMPMetadata returns the document's raw XMP metadata packet (Root/Metadata), decoded and normalised to UTF-8. It returns an error if the document has no XMP metadata stream.

type FileResult

type FileResult[T any] = pdf.FileResult[T]

func ConvertAll added in v0.2.0

func ConvertAll(paths []string, p *Profile) ([]FileResult[ConvertResult], error)

ConvertAll opens, converts, and closes a batch of files concurrently.

func VerifyAll

func VerifyAll(paths []string, p *Profile) ([]FileResult[Result], error)

VerifyAll opens, verifies, and closes a batch of files concurrently.

type LevelType

type LevelType = pdf.LevelType

type PDFError

type PDFError = pdf.PDFError

type Profile

type Profile = pdf.Profile

func NewProfile

func NewProfile(level LevelType) *Profile

NewProfile returns an empty profile for the given conformance level.

type Result

type Result = pdf.Result

func Verify added in v0.2.0

func Verify(path string, p *Profile) (Result, error)

Verify opens, verifies, and closes a single file.

func VerifyBytes added in v0.3.0

func VerifyBytes(data []byte, p *Profile) (Result, error)

VerifyBytes is Verify for an in-memory PDF.

Directories

Path Synopsis
benchmarks
cmd/gopdfrab-bench command
Command gopdfrab-bench drives the gopdfrab library for benchmarking against other PDF/A verifiers (veraPDF, PDFBox Preflight, a JS validator).
Command gopdfrab-bench drives the gopdfrab library for benchmarking against other PDF/A verifiers (veraPDF, PDFBox Preflight, a JS validator).
internal
convert
Package-level pipeline for converting an arbitrary PDF into a PDF/A-1b rewrite:
Package-level pipeline for converting an arbitrary PDF into a PDF/A-1b rewrite:
pdf
Command wasm is a syscall/js thin wrapper around gopdfrab that exposes VerifyBytes and ConvertBytes as awaitable JavaScript functions registered on the global object.
Command wasm is a syscall/js thin wrapper around gopdfrab that exposes VerifyBytes and ConvertBytes as awaitable JavaScript functions registered on the global object.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL