If you are using only regex, it is easy to construct a PDF file that your program will not be able to handle. PDF dictionaries and lists can contain other objects. Regex can't handle recursive structures, at least not Python re module.
A pdf file is a tree of objects and streams:
- Dictionaries: 
<< (name value)* >> 
- Lists: 
[ (value)* ] 
- Names: 
/ (regular char)* 
- Strings: 
( (char)* ) 
- Hex strings: 
< (hexchar)* > 
- Numbers: (
-)? ((digit)+ | (digit)+ . (digit)* | . (digit)+) 
- Booleans: 
true | false 
- References: (digit)+ (whitespace)+ (digit)+ (whitespace)+ 
R 
Whitespace and comments are ignored in most places.
Comments start with % and run until the end of the line.
Indirect objects are specified as:
1 0 obj
(any object)
endobj
This object can then be referenced as 1 0 R. Indirect dictionaries can also have a stream attached:
1 0 obj
<<
/Length 22
>>
stream
(22 bytes of raw data)
endstream
endobj
A PDF file looks something like this:
%PDF-1.4
%ÿÿÿÿ
1 0 obj
<< /Author (MizardX) >>
endobj
2 0 obj
<<
/Type /Catalog
% more required keys
>>
endobj
%lots of more indirect objects, one after another
trailer
<<
/Info 1 0 R
/Root 2 0 R
% ... more required keys
>>
xref
0 3
0000000000 65535 f
0000000015 00000 n
0000000054 00000 n
startxref
225
%%EOF
The root of the object tree is the trailer object. Every objects is referenced directly or indirectly from this dictionary.
There are a lot more complexity hidden inside the streams, but that does not affect the file structure.
The full specification can be found at Adobe's website.