PDF is not a document format in the way Word or RTF is. Those formats store a sequence of content that a renderer interprets at the moment of display, so the output depends on whatever fonts and layout engine happen to be present. PDF stores the result of that process: precise rendering instructions, font programs, compressed image streams, and an object graph that ties them together into a self-contained description of every page. The file carries enough information to reproduce each page identically on any conforming renderer, which is both its main design goal and the source of most of the complexity you encounter when you try to generate, parse, or modify one programmatically
The object model
Every PDF is a collection of numbered objects. An object can be a boolean, integer, real number, name, string, array, dictionary, stream, or null. Almost everything interesting is a dictionary, which is a set of key-value pairs where keys are names and values are any other object type, including references to other objects by number and generation count. A stream is a dictionary followed by a byte sequence, typically compressed
The catalog dictionary is the root. It points to the page tree, which organizes the page dictionaries in a balanced tree structure rather than a flat list, so navigating to page 5,000 of a 10,000-page document does not require traversing every preceding page descriptor. Each page dictionary references its content streams (one or more sequences of page-description operators), its resource dictionary (which in turn references font descriptors, color spaces, and image XObjects), and its media box (the coordinate space the page lives in). The coordinate origin is at the bottom-left corner, with positive Y running upward, in units of 1/72 inch
At the end of the file sits the cross-reference table, which maps each object number to its byte offset in the file. This is what enables random access: a viewer reads the cross-reference table first, then seeks directly to whichever objects it needs. PDF 1.5 introduced cross-reference streams, which compress the table into a stream object and pack related objects into object streams, reducing file size noticeably for documents with many small objects
Content streams and the graphics model
A page’s visual content lives in one or more content streams. Each stream is a sequence of PDF operators interspersed with their operands. The text operator BT begins a text object, Tf selects a font and size from the resource dictionary, Td positions the text cursor, Tj or TJ paints a string, and ET closes the text object. Vector graphics follow a similar pattern: m sets a path starting point, l appends a line segment, c appends a Bezier curve, and f or S fills or strokes the path
The graphics state governs everything that happens between operators: current transformation matrix, line width, color space, fill color, stroke color, and the clipping path. Operators like q and Q push and pop the graphics state onto a stack, which is how PDF implements local coordinate transformations and temporary state overrides without affecting the context around them. Form XObjects generalize this: a self-contained content stream with its own resource dictionary that can be painted onto a page at arbitrary positions and scales with a single Do operator
Font embedding and text extraction
PDF can reference fonts by name and rely on the viewer to substitute something, but in practice any document you intend to share must embed the font data. A Type 1 or TrueType/OpenType font embedded in a PDF carries a font descriptor dictionary pointing to a font file stream. For TrueType fonts, that stream contains the binary font program; for Type 1, it is the PFB data. Subsetting, which is what every serious PDF generator does, strips out glyphs not referenced by the document, keeping file sizes manageable even for large Unicode fonts
Text extraction is where font embedding bites back. The visual representation of a character is determined by a glyph in the embedded font program. The Unicode value of that character is determined by a ToUnicode CMap stream attached to the font dictionary. When the ToUnicode CMap is missing or incorrect, a PDF viewer can render text legibly but cannot extract it as meaningful Unicode, which is why copy-paste from some PDFs produces garbage. Tagged PDF (ISO 32000 §14.8) adds a second layer: a logical structure tree that maps page content to document-semantic roles like paragraphs, headings, and table cells. Screen readers and reflow engines use the structure tree rather than the raw content stream order, which explains why a visually well-laid-out PDF can still be inaccessible if the tagging is absent or wrong
Incremental updates and digital signatures
When you save changes to an existing PDF without rewriting it from scratch, the new objects are appended after the original file body along with a new cross-reference section and a new trailer dictionary. The updated trailer points to the new cross-reference data, and superseded objects remain in the file but are simply not referenced by the new cross-reference chain. This is incremental update, and it has two significant consequences
First, the file grows with every save cycle. A document edited and saved repeatedly accumulates layers of obsolete objects. Tools like QPDF can linearize or compress-and-rewrite a file to reclaim that space, but the default is accumulation. Second, digital signatures depend on incremental updates for their integrity model. An ISO 32000 signature covers a byte range of the file, typically everything except the placeholder for the signature value itself. Any post-signing changes that appear as additional incremental updates are visible to a validating reader as modifications made after signing, which is exactly the audit trail you want. However, this also means that certain modifications, such as adding an approval signature or filling in form fields, are explicitly permitted by the standard without invalidating the original signature, provided the changes conform to the document’s permission settings (ISO 32000-2 §12.7.6). A modification that falls outside those permissions is flagged as unauthorized. Getting this distinction right matters when you are generating documents that will be countersigned downstream
Conformance levels and the ISO 32000 lineage
PDF started as a proprietary Adobe format in 1993, absorbed PostScript’s imaging model, and over fifteen versions accumulated features: encryption in 1.1, interactive forms in 1.2, digital signatures and logical structure in 1.3, transparency in 1.4, object streams in 1.5, AES encryption in 1.6. Adobe submitted PDF 1.7 to ISO in 2007, and ISO 32000-1:2008 was the result. ISO 32000-2:2020 covers PDF 2.0, which tightened several underspecified areas, revised the AES-256 key derivation (revision 6 replacing revision 5), and added explicit support for associated files and rich media
The substandards derive from the same base. PDF/A (ISO 19005) trades features for archival stability: no encryption, no external content dependencies, all fonts embedded, color spaces device-independent, XMP metadata required. PDF/A-1 is based on PDF 1.4, PDF/A-2 on PDF 1.7, PDF/A-3 allows embedded files of any format. PDF/X (ISO 15930) is the print production subset: output intents, bleed and trim boxes, no transparency in older conformance levels. PDF/UA (ISO 14289) mandates tagged structure, Unicode mappings, and language metadata for accessibility. These are not competing formats; they are sets of additional constraints on top of core PDF, and a single file can conform to more than one simultaneously provided the constraints do not conflict
For anyone writing code that generates or processes PDF, the practical baseline is ISO 32000-2 with careful attention to the sections covering the cross-reference model (§7.5), the graphics state (§8.4), text state operators (§9.3), font descriptors and ToUnicode (§9.6 and §9.10), interactive forms (§12.7), and digital signatures (§12.8). The standard is long, but most programmatic PDF work touches a narrow slice of it repeatedly. Understanding the object model and the cross-reference mechanism is the entry point; everything else is specialization from there