Τεχνικό Άρθρο

Το λογικό μοντέλο αντικειμένων PDF: τύποι και αναφορές

Ένα αρχείο PDF είναι, κατά βάθος, μια συλλογή αντικειμένων που δείχνουν το ένα στο άλλο. Αφαιρέστε τη συμπίεση, τη λογιστική των διασταυρούμενων αναφορών και τις μετατοπίσεις byte, και αυτό που μένει είναι ένα γράφημα: ένα μικρό σύνολο τυποποιημένων τιμών, συνδεδεμένων μεταξύ τους με αναφορές, με ρίζα ένα και μόνο αντικείμενο που ο αναγνώστης ξέρει να εντοπίζει. Οτιδήποτε μπορεί να εκφράσει ένα PDF, από μια παράγραφο κειμένου μέχρι μια ενσωματωμένη γραμματοσειρά ή μια ψηφιακή υπογραφή, χτίζεται από οκτώ πρωτογενείς τύπους αντικειμένων και τον κανόνα που επιτρέπει σε ένα αντικείμενο να αναφέρεται σε ένα άλλο. Μάθετε αυτά, και η υπόλοιπη μορφή διαβάζεται ως σύνθεση και όχι ως μυστήριο

Αυτό είναι το λογικό επίπεδο του PDF, που ορίζεται στη ρήτρα 7.3 του ISO 32000-1, και βρίσκεται ένα επίπεδο πάνω από τη φυσική διάταξη του αρχείου (η κεφαλίδα, το σώμα, ο πίνακας διασταυρούμενων αναφορών και το τρέιλερ, που αποτελούν δικό τους θέμα στην τεχνική επισκόπηση της δομής αρχείου PDF). Το λογικό μοντέλο είναι το τι σημαίνουν εκείνα τα byte αφού αναλυθούν. Ένα πρόγραμμα προβολής διαβάζει το αρχείο ανάποδα για να βρει το τρέιλερ, το ακολουθεί ως τη ρίζα, και από εκεί το έγγραφο ξεδιπλώνεται ως αντικείμενα που αναφέρονται σε αντικείμενα. Αυτό είναι το κομμάτι για το οποίο σκέφτεστε όταν εντοπίζετε σφάλματα σε μια κακοσχηματισμένη σελίδα, γράφετε έναν αναλυτή ή εμπιστεύεστε μια βιβλιοθήκη να συναρμολογήσει ένα έγγραφο

Οκτώ τύποι αντικειμένων, και τίποτε άλλο

Το PDF ορίζει ακριβώς οκτώ βασικούς τύπους αντικειμένων. Κάθε τιμή μέσα σε ένα έγγραφο είναι ένας από αυτούς, και αυτό είναι που κρατά τη μορφή διαχειρίσιμη παρά την έκτασή της

Πλέγμα καρτών PDF με τους οκτώ τύπους αντικειμένων του PDF και δείγματα σύνταξης για boolean, αριθμό, συμβολοσειρά, όνομα, πίνακα, λεξικό, ροή και null
Οκτώ πρωτογενείς τύποι συγκροτούν ολόκληρο το λεξιλόγιο τιμών του PDF· το λεξικό σηκώνει το δομικό βάρος ενώ οι ροές μεταφέρουν το ογκώδες συμπιεσμένο φορτίο

Οι τιμές Boolean είναι οι λέξεις-κλειδιά true και false. Ενεργοποιούν και απενεργοποιούν σημαίες, όπως το αν εκτυπώνεται μια σχολίαση

Οι αριθμοί έρχονται σε δύο γεύσεις που η προδιαγραφή αντιμετωπίζει ως έναν τύπο: ακέραιους όπως το 42 και πραγματικούς όπως το 3.14 ή το -0.002. Το PDF δεν έχει εκθετική σημειογραφία, οπότε δεν πρόκειται ποτέ να δείτε 1e6 σε ένα συμμορφούμενο αρχείο. Οι συντεταγμένες, τα μεγέθη γραμματοσειρών και οι γωνίες περιστροφής είναι όλα αριθμοί

Οι συμβολοσειρές κρατούν ακολουθίες από byte, γραμμένες είτε μέσα σε παρενθέσεις, (Hello), είτε μέσα σε γωνιώδεις αγκύλες ως δεκαεξαδικά, <48656C6C6F>. Και οι δύο σημειογραφίες κωδικοποιούν πανομοιότυπο περιεχόμενο· η δεκαεξαδική είναι η έξοδος διαφυγής για byte που είναι άβολα μέσα σε παρενθέσεις. Οι συμβολοσειρές μεταφέρουν κείμενο, αλλά είναι πρώτα από όλα byte, κάτι που έχει σημασία από τη στιγμή που χειρίζεστε οτιδήποτε πέρα από ASCII

Τα ονόματα είναι ατομικές λεκτικές μονάδες που εισάγονται με μια κάθετο: /Type, /Pages, /MediaBox. Ένα όνομα δεν είναι συμβολοσειρά· είναι αναγνωριστικό, που χρησιμοποιείται ως κλειδί λεξικού ή ως τιμή απαρίθμησης, και δύο ονόματα είναι ίσα μόνο αν ταιριάζουν byte προς byte. Η κάθετος είναι σύνταξη, όχι μέρος του ονόματος. Αυτό μπερδεύει τους νεοεισερχόμενους που θεωρούν το /Times-Roman και τη συμβολοσειρά (Times-Roman) εναλλάξιμα· η μορφή δεν τα θεωρεί

Οι πίνακες είναι διατεταγμένες, ετερογενείς λίστες μέσα σε αγκύλες: το [0 0 612 792] είναι ένα ορθογώνιο σελίδας, και ένας πίνακας μπορεί να αναμειγνύει ελεύθερα τύπους, συμπεριλαμβανομένων αναφορών σε άλλα αντικείμενα. Τα λεξικά είναι το άλογο της δουλειάς. Γραμμένο ανάμεσα σε << και >>, ένα λεξικό αντιστοιχίζει κλειδιά-ονόματα σε τιμές οποιουδήποτε τύπου, και σχεδόν κάθε ουσιαστική δομή στο PDF, σελίδα, κατάλογος, γραμματοσειρά, σχολίαση, είναι ένα λεξικό με ένα κλειδί /Type που δηλώνει τι είναι

Οι ροές είναι λεξικά με μια ουρά από ακατέργαστα byte ανάμεσα στις λέξεις-κλειδιά stream και endstream. Το λεξικό περιγράφει τα byte (το μήκος τους και τυχόν φίλτρα όπως το FlateDecode που τα συμπιέζουν), και τα byte μεταφέρουν το ογκώδες φορτίο: οδηγίες περιεχομένου σελίδας, ενσωματωμένα προγράμματα γραμματοσειρών, εικόνες. Η ροή είναι εκεί όπου το PDF βάζει οτιδήποτε είναι πολύ μεγάλο ή πολύ δυαδικό για να κάθεται ενσωματωμένο

Ο όγδοος τύπος είναι το αντικείμενο null, η λέξη-κλειδί null. Είναι πραγματική τιμή, διακριτή από την απουσία ενός κλειδιού. Μια καταχώριση λεξικού που έχει οριστεί σε null αντιμετωπίζεται σαν να μην υπήρχε, και μια αναφορά που καταλήγει σε ανύπαρκτο αντικείμενο δίνει επίσης null αντί για σφάλμα. Αυτή η επιεικής συμπεριφορά είναι σκόπιμη: επιτρέπει σε ένα κατεστραμμένο αρχείο να υποβαθμίζεται αντί να αρνείται να ανοίξει. Δεν υπάρχει ένατος τύπος· ό,τι εκφράζει το PDF προκύπτει από τον τρόπο που συνδυάζονται αυτοί οι οκτώ

Άμεσες τιμές, έμμεσα αντικείμενα και αναφορές

Οποιοσδήποτε από αυτούς τους οκτώ τύπους μπορεί να εμφανιστεί με δύο τρόπους. Ένα άμεσο αντικείμενο γράφεται επιτόπου, όπως το 612 μέσα σε έναν πίνακα MediaBox. Ένα έμμεσο αντικείμενο αποκτά ταυτότητα ώστε άλλα αντικείμενα να μπορούν να δείχνουν σε αυτό: δύο ακέραιοι, ένας αριθμός αντικειμένου και ένας αριθμός γενιάς, που τυλίγουν τον ορισμό μέσα σε obj και endobj:

Ανατομία PDF μιας έμμεσης αναφοράς PDF που δείχνει το αντικείμενο γραμματοσειράς δώδεκα να ορίζεται μία φορά και να προσεγγίζεται από το λεξικό πόρων μιας σελίδας μέσω της σημειογραφίας 12 0 R
Ένα αντικείμενο αποκτά ταυτότητα μέσα από τον αριθμό αντικειμένου και τον αριθμό γενιάς του, και οι καταναλωτές το φτάνουν όπου χρειάζεται γράφοντας την αντίστοιχη αναφορά R αντί για αντίγραφο
12 0 obj
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
endobj

Αυτό είναι το αντικείμενο 12, γενιά 0, ένα λεξικό γραμματοσειράς. Οπουδήποτε αλλού μέσα στο αρχείο, ένα άλλο αντικείμενο αναφέρεται σε αυτό με μια έμμεση αναφορά: τους ίδιους δύο αριθμούς ακολουθούμενους από τη λέξη-κλειδί R, 12 0 R. Η αναφορά είναι δείκτης. Όταν το λεξικό πόρων μιας σελίδας λέει /Font << /F1 12 0 R >>, ονομάζει το αντικείμενο 12 ως τη γραμματοσειρά πίσω από το όνομα πόρου /F1, χωρίς να αντιγράφει τον ορισμό της γραμματοσειράς μέσα στη σελίδα

Ο αριθμός γενιάς υπάρχει για τις διαγραφές και την επαναχρησιμοποίηση. Όταν ένα αντικείμενο απελευθερώνεται και η θέση του χρησιμοποιείται ξανά, η γενιά αυξάνεται ώστε μια ξεπερασμένη 12 0 R να μην μπορεί να καταλήξει στον νέο ένοικο της θέσης 12. Τα φρεσκογραμμένα αρχεία είναι σχεδόν όλα γενιάς 0, αλλά ένα αρχείο με πολλές επεξεργασίες μπορεί να κουβαλά μεγαλύτερους αριθμούς, και ένας αναλυτής που αγνοεί τη γενιά θα διαβάσει κάποια στιγμή λάθος αντικείμενο

Η έμμεση αναφορά είναι αυτό που κάνει το PDF αποδοτικό και επεξεργάσιμο. Μία γραμματοσειρά, εικόνα ή χρωματικός χώρος μπορεί να οριστεί μία φορά και να αναφερθεί από εκατό σελίδες. Μια μικρή αλλαγή μπορεί να προσαρτηθεί ως νέα αναθεώρηση που αντικαθιστά ένα μόνο αντικείμενο αντί να ξαναγραφτεί το αρχείο. Ο πίνακας διασταυρούμενων αναφορών είναι το ευρετήριο που μετατρέπει έναν αριθμό αντικειμένου σε μετατόπιση byte, ώστε ο αναγνώστης να πηδά κατευθείαν στο 12 0 obj χωρίς σάρωση, αλλά αυτό είναι φυσική βελτιστοποίηση. Λογικά, το μόνο που χρειάζεται να ξέρετε είναι ότι το 12 0 R σημαίνει «το αντικείμενο με ταυτότητα 12 0.»

Ο κατάλογος: εκεί όπου ξεκινά κάθε έγγραφο

Η επίλυση των αναφορών πρέπει να ξεκινά από κάπου, και αυτό το κάπου είναι η καταχώριση /Root του τρέιλερ, που δείχνει στον κατάλογο του εγγράφου: τη ρίζα του γραφήματος αντικειμένων, ένα λεξικό με /Type /Catalog. Ο αναγνώστης τον φτάνει πρώτο επειδή το τρέιλερ βρίσκεται πρώτο, και από εκεί κάθε άλλο μέρος του εγγράφου είναι προσβάσιμο ακολουθώντας αναφορές

Ο κατάλογος κουβαλά μόνο δύο αυστηρά υποχρεωτικές καταχωρίσεις: τον /Type του και το /Pages, μια έμμεση αναφορά στη ρίζα του δέντρου σελίδων. Οι υπόλοιπες είναι προαιρετικές και περιγράφουν συμπεριφορά σε επίπεδο εγγράφου παρά περιεχόμενο: το /Outlines δείχνει στο δέντρο των σελιδοδεικτών, το /Names κρατά δέντρα ονομάτων με κλειδιά συμβολοσειρές, το /Metadata αναφέρεται σε μια ροή μεταδεδομένων XMP, ενώ τα /PageMode και /PageLayout προτείνουν πώς πρέπει να ανοίξει το έγγραφο ένα πρόγραμμα προβολής. Κανένα από αυτά δεν χρειάζεται για την απόδοση μιας σελίδας· διαμορφώνουν την εμπειρία γύρω από τις σελίδες. Οι δομές σελιδοδεικτών, μεταδεδομένων και σχολιάσεων που κρέμονται από τον κατάλογο αναλύονται στο άρθρο για τα μεταδεδομένα, τους σελιδοδείκτες και τις σχολιάσεις PDF

Το παρακάτω διάγραμμα δείχνει πού κάθεται το σώμα των αντικειμένων μέσα στο περιβάλλον αρχείο. Ο κατάλογος και το δέντρο σελίδων ζουν μέσα σε εκείνο το σώμα ως συνηθισμένα έμμεσα αντικείμενα· η κεφαλίδα, ο πίνακας διασταυρούμενων αναφορών και το τρέιλερ γύρω τους είναι η φυσική σκαλωσιά που επιτρέπει σε έναν αναγνώστη να τα εντοπίσει

Διάγραμμα των τεσσάρων φυσικών ενοτήτων ενός αρχείου PDF: μια κεφαλίδα έκδοσης, ένα σώμα που κρατά τα αντικείμενα του εγγράφου μαζί με τον κατάλογο και το δέντρο σελίδων, ένας πίνακας διασταυρούμενων αναφορών με τις μετατοπίσεις των αντικειμένων, και ένα τρέιλερ που δείχνει στη ρίζα

Το δέντρο σελίδων: μια ισορροπημένη ιεραρχία σελίδων

Από το /Pages το έγγραφο διακλαδώνεται στο δέντρο σελίδων, εκεί όπου αποδίδει η επιλογή του PDF να προτιμήσει γράφημα αντί για επίπεδη λίστα. Οι σελίδες δεν αποθηκεύονται ως απλή ακολουθία· κρέμονται από ένα δέντρο του οποίου οι εσωτερικοί κόμβοι είναι κόμβοι δέντρου σελίδων (/Type /Pages) και τα φύλλα του είναι αντικείμενα σελίδας (/Type /Page). Ένας εσωτερικός κόμβος απαριθμεί τα παιδιά του σε έναν πίνακα /Kids και καταγράφει, στο /Count, πόσες σελίδες-φύλλα ζουν από κάτω του. Κάθε κόμβος εκτός από τη ρίζα κουβαλά μια αναφορά /Parent προς τα πάνω, οπότε το δέντρο διατρέχεται και προς τις δύο κατευθύνσεις

Διάγραμμα PDF ενός δέντρου σελίδων PDF με έναν ριζικό κόμβο pages, έναν εσωτερικό κόμβο, σελίδες-φύλλα που κουβαλούν αναφορές γονέα, και συνδέσεις προς μια ροή περιεχομένου και κοινόχρηστους πόρους γραμματοσειρών
Οι σελίδες-φύλλα αναφέρονται σε έναν γονέα, σε μια ροή περιεχομένου και σε κοινόχρηστους πόρους, ενώ η ρίζα παρακολουθεί τα σύνολα στο /Count και μπορεί να κληροδοτεί ιδιότητες όπως το /MediaBox σε κάθε απόγονο
2 0 obj                                  % ρίζα του δέντρου σελίδων
<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 3 >>
endobj

3 0 obj                                  % μια σελίδα-φύλλο
<< /Type /Page /Parent 2 0 R
   /MediaBox [0 0 612 792]
   /Resources << /Font << /F1 12 0 R >> >>
   /Contents 5 0 R >>
endobj

4 0 obj                                  % εσωτερικός κόμβος που ομαδοποιεί δύο ακόμη σελίδες
<< /Type /Pages /Parent 2 0 R /Kids [6 0 R 7 0 R] /Count 2 >>
endobj

Εδώ το αντικείμενο 2 είναι η ρίζα, με τρεις σελίδες από κάτω του: τη σελίδα-φύλλο 3, συν δύο ακόμη προσβάσιμες μέσω του εσωτερικού κόμβου 4. Το /Count της ρίζας με τιμή 3 πρέπει να ισούται με το σύνολο των φύλλων από κάτω της, και ένα πλήθος που διαφωνεί με την πραγματική δομή είναι συνηθισμένος τρόπος να στραβώσει ένα αρχείο που επεξεργάστηκε στο χέρι. Το νόημα του δέντρου είναι η τοπικότητα της πρόσβασης. Ένας αναγνώστης που ανοίγει τη σελίδα 900 ενός εγγράφου χιλίων σελίδων δεν διατρέχει 900 αντικείμενα· κατεβαίνει λίγους κόμβους, επειδή ένα καλοσχηματισμένο δέντρο μένει ρηχό και ισορροπημένο. Το χτίσιμο ενός τέτοιου δέντρου στο χέρι είναι αρκετά ιδιότροπο ώστε να αξίζει να το δείτε από άκρη σε άκρη, κάτι που κάνει ο οδηγός για το χτίσιμο ενός εγγράφου PDF από το μηδέν

Το δέντρο κερδίζει τη δεύτερη αξία του μέσα από την κληρονομικότητα. Λίγες ιδιότητες σελίδας, οι /Resources, /MediaBox, /CropBox και /Rotate, μπορούν να οριστούν σε έναν εσωτερικό κόμβο και να παραλειφθούν από τις μεμονωμένες σελίδες, οι οποίες τότε κληρονομούν την τιμή του πλησιέστερου προγόνου. Ορίστε το /MediaBox μία φορά στη ρίζα και κάθε φύλλο παίρνει το ίδιο μέγεθος σελίδας χωρίς επανάληψη· μια σελίδα που χρειάζεται να διαφέρει δηλώνει το δικό της. Αυτό είναι το μοναδικό σημείο του μοντέλου αντικειμένων όπου το νόημα μιας τιμής εξαρτάται από τη θέση ενός αντικειμένου στο δέντρο και όχι μόνο από το ίδιο του το περιεχόμενο

Τι κρατά στην πραγματικότητα μια σελίδα-φύλλο

Ένα αντικείμενο σελίδας είναι το σημείο ένωσης ανάμεσα στο δομικό μοντέλο και στο ορατό περιεχόμενο. Η καταχώρισή του /Contents αναφέρεται σε μία ή περισσότερες ροές περιεχομένου, τους τελεστές σχεδίασης που ζωγραφίζουν κείμενο και γραφικά πάνω στη σελίδα. Το λεξικό του /Resources ονομάζει τις γραμματοσειρές, τις εικόνες και τους χρωματικούς χώρους στους οποίους στηρίζονται αυτοί οι τελεστές, με κάθε καταχώριση να είναι έμμεση αναφορά σε ένα αντικείμενο κοινό για πολλές σελίδες. Το /MediaBox δίνει το ορθογώνιο της σελίδας σε στιγμές (1/72 της ίντσας), ενώ καταχωρίσεις όπως τα /Rotate και /CropBox ρυθμίζουν τον τρόπο παρουσίασής της

Αυτός ο καταμερισμός εργασίας είναι ολόκληρο το μοντέλο σε μικρογραφία. Το λεξικό της σελίδας είναι δομή: τυποποιημένες καταχωρίσεις και αναφορές που λένε τι είναι η σελίδα και με τι σχεδιάζει. Η ροή περιεχομένου είναι οδηγίες: ένα ξεχωριστό, συμπιέσιμο μπλοκ που λέει πώς να σχεδιαστεί. Η γραμματοσειρά πίσω από το /F1 είναι κοινόχρηστος πόρος, ορισμένος μία φορά και προσπελάσιμος όπου χρειάζεται. Το λεξικό, η ροή και η αναφορά συνεργάζονται για να αποδοθεί μία σελίδα, και τα ίδια μοτίβα κλιμακώνονται σε ολόκληρο το έγγραφο. Οι τελεστές ροής περιεχομένου μέσα σε εκείνο το μπλοκ καλύπτονται ξεχωριστά για το κείμενο και τις γραμματοσειρές και για τα γραφικά και τα οπτικά στοιχεία

Γιατί αξίζει να ξέρετε αυτό το μοντέλο

Οι περισσότεροι προγραμματιστές συναντούν το μοντέλο αντικειμένων μόνο όταν κάτι χαλάει: μια σελίδα αποδίδεται κενή επειδή η αναφορά /Contents της κρέμεται στο κενό, το κείμενο βγαίνει ως πλαίσια επειδή ένας πόρος γραμματοσειράς δεν ενσωματώθηκε ποτέ, ένα εργαλείο αναφέρει ένα /Count που δεν ταιριάζει με τις σελίδες που βρίσκει. Καθένα από αυτά είναι μια δήλωση για το γράφημα, και η απευθείας ανάγνωση του γραφήματος είναι καλύτερη από τη μαντεψιά. Οι οκτώ τύποι και ο κανόνας των αναφορών είναι αρκετά μικρό λεξιλόγιο για να το κρατάτε στο μυαλό σας, και μόλις δείτε ένα PDF ως αντικείμενα που δείχνουν σε αντικείμενα, τα κακοσχηματισμένα αρχεία παύουν να είναι αδιαφανή

Τούτου λεχθέντος, το γράψιμο του μοντέλου στο χέρι σπάνια είναι η σωστή επιλογή πέρα από τη μάθηση. Το να κρατάτε συνεπείς τις μετατοπίσεις των διασταυρούμενων αναφορών, τους αριθμούς γενιάς, τα πλήθη του δέντρου σελίδων και τα μήκη των ροών σε κάθε επεξεργασία είναι ακριβώς η λογιστική για την οποία υπάρχει μια βιβλιοθήκη. Στην παραγωγή, μια ώριμη βιβλιοθήκη ανάπτυξης PDF διαχειρίζεται το γράφημα αντικειμένων και σας αφήνει να σκέφτεστε σε σελίδες και περιεχόμενο. Η γνώση του μοντέλου εξακολουθεί να αποδίδει: καταλαβαίνετε τι χτίζει η βιβλιοθήκη από κάτω, και γιατί