PDF/A in Go
Go has good libraries for creating and manipulating PDFs. However, it offers very little in the way of tools for determining whether a document conforms to ISO 19005 — a different and more challenging problem.
Three jobs that are often confused
The generation process creates a document from your data. As you control every object you emit, the hard part is the layout rather than the parsing. Manipulation involves splitting, merging, stamping or optimising an existing file: it requires a real parser, but only needs to understand the objects it affects.
Conformance determines whether an arbitrary file meets a given standard. It must understand everything in the file, including parts that are not relevant to anything else, and interpret them correctly. The Go PDF library comparison maps the ecosystem onto these three categories.
Why conformance is hard
A generator only ever encounters files that it has created itself. In contrast, a validator encounters files produced by an outdated scanner, truncated by a failed upload, encrypted with an obsolete cipher, or crafted to crash the parser. The validator has to open them anyway because 'this file is broken' is a result that the caller needs. It also has to do this without being taken down by a decompression bomb.
Then there is the standard itself. ISO 19005-1 is not a checklist. Clauses interact and several are ambiguous; the de facto arbiter of these is whatever the reference implementation does. This is why gopdfrab's default profile deliberately aligns with veraPDF's interpretation, and a separate, stricter legacy profile is provided rather than assuming one reading is obviously correct.
Verifying from Go
The entire API process involves opening a document, running a profile and reading the issues.
// Verify opens, verifies, and closes a file in one call
result, err := gopdfrab.Verify(path, gopdfrab.PDFA1B)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Valid)Each issue carries the clause from which it originated, the check name, the page number, and a human-readable message. See diagnostics for the full list, and the error reference for details of individual failures.
Converting from Go
cr, err := gopdfrab.Convert(path, gopdfrab.PDFA1B)
if err != nil {
log.Fatal(err)
}
defer cr.Close() // releases the output (a large one spills to a temp file)
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 conformantConversion is best-effort by nature. Some documents cannot be made conformant without changing what they look like. gopdfrab reports what it could not fix as residual issues, and CheckFidelity renders both sides so you can see what changed.
Advantages of a pure Go solution
Validation occurs within the same goroutine as the request, providing typed errors rather than the need to parse another tool's output. There is no runtime to deploy alongside the binary, nor anything to warm up before the first byte is read. This makes per-request validation viable. The same code can be cross-compiled to WebAssembly, which is how the validator on this site runs entirely in your browser.
Frequently asked
Can I validate PDF/A from Go without shelling out to veraPDF?
Yes, gopdfrab is a pure Go library. Import it, call Verify, and read the issues. There is no subprocess and no CGo, so it cross-compiles anywhere Go does, including GOOS=js GOARCH=wasm, which is what runs the validator on this site.
Why is validating a PDF harder than writing one?
Creating a PDF involves producing a structure that you control. Validating one, however, means parsing a file produced by someone else that may be damaged, encrypted or even malicious, and then answering a few hundred questions about its conformance. The parser must be able to handle input that no writer would ever produce.
Does Go's standard library have anything for PDF?
No. There is no PDF support in the standard library, which is why the ecosystem is a set of independent third-party libraries with quite different goals.
How do I convert a PDF to PDF/A in Go?
Call gopdfrab.Convert with the target profile. It applies pre-emptive fixups, then runs a verify/fix loop, and rasterizes as a last resort. Anything that could not be fixed is reported as a residual issue, rather than being silently passed as valid.