Analysis
A PDF file is a set of numbered objects plus a cross reference table mapping each number to a byte offset. Everything else - pages, fonts, images, annotations - is a dictionary somewhere in that set, referring to other objects by number. The guides in this section are about reading that structure rather than the document it describes, which is what you do when a file renders wrong, when you want to know whether a document is worth running extraction on, or when you are auditing a corpus.
Two layers are available, and picking the wrong one is the usual source of frustration.
| Layer | What you get | Use it for |
|---|---|---|
model.PdfReader | Pages, fonts, annotations and outlines as Go structs, plus Inspect, PrintPdfObject and PrintPdfObjects for debugging. | Almost everything. Start here. |
core primitives | The raw object graph: PdfObjectDictionary, PdfObjectArray, PdfObjectStream, PdfIndirectObject, PdfObjectReference. | Keys the model does not expose, malformed files, and anything you need to read byte for byte. |
Reach for core when the model has no accessor for what you want. A page dictionary
entry that UniPDF does not model is still reachable through
page.GetPageDict().Get("SomeKey"), and the reader hands you any numbered object with
GetIndirectObjectByNumber.
Working with core objects
Every core object satisfies the PdfObject interface:
type PdfObject interface {
String() string // debugging representation
Write() []byte // the object as written to file
Direct() PdfObject // dereferences references and indirect objects
Equals(other PdfObject) bool // deep comparison
}Direct() and Equals() are new in v5 and are equivalent to the free functions
core.TraceToDirectObject(obj) and core.EqualObjects(obj, other), which still work.
Write() returns the serialized bytes; in v4 the method was WriteString() and
returned a string. See the v5 migration guide if you
implement PdfObject yourself.
Reading a value out of the graph means resolving a reference and then type asserting.
The core.Get* helpers do both:
dict := page.GetPageDict()
if name, ok := core.GetNameVal(dict.Get("Type")); ok {
fmt.Println(name) // Page
}
if arr, ok := core.GetArray(dict.Get("MediaBox")); ok {
fmt.Println(arr.Len()) // 4
}GetBool, GetInt, GetFloat, GetString, GetName, GetArray and GetDict all
trace through indirect objects before asserting, and each has a *Val variant that
returns the Go value instead of the wrapper. GetStream and GetIndirect are the
exceptions: they resolve a reference but do not trace deeper, because a stream or an
indirect object is the thing you asked for rather than something to unwrap.
Stream contents are compressed on disk. core.DecodeStream(stream) applies the
stream’s filters and hands back the decoded bytes.
Lazy loading and encryption
The two reader constructors used across these examples differ in a way that matters for
large files. model.NewPdfReaderFromFile(path, nil) loads lazily, resolving objects
from disk on demand. model.NewPdfReader(f) loads the whole object graph into memory up
front.
They also differ on encrypted documents. NewPdfReaderFromFile tries the password from
ReaderOpts, empty by default, and returns an error if it does not authenticate, so a
password protected file never yields a reader. NewPdfReader does not attempt
decryption at all: it returns a reader whose structure is not loaded, and the following
GetNumPages fails with “file need to be decrypted first” until you call Decrypt.
Get PDF info works through that case.
Where to look
| Guide | Covers |
|---|---|
| Get PDF info | Page count and encryption status, and handling documents that need a password. |
| Inspect PDF objects | Counting object types, and screening for JavaScript and rich media. |
| Detect scanned PDF document | Deciding whether a document has a text layer before extracting from it. |
| Get all PDF objects | Dumping every object in the file, decoded streams included. |
| Get PDF object | Printing one object by number, or the trailer. |
| Print content streams | Reading a page’s content stream and its operator list. |
| Summarize images | Building an inventory of image filters, colorspaces and sizes across many files. |
For reading the document rather than its structure, see extraction. For title, author and the rest of the document information dictionary, see metadata.