Το Portable Document Format (PDF) είναι θεμελιωδώς μια βάση δεδομένων αντικειμένων (object-database) μεταμφιεσμένη σε μορφή εγγράφου. Η κατανόηση του πώς συντίθεται ένα αρχείο PDF είναι απαραίτητη για κάθε προγραμματιστή που γράφει λογισμικό το οποίο δημιουργεί, αναλύει ή χειρίζεται αυτά τα αρχεία. Σε αντίθεση με τις απλές μορφές κειμένου ή τις γραμμικές ροές (linear streams) όπως η HTML, ένα PDF επιτρέπει την τυχαία πρόσβαση στα στοιχεία του, επιτρέποντας την ταχεία απόδοση (rendering) συγκεκριμένων σελίδων χωρίς τη φόρτωση ολόκληρου του αρχείου στη μνήμη
Αυτή η δυνατότητα τυχαίας πρόσβασης υπαγορεύει ολόκληρη τη φυσική δομή του αρχείου. Σε μακροσκοπικό επίπεδο, ένα τυπικό (μη γραμμικοποιημένο - non-linearized) PDF χωρίζεται σε τέσσερις διακριτές ενότητες: την Κεφαλίδα Αρχείου (File Header), το Σώμα (Body), τον Πίνακα Cross-Reference (Cross-Reference Table) και το Trailer
1. Η Κεφαλίδα Αρχείου (File Header)
Κάθε PDF ξεκινά με μια υπογραφή που προσδιορίζει τη μορφή του αρχείου και την έκδοση των προδιαγραφών που προτίθεται να ακολουθήσει. Αυτή είναι πάντα η πρώτη γραμμή του αρχείου, ξεκινώντας από το byte 0. Παίρνει τη μορφή:
%PDF-1.7
% Σημείωση: Ο αριθμός έκδοσης μπορεί να παρακαμφθεί από το λεξικό Catalog
Αμέσως μετά από αυτήν τη γραμμή, θα βρείτε σχεδόν πάντα μια δεύτερη γραμμή σχολίου που περιέχει τουλάχιστον τέσσερις δυαδικούς χαρακτήρες (byte με τιμές μεγαλύτερες από 127). Για παράδειγμα: %âãÏÓ. Αυτή η μη-ASCII ακολουθία διασφαλίζει ότι τα προγράμματα μεταφοράς αρχείων (όπως οι FTP clients σε λειτουργία ASCII) ή τα συστήματα ελέγχου εκδόσεων (version control systems) αναγνωρίζουν το PDF ως δυαδικό (binary) αρχείο αντί για απλό κείμενο, αποτρέποντας τις καταστροφικές μετατροπές carriage-return/line-feed
2. Το Σώμα (Body)
Το σώμα περιέχει τα πραγματικά δεδομένα του εγγράφου PDF. Αποτελείται από μια ακολουθία "έμμεσων αντικειμένων" (indirect objects). Τα πάντα στο PDF — γραμματοσειρές, εικόνες, περιγραφές σελίδων, σελιδοδείκτες (bookmarks), σχολιασμοί (annotations) — αναπαρίστανται ως αντικείμενο
Κάθε έμμεσο αντικείμενο έχει ένα μοναδικό αναγνωριστικό (identifier) που αποτελείται από δύο αριθμούς: έναν Αριθμό Αντικειμένου (ένας θετικός ακέραιος) και έναν Αριθμό Γενιάς (συνήθως 0). Το αντικείμενο οριοθετείται από τις λέξεις-κλειδιά obj και endobj
% Αντικείμενο 3, Γενιά 0
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/Resources 4 0 R
/Contents 5 0 R
>>
endobj
Σε αυτό το παράδειγμα, το αντικείμενο 3 είναι ένα αντικείμενο Dictionary (λεξικό) (περικλείεται σε << και >>) που ορίζει μια σελίδα. Αναφέρεται σε άλλα αντικείμενα χρησιμοποιώντας τη λέξη-κλειδί R. Για παράδειγμα, το 4 0 R είναι μια αναφορά στο Αντικείμενο 4, Γενιά 0. Αυτός ο ιστός (web) αναφορών αντικειμένων σχηματίζει ένα κατευθυνόμενο γράφημα (directed graph), με το Document Catalog (Κατάλογος Εγγράφου) στη ρίζα
3. Ο Πίνακας Cross-Reference (xref)
Αυτός είναι ο μηχανισμός που επιτρέπει την τυχαία πρόσβαση. Αντί να διαβάσει ολόκληρο το αρχείο για να βρει πού βρίσκεται το Αντικείμενο 3, ένας αναλυτής PDF αναζητά το Αντικείμενο 3 στον πίνακα cross-reference (διασταυρούμενης αναφοράς), ο οποίος παρέχει το ακριβές byte offset αυτού του αντικειμένου από την αρχή του αρχείου
Ο πίνακας ξεκινά με τη λέξη-κλειδί xref. Ακολουθούν οι υποενότητες (subsections). Μια υποενότητα ξεκινά με δύο αριθμούς: τον αρχικό αριθμό αντικειμένου και τον αριθμό των καταχωρήσεων σε αυτήν την υποενότητα. Κάθε καταχώρηση έχει μήκος ακριβώς 20 byte (συμπεριλαμβανομένου του τέλους γραμμής CRLF), καθιστώντας τετριμμένη την αναζήτηση (seek) στην καταχώρηση για οποιονδήποτε δεδομένο αριθμό αντικειμένου
xref
0 6 % Ξεκινά στο αντικείμενο 0, περιέχει 6 καταχωρήσεις
0000000000 65535 f % Το αντικείμενο 0 είναι πάντα η ειδική ελεύθερη καταχώρηση (free entry)
0000000010 00000 n % Το αντικείμενο 1 βρίσκεται στο byte offset 10, σε χρήση (n)
0000000059 00000 n % Το αντικείμενο 2 βρίσκεται στο byte offset 59, σε χρήση (n)
0000000124 00000 n % Το αντικείμενο 3 βρίσκεται στο byte offset 124, σε χρήση (n)
0000000285 00000 n % Το αντικείμενο 4 βρίσκεται στο byte offset 285, σε χρήση (n)
0000000352 00000 n % Το αντικείμενο 5 βρίσκεται στο byte offset 352, σε χρήση (n)
Εάν ένα PDF ενημερωθεί (π.χ., ένας χρήστης συμπληρώνει μια φόρμα ή προσθέτει έναν σχολιασμό), η τυπική αποθήκευση προσαρτά τα νέα/τροποποιημένα αντικείμενα στο τέλος του αρχείου, ακολουθούμενα από μια νέα ενότητα cross-reference που περιέχει μόνο τις ενημερωμένες καταχωρήσεις. Αυτή η "αυξητική ενημέρωση" (incremental update) είναι εξαιρετικά αποδοτική για μεγάλα αρχεία
4. Το Trailer
Το Trailer είναι το σημείο εισόδου για την ανάλυση (parsing) ενός PDF. Όταν ένας PDF reader ανοίγει ένα αρχείο, δεν ξεκινά από την αρχή. Αναζητά στο τέλος του αρχείου (seeks to the end) και διαβάζει προς τα πίσω για να βρει το λεξικό trailer. Το trailer παρέχει βασικές πληροφορίες πλοήγησης
trailer
<<
/Size 6 % Το Size είναι ο συνολικός αριθμός καταχωρήσεων στον πίνακα cross-reference
/Root 1 0 R % Το Root δείχνει στο Document Catalog (Αντικείμενο 1 σε αυτήν την περίπτωση)
>>
startxref
485 % Byte offset στη λέξη-κλειδί 'xref'
%%EOF % Ο δείκτης Τέλους Αρχείου (End-Of-File marker)
Βασικά στοιχεία του trailer:
/Size: Ο συνολικός αριθμός των καταχωρήσεων cross-reference. Είναι κατά ένα μεγαλύτερος από τον υψηλότερο αριθμό αντικειμένου στο έγγραφο/Root: Η αναφορά στο λεξικό Catalog, το οποίο είναι ο ριζικός κόμβος (root node) του γραφήματος αντικειμένων (object graph). Από το Catalog, μπορείτε να βρείτε το Pages tree, το περίγραμμα (outline) του εγγράφου (σελιδοδείκτες), τους ονομασμένους προορισμούς (named destinations) και τα διαδραστικά (interactive) πεδία φόρμας/Info(προαιρετικό): Περιέχει μεταδεδομένα (metadata) όπως Author (Συγγραφέας), Title (Τίτλος) και CreationDate (Ημερομηνία Δημιουργίας)startxref: Το ακριβές byte offset από την αρχή του αρχείου έως τη λέξη-κλειδίxrefτης τελευταίας ενότητας cross-reference
Πώς Λειτουργεί η Ανάλυση (Parsing) στην Πράξη
Συνοψίζοντας, ένας τυπικός συμβατός reader εκτελεί τα ακόλουθα βήματα κατά το άνοιγμα ενός αρχείου:
- Αναζητά (Seek) κοντά στο τέλος του αρχείου, ψάχνοντας για τον δείκτη
%%EOF - Διαβάζει προς τα πίσω για να βρει το offset του
startxref - Μεταβαίνει (Seek) στο offset του
startxrefγια να αναλύσει τον πίνακα cross-reference (ή το cross-reference stream σε νεότερα PDF) - Αναλύει το λεξικό
trailerαμέσως μετά τον πίνακα xref - Αναζητά τον αριθμό αντικειμένου
/Rootστον πίνακα xref για να βρει τη φυσική του θέση στο αρχείο - Μεταβαίνει σε αυτήν τη θέση, αναλύει το λεξικό Catalog, και ακολουθεί την αναφορά
/Pagesγια να κατασκευάσει το page tree και να ξεκινήσει την απόδοση (rendering)
Αυτή η αρχιτεκτονική, σχεδιασμένη στις αρχές της δεκαετίας του 1990, παραμένει το θεμέλιο της μορφής PDF, επιτρέποντάς της να χειρίζεται αποτελεσματικά τα πάντα, από ένα τιμολόγιο μίας σελίδας έως ένα τεράστιο μηχανολογικό σχέδιο 10.000 σελίδων