A PDF file is written in a small text syntax, borrowed from PostScript, with raw bytes allowed inside streams. There are eight types of object. Any object can be written directly where it is used, or as a numbered indirect object that others refer to. In PDF 1.5 to 2.0 an indirect object can also be stored inside an object stream. The syntax is the same in every version from 1.5 to 2.0; PDF 2.0 adds UTF-8 text strings.
White space
Six bytes count as white space. Any run of them is the same as one, except inside strings and stream data, where every byte counts.
| Byte | Name |
|---|---|
00 | Null (NUL) |
09 | Horizontal tab (HT) |
0A | Line feed (LF) |
0C | Form feed (FF) |
0D | Carriage return (CR) |
20 | Space (SP) |
CR, LF and CR LF each end a line. Line ends matter only in a few places: after a
comment, after the stream keyword, and in the entries of a
cross-reference table.
Delimiters
Ten characters end a token without white space:
( ) < > [
] { } / %. So
/Type/Page is two names, and [1 2] needs no space after the
bracket. The braces appear only in PostScript calculator functions. Every other
byte is a regular character, and a run of regular characters is one token: a
number, a keyword such as obj or true, or an operator in a
content stream.
The eight types
| Type | Example |
|---|---|
| Boolean | true, false |
| Number, integer or real | 612, -3.5, .25 |
| String | (Hello), <48656C6C6F> |
| Name | /Type, /A#20B |
| Array | [0 0 612 792] |
| Dictionary | << /Type /Catalog /Pages 2 0 R >> |
| Stream | a dictionary, then stream … endstream |
| Null | null |
Booleans
The keywords true and false, in lower case.
Numbers
An integer is one or more decimal digits with an optional sign: 123,
+17, -98, 0. A real has a decimal point:
34.5, -3.62, +123.6, 4.,
-.002. There is no exponent form, so 6.02E23 is not a
number, and there is no hexadecimal or octal form. Where a real is expected an
integer may be written, and readers turn it into a real. Where an integer is
expected a real is an error, though readers often round it.
Strings
A string is a sequence of bytes, written in one of two ways. Both give the same kind of object; a writer picks whichever is shorter or safer. What the bytes mean depends on where the string is used: text, a date, raw bytes such as an ID or a password hash, or character codes for a font in a content stream.
Literal strings
Bytes between round brackets: (This is a string). Brackets inside
may be left as they are when they balance, (a (b) c), and otherwise
need a backslash. The backslash starts an escape:
| Escape | Byte |
|---|---|
\n | 0A line feed |
\r | 0D carriage return |
\t | 09 horizontal tab |
\b | 08 backspace |
\f | 0C form feed |
\( \) \\ | the bracket or backslash itself |
\ddd | one to three octal digits: \053 is +; high-order overflow is ignored |
\ then a line end | nothing: the string continues on the next line |
A backslash before any other character is dropped. A line end inside the string without a backslash, whether CR, LF or CR LF, is read as a single LF.
Hexadecimal strings
Pairs of hexadecimal digits between angle brackets:
<4E6F762073686D6F7A206B6120706F702E>. Upper and lower case are
both allowed, and white space between digits is ignored. If the number of digits
is odd, the last is taken as followed by 0: <901FA> is the
bytes 90 1F A0.
Names
A name is a slash followed by regular characters: /Type,
/FlateDecode, /1.7. The slash is not part of the name.
Names are case-sensitive, and are compared byte for byte. A name may hold any byte
but NUL by writing it as # and two hexadecimal digits:
/A#20B is “A B”, /#23 is “#”. Bytes outside 21 to 7E, and
delimiters and # itself, must be written that way. The bytes of a name
are best read as UTF-8, though a name is not a text string. / on its
own is a valid, empty name.
Arrays
Objects between square brackets, in any mix of types: [549 3.14 false
(Ralph) /SomeName]. An array may hold arrays. Arrays have one dimension; a
matrix is written as six numbers in one array.
Dictionaries
Pairs of a name key and a value between double angle brackets:
<< /Type /Example
/Version 0.01
/IntegerItem 12
/StringItem (a string)
/Subdictionary << /Item1 0.4 /Item2 true /LastItem (not!) >>
>>
Keys should be unique; when one appears twice, readers differ about which value
they take. A key whose value is null is the same as a key that is not
there. Many dictionaries have a Type key that names what they are, and
often a Subtype.
Streams
A stream is a dictionary followed by any number of bytes. It is always an indirect object, and never in an object stream:
4 0 obj
<< /Length 43 >>
stream
BT /F1 24 Tf 72 712 Td (Hello, world) Tj ET
endstream
endobj
The keyword stream is followed by CR LF or LF, and the data starts
straight after. Length gives the number of bytes of data, as stored, up
to but not including the line end before endstream. Most streams are
compressed. Their dictionary keys and every filter are on
Streams and filters.
Null
The keyword null, an object with no value. A reference to an object
that does not exist, or that is free in the cross-reference, is read as null.
Indirect objects
An object given a number so others can refer to it. Written standing alone in the body, it is:
12 0 obj
(Brillig)
endobj
The object number is a positive integer. The generation is a non-negative integer, 0 for every object of a new file, raised only when a number is freed and used again. The pair is the object’s identifier.
From PDF 1.5 an indirect object with generation 0 may instead be stored in an
object
stream. There it is written bare, without 12 0 obj and
endobj: the object stream’s header gives its number, and its generation
is always 0. Streams, the encryption dictionary and the length of the object stream
itself cannot be stored this way.
References
An indirect object is used by writing its numbers and R:
12 0 R. A reference may appear wherever an object may, as a dictionary
value or an array element. Some values must be direct, such as most entries of the
cross-reference stream’s dictionary, and some must be indirect, such as page
objects and every stream. A reference to a missing object reads as
null.
A stream’s Length may be a reference, so a writer can write the data
before it knows its size: /Length 5 0 R, with object 5 written after
the stream.
Text strings
Strings that people read, such as a title, a bookmark or an annotation’s contents, are text strings. Their bytes are in one of three encodings:
| Encoding | Since | How it is marked |
|---|---|---|
| PDFDocEncoding | 1.0 | No mark: one byte a character, a superset of ISO Latin 1 with some typographic characters in 80 to 9F |
| UTF-16BE | 1.2 | Starts with the byte order mark FE FF |
| UTF-8 | 2.0 | Starts with the byte order mark EF BB BF |
(\376\377\000H\000i) and <FEFF00480069> are both the
UTF-16BE text “Hi”. A UTF-16BE text string may hold an escape sequence that names the
language of what follows: the code unit 00 1B, the two ASCII bytes of
an ISO 639 language code, optionally two more of an ISO 3166 country code, and
00 1B again. A reader of PDF 1.7 or earlier that meets a string starting
EF BB BF shows it as three PDFDocEncoding characters followed by the
UTF-8 bytes.
Dates
A date is a string in the form
(D:YYYYMMDDHHmmSSOHH'mm)
YYYY is the year, MM the month 01 to 12, DD
the day, HH the hour 00 to 23, mm minutes and
SS seconds. O is +, - or
Z, the relation of local time to UT, and HH'mm the offset.
Every field after the year may be left off, with defaults of 01 for month and day and
00 for the rest. (D:20260214123151-08'00') is 14 February 2026, 12:31:51,
eight hours behind UT. Many writers add a closing apostrophe after the minutes of the
offset, as Adobe’s PDF Reference showed it, and readers accept both. The
D: prefix should be there but is sometimes missing.
Rectangles
A rectangle is an array of four numbers, the coordinates of two opposite corners:
usually [llx lly urx ury], lower left then upper right, in default user
space units of 1/72 inch. [0 0 612 792] is a US Letter page and
[0 0 595 842] about A4. Readers put the corners in order themselves if a
writer gives them the other way round.
Name trees and number trees
Two kinds of dictionary hold large sorted maps. A name tree maps strings to values,
a number tree integers to values. The root has Kids (references to
intermediate nodes) or Names (for number trees, Nums): an
array of keys and values, alternating, in sorted order. Intermediate and leaf nodes
carry Limits, the smallest and largest key below them, so a reader can
find a key without reading the whole tree. The trees the document uses are on
Document
structure.
Limits
The format sets few limits of its own. Annex C of ISO 32000-1 lists the limits of Acrobat on 32-bit machines, as advice for writers who want files to open everywhere:
| Quantity | Limit |
|---|---|
| Integer | −2,147,483,648 to 2,147,483,647 |
| Real | about ±3.403 × 1038, with about five significant decimal digits of precision in the fraction |
| String | 32,767 bytes (in a content stream) |
| Name | 127 bytes |
| Indirect objects in a file | 8,388,607 |
q nesting | 28 |
| DeviceN colourants | 32 |
These are advice, not rules of the format. Many readers today handle more, but files that stay within these numbers open in old readers too.
Comments
A
%outside a string or a stream starts a comment that runs to the end of the line. It counts as one white-space character. Two comments have a meaning to readers: the%PDF-header and the%%EOFmarker.