To show how to read a PDF file, I have created a step by step guide to reading very ‘simple’ PDF (it just shows Hello World). You can download the PDF here.
1. Read the last 1024 bytes of the file
There will be a value startxref followed by a number. This is the binary offset to the Cross Reference table.
2. Read the object offsets
The Cross reference table tells us that there are 16 objects. Object 0 is unused and the final object is 15. Object one is found at byte offset 285, Object 2 at 3440, etc. You can use this information to read in any object.
0000000000 65535 f
0000000285 00000 n
0000003440 00000 n
0000000022 00000 n
0000000395 00000 n
0000003204 00000 n
0000003673 00000 n
0000003631 00000 n
0000000492 00000 n
0000003292 00000 n
0000003239 00000 n
0000003369 00000 n
0000003529 00000 n
0000004040 00000 n
0000004306 00000 n
0000006138 00000 n
3. Read the trailer
The trailer is immediately after the cross reference table. It is a Dictionary so all keys start with a / (/Size, /Root, /Info, /ID). Values can be a number, an object (found using the references above) or various data values.
The actual PDF data structure is a binary tree, which we parse to access any part of the document. It starts with the Root Node (Object 12).
The PDF file also has an information Object (Object 15) which contains metadata about the file (information about Author, tools used to create, date, etc).
ID is two strings (stored as Hex values) which you will need if the file is encrypted.
<</Size 16 /Root 12 0 R /Info 15 0 R /ID [ <ee0351b21bd521fdd345ea49d40844bb>
<ee0351b21bd521fdd345ea49d40844bb> ] >>
4. Read the Root Object
The Root object is object 12, which starts at byte offset 3529 from the start of the file. This is another Dictionary Object (it is the Catalog). It tells us that we can find the Page tree defined in Object 2. This PDF has some structure information (if we wanted to extract structured content (which is defined in Object 10).
12 0 obj
<< /Type /Catalog /Pages 2 0 R /MarkInfo << /Marked true >> /StructTreeRoot
10 0 R >>
5. Read the Pages Object
The Pages object is object 2, which gives us the page dimensions (those values are A4). It tells us there is 1 Page defined as Object 1
2 0 obj
<< /Type /Pages /MediaBox [0 0 595.28 841.89] /Count 1 /Kids [ 1 0 R ] >>
6. Read the Page Object
The Page object is object 1 and also gives us the page dimensions (those values are A4). If not, it would inherit the value defined in the parent Pages Object. The page is drawn using the PostScript stream command defined in Contents (Object 3) with the fonts, colors and images from Resources (Object 4).
The data in object 3 is compressed binary data, which needs to be read as binary data and decompressed.
Explaining how all this data is parsed is way beyond the scope of this tutorial!
3 0 obj
<< /Filter /FlateDecode /Length 191 >>
���H��&a���#O8"�`:E��W]7�a����}i |e*)��c6���P� 6H�4[(P�������a�
1 0 obj
<< /Type /Page /Parent 2 0 R /Resources 4 0 R /Contents 3 0 R /MediaBox [0 0 595.28 841.89]
4 0 obj
<< /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 5 0 R >> /Font << /TT1 6 0 R
PDF files have a very complex structure (we have been writing our PDF parser since 2001 and still tweaking it). So our advice is always to use a library (there are lots of Open Source and commercial ones out there including our JPedal library) for anything other than basic access. If you want to view or rasterize a PDF file, you will definitely need a third party-library.
Are you a Developer working with PDF files?
|Free: The Developer's Guide to PDF|
|Convert PDF files to HTML|
|Use PDF Forms in a web browser|
|Convert PDF Documents to an image|
|Work with PDF Documents in Java|