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

Γραμμικοποίηση PDF και Γρήγορη Προβολή στο Web: Πώς Λειτουργεί

Τοποθετήστε μια σαρωμένη αναφορά 80 MB πίσω από έναν σύνδεσμο, ανοίξτε την σε ένα πρόγραμμα περιήγησης και δείτε τι συμβαίνει: το πρόγραμμα προβολής παραμένει σε ένα κενό παράθυρο μέχρι να φτάσει ένα μεγάλο μέρος αυτών των byte και, στη συνέχεια, σχεδιάζει την πρώτη σελίδα με τη μία. Μεταβείτε στη σελίδα 40 και, σε ένα κακοφτιαγμένο αρχείο, ολόκληρη η λήψη μπορεί να ξεκινήσει από την αρχή. Το απογοητευτικό είναι ότι ο αναγνώστης ήθελε μόνο την πρώτη σελίδα. Η γραμμικοποίηση είναι η δομική απάντηση σε αυτό το πρόβλημα. Αναδιατάσσει ένα PDF έτσι ώστε ένα πρόγραμμα προβολής να μπορεί να αποδώσει την αρχική σελίδα από ένα μικρό πρόθεμα του αρχείου και να ανακτήσει τα υπόλοιπα κατά παραγγελία, γι' αυτό και η Adobe προωθεί τη λειτουργία ως "Fast Web View"

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

Τι πραγματικά αναδιατάσσει η γραμμικοποίηση

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

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

Το σύνολο αντικειμένων πρώτης σελίδας και το λεξικό παραμέτρων γραμμικοποίησης

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

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

Ροές υποδείξεων: το ευρετήριο που κάνει τα άλματα σελίδας φθηνά

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

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

Γιατί ο διακομιστής πρέπει να συνεργαστεί

Η γραμμικοποίηση προϋποθέτει ότι η μεταφορά μπορεί να παραδώσει αυθαίρετα τμήματα του αρχείου, και αυτή η υπόθεση αξίζει να ελεγχθεί πριν κατηγορήσετε τη μορφή για κακά αποτελέσματα. Ο μηχανισμός είναι HTTP byte-serving: ο αναγνώστης εκδίδει αιτήματα εύρους (range requests) και ο διακομιστής απαντά με αποκρίσεις 206 Partial Content. Εάν ο διακομιστής δεν διαφημίζει Accept-Ranges: bytes, ή εάν ένας διακομιστής μεσολάβησης (proxy) ή CDN μπροστά του συμπτύσσει τα αιτήματα εύρους σε πλήρεις μεταφορές, ο αναγνώστης δεν έχει κανέναν τρόπο να ανακτήσει τη σελίδα 40 μεμονωμένα και επιστρέφει στη λήψη ολόκληρου του αρχείου. Η δομή μέσα στο PDF είναι τότε απολύτως σωστή και εντελώς χαμένη

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

Οι σταδιακές ενημερώσεις καταστρέφουν αθόρυβα τη γραμμικοποίηση

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

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

Εάν η ροή εργασίας σας χρειάζεται τόσο επεξεργασίες όσο και Fast Web View, ο κανόνας προκύπτει άμεσα από τη δομή: επεξεργαστείτε σταδιακά όσο το έγγραφο μεταβάλλεται και, στη συνέχεια, επαναγραμμικοποιήστε το μία φορά στο τέλος. Μια πλήρης επανεγγραφή είναι αυτό που αποκαθιστά τη διάταξη. Με όρους HotPDF, αυτό σημαίνει ότι μια συνεχιζόμενη επεξεργασία περνά από τα BeginIncrementalUpdate και SaveIncrementalUpdate, τα οποία προσαρτούν ένα δέλτα, ενώ το τελικό βήμα φορτώνει ολόκληρο το έγγραφο και το σειριοποιεί φρέσκο με το LoadFromFile ακολουθούμενο από το SaveLoadedDocument, το οποίο απορρίπτει τις συσσωρευμένες παλιές αναθεωρήσεις και εκπέμπει μία καθαρή διάταξη. Η ίδια ανταλλαγή εμφανίζεται με τις ροές αντικειμένων: η ενεργοποίηση του UseObjectStreams μαζί με το UseXRefStream συμπιέζει τις διασταυρούμενες αναφορές και πακετάρει τα αντικείμενα σφιχτά, γεγονός που βοηθά στο μέγεθος του αρχείου, αλλά, όπως κάθε δομική επιλογή, πρέπει να εφαρμοστεί κατά τη διάρκεια αυτής της τελικής επανεγγραφής αντί να βιδωθεί σε μια προσαρτημένη αναθεώρηση

// In-flight edits: append a delta, keep prior revisions intact.
// This leaves the file NOT linearized.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');

// Finishing step: full re-serialization produces one clean layout,
// dropping the stacked revisions. Re-run your linearizer on the output.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');

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

qpdf --linearize report-final.pdf report-web.pdf

Πώς να καταλάβετε εάν ένα αρχείο είναι γραμμικοποιημένο

Μην εμπιστεύεστε το όνομα αρχείου ή το εργαλείο που ισχυρίζεται ότι το παρήγαγε· επαληθεύστε τα byte. Ο πιο άμεσος έλεγχος είναι η κεφαλή του αρχείου: ανοίξτε το και αναζητήστε το λεξικό παραμέτρων γραμμικοποίησης ως το πρώτο αντικείμενο μετά την κεφαλίδα, φέροντας το κλειδί /Linearized. Μια συντόμευση για τον αναγνώστη είναι το παράθυρο διαλόγου Document Properties (Ιδιότητες εγγράφου) του Acrobat, το οποίο αναφέρει "Fast Web View: Yes" μόνο όταν η δομή είναι πραγματικά παρούσα και τρέχουσα

Για ελέγχους μέσω σεναρίων (scripted checks), το qpdf αναφέρει τόσο την παρουσία όσο και την ακεραιότητα της δομής, κάτι που έχει σημασία επειδή ένα αρχείο μπορεί να φέρει ένα λεξικό γραμμικοποίησης που δεν αντανακλά πλέον τη διάταξή του, ακριβώς την κατάσταση που αφήνει πίσω της μια σταδιακή ενημέρωση:

# Reports "File is linearized" and validates hint tables against the layout
qpdf --check report-web.pdf

# Dumps the linearization parameters and hint data in detail
qpdf --show-linearization report-web.pdf

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

Η γραμμικοποίηση εξακολουθεί να αξίζει να εφαρμόζεται σε κάθε μεγάλο έγγραφο που εξυπηρετείται μέσω του web, ειδικά σε φορητούς αναγνώστες με ασταθείς συνδέσεις, και κοστίζει μερικά τοις εκατό του μεγέθους του αρχείου για το μπροστινά φορτωμένο ευρετήριο. Τα δύο πράγματα που πρέπει να θυμάστε είναι ότι τόσο η δομή μέσα στο PDF όσο και το byte-serving έξω από αυτό πρέπει να είναι σωστά, και ότι οποιαδήποτε επεξεργασία εκ των υστέρων αναιρεί τη βελτιστοποίηση μέχρι να ξαναγράψετε το αρχείο. Αντιμετωπίστε την επαναγραμμικοποίηση ως το τελευταίο βήμα στη ροή διαδικασιών, αφού έχει διευθετηθεί κάθε άλλη αλλαγή. Η συμπεριφορά των διασταυρούμενων αναφορών, της ροής αντικειμένων και της σταδιακής ενημέρωσης που περιγράφεται εδώ αποτελεί μέρος του δομικού μοντέλου που υλοποιεί το HotPDF Component για Delphi και C++Builder· για το ευρύτερο υπόβαθρο διάταξης αρχείων δείτε πώς είναι δομημένο ένα PDF, και για τη ροή εργασίας σταδιακής ενημέρωσης και μεγάλων αρχείων σε κώδικα δείτε επεξεργασία μεγάλων PDF από Delphi