PDF syntax 101: File structure and PDF object types

Table of contents

    PDF syntax 101: File structure and PDF object types
    TL;DR

    PDF syntax describes how objects are written inside a PDF file. A conventional PDF contains a header, a body of objects, cross-reference data, and a trailer. Dictionaries connect pages, fonts, and content streams; indirect references let a reader locate and reuse objects without scanning the entire file.

    Understanding PDF file structure helps when you inspect a damaged file, investigate a rendering issue, or trace a page’s resources. This guide explains the basic object types and how a reader finds them.

    The PDF specification(opens in a new tab) defines the syntax. The examples below illustrate its main concepts; they aren’t a complete PDF writer or validator.

    Introduction to PDF file format

    A PDF stores page content and the information needed to interpret it, including fonts, images, and drawing instructions. Objects connect these resources to the document’s pages. Their order in the file doesn’t have to match the order in which pages are displayed.

    PDF syntax includes readable keywords, but a PDF may also contain compressed or encrypted binary data. Opening a file in a text editor can reveal its structure without making every part readable.

    File structure

    The following example creates a single page containing “Hello PSPDFKit.” Save it as ASCII or UTF-8 without a byte order mark, using carriage return line feed (CRLF) line endings and a final newline after %%EOF. The offsets and stream length below are calculated for those exact bytes; preserve the spaces shown.

    %PDF-1.7
    1 0 obj
    << /Type /Catalog /Pages 2 0 R >>
    endobj
    2 0 obj
    << /Type /Pages /Kids [ 3 0 R ] /Count 1 >>
    endobj
    3 0 obj
    << /Type /Page /Parent 2 0 R /MediaBox [ 0 0 595 842 ] /Resources 4 0 R /Contents 5 0 R >>
    endobj
    4 0 obj
    << /ProcSet[ /PDF /Text ] /Font <</Font1 << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> >> >>
    endobj
    5 0 obj
    << /Length 66 >>
    stream
    BT
    /Font1 35 Tf
    1 0 0 1 170 450 Tm
    (Hello PSPDFKit) Tj
    ET
    endstream
    endobj
    xref
    0 6
    0000000000 65535 f
    0000000010 00000 n
    0000000062 00000 n
    0000000124 00000 n
    0000000233 00000 n
    0000000353 00000 n
    trailer
    << /Root 1 0 R /Size 6 >>
    startxref
    475
    %%EOF

    PDF page showing the text Hello PSPDFKit

    The readable keywords identify objects and sections. A real PDF can also contain compressed content streams, binary images, or encrypted strings that a text editor cannot interpret directly.

    Header and trailer

    In the conventional table-based file structure, each part has a specific role:

    PartRole
    HeaderDeclares a PDF version, such as %PDF-1.7; creator metadata is stored elsewhere.
    BodyContains indirect objects, including page dictionaries and content streams.
    Cross-reference tableRecords object locations and generation numbers, or marks entries as free.
    Trailer dictionaryIdentifies the document catalog through /Root and records other file-level information.
    startxref and %%EOFLocate the final cross-reference section and mark the end of the file revision.

    PDF 1.5 introduced cross-reference streams, which can replace the traditional table and carry trailer entries in their dictionary. A PDF can therefore be valid without a literal xref table. See the PDF Association’s syntax notes(opens in a new tab) for specification details and corrections.

    PDF objects

    PDF defines eight basic object types. The number type includes both integers and real numbers.

    PDF object typeSyntax examplePurpose
    Booleantrue or falseRepresents a logical value.
    Number42 or 3.5Represents an integer or real value.
    String(Hello) or <48656C6C6F>Stores a sequence of bytes in literal or hexadecimal notation.
    Name/TypeIdentifies a named value or dictionary key.
    Array[0 0 595 842]Holds an ordered collection of objects.
    Dictionary<< /Type /Page >>Maps name keys to values.
    StreamA dictionary followed by stream data and endstreamStores bytes, often compressed, such as page instructions or image data.
    NullnullRepresents the null value.

    An object’s type is separate from whether it’s direct or indirect. A direct object appears where it’s used. An indirect object has an object number and generation number and can be referenced elsewhere in the file. Streams must be indirect objects.

    Direct object reference

    Direct objects appear inline where they’re used.

    This resource dictionary contains a direct font dictionary for Helvetica, one of PDF’s standard Type 1 fonts. It illustrates nesting and is a fragment, not a complete PDF file:

    << /ProcSet[ /PDF /Text ] /Font <</Font1<</BaseFont/Helvetica/Subtype/Type1/Type/Font>> >> >>

    Indirect object reference

    An indirect object is defined between obj and endobj, with a positive object number and a generation number. The generation number is commonly zero; it can change when a freed object number is reused.

    A reference such as 3 0 R means “object number three, generation zero.” Object numbers identify objects within the file; they don’t specify page order.

    This abbreviated example shows one dictionary defined as an indirect object and another dictionary referring to it:

    3 0 obj
    <</BaseFont/Helvetica/Subtype/Type1/Type/Font>>
    endobj
    4 0 obj
    << /ProcSet[ /PDF /Text ] /Font <</Font1 3 0 R >> >>
    endobj

    Document catalog and page tree

    The trailer’s /Root entry points to the document catalog, a dictionary with /Type /Catalog. The catalog’s /Pages entry points to the root of the page tree.

    A page-tree node uses /Kids to list child page nodes or other page-tree nodes. A leaf page dictionary has /Type /Page. Its /Contents entry refers to the page’s drawing instructions, and its resources identify items such as fonts and images.

    In the opening example, the catalog refers to object two, which refers to the page in object three. The page then refers to its resources and content stream. A reader follows these references to assemble the page.

    Cross-reference

    A reader locates indirect objects through cross-reference data. In a conventional PDF, it looks near the end for the final startxref entry and follows the byte offset to the cross-reference table or stream.

    That lookup doesn’t mean a viewer reads every byte backward. It uses the index to request the objects it needs. Linearized PDFs additionally arrange data to support displaying the first page while the rest of a document is being transferred.

    In the opening example, with the specified CRLF line endings, the xref keyword begins at byte offset 475:

    startxref
    475

    The matching cross-reference subsection is shown below. Each entry is 20 bytes long, including its CRLF line ending:

    xref
    0 6
    0000000000 65535 f
    0000000010 00000 n
    0000000062 00000 n
    0000000124 00000 n
    0000000233 00000 n
    0000000353 00000 n

    The 0 6 line declares a subsection starting at object zero with six entries. For an in-use entry ending in n, the first field is the object’s byte offset, and the second is its generation number. An entry ending in f is free; its first field participates in a list of free entries rather than pointing to an object body.

    Object zero is reserved and marked free with generation number 65535. It isn’t an empty page or a content object.

    Cross-reference streams and incremental updates

    Cross-reference streams encode the index as stream data. They can identify objects stored inside compressed object streams, so not every entry is a direct byte offset to an object body.

    An incremental update appends changed objects and new cross-reference data to the existing file. The latest trailer can point to the preceding cross-reference section through /Prev. A reader follows that chain to resolve the current objects. The PDF Association glossary(opens in a new tab) explains the related terms.

    Why copied PDF examples may not open

    The opening example uses CRLF line endings so its cross-reference entries have the required width. Saving it with LF line endings, adding a byte order mark, or changing whitespace changes byte positions. If you alter the example, recalculate its offsets and stream length. Font definitions and trailer entries also need to satisfy the specification.

    Use a PDF library to write the file and a validator to check its structure. A viewer may repair a damaged file while opening it, so successful rendering alone doesn’t prove validity. See how to check whether a PDF is valid.

    Learn more

    The object model explains how pages refer to fonts, images, and content streams. Cross-reference data lets a reader find those objects without treating the PDF as a sequential text document.

    For the rendering step, read our guide to the complexities of PDF rendering. The PDF Association’s minimal-file examples(opens in a new tab) provide a useful next step for studying valid serialized files.

    FAQ

    What is the internal structure of a PDF?

    A conventional PDF contains a header, indirect objects, cross-reference data, and a trailer. The document catalog and page tree connect the objects needed to display each page. Newer files may use cross-reference streams instead of a traditional table.

    What are the PDF object types?

    The eight basic PDF object types are Boolean, number, string, name, array, dictionary, stream, and null. Numbers can be integers or real values. These types describe object values; direct and indirect describe how objects are stored and referenced.

    What is an indirect object in a PDF?

    An indirect object has an object number and a generation number. Other objects refer to it using those numbers followed by R. Cross-reference data tells the reader where to find it.

    What does startxref mean in a PDF?

    The number after startxref is a byte offset from the beginning of the file to its cross-reference table or stream. Readers use the final entry to locate the latest cross-reference data.

    Are PDFs read backward?

    A reader may begin near the end to find cross-reference information, but it doesn’t read the whole document backward. It follows references to the objects needed for the requested pages.

    Can a valid PDF have no xref table?

    Yes. PDF 1.5 and later can use a cross-reference stream instead of a text-based xref table. Searching for the literal keyword alone isn’t a validity check.

    Stefan Kieleithner

    Stefan Kieleithner

    iOS Senior Software Engineer

    Stefan began his journey into iOS development in 2013 and has been passionate about it ever since. In his free time, he enjoys playing board and video games, spending time with his cats, and gardening on his balcony.

    Explore related topics

    Try for free Ready to get started?