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

Εκμάθηση σε Βάθος της Δομής PDF: Μεταδεδομένα XML, Σελιδοδείκτες και Σχολιασμοί

Πέρα από το ορατό κείμενο και τα γραφικά, ένα αρχείο PDF είναι ένας πλούσιος περιέκτης (container) δομημένων πληροφοριών. Για την κατασκευή στιβαρών εφαρμογών επεξεργασίας PDF, οι προγραμματιστές πρέπει να κατανοήσουν πώς να έχουν πρόσβαση και να ερμηνεύουν αυτά τα κρυφά επίπεδα. Αυτό το άρθρο εξερευνά τρία κρίσιμα στοιχεία της δομής του PDF: Μεταδεδομένα XML (XMP), Περιγράμματα Εγγράφων (Σελιδοδείκτες - Bookmarks) και Σχολιασμούς (Annotations)

Μεταδεδομένα XML (XMP): Το Σύγχρονο Πρότυπο

Ιστορικά, τα μεταδεδομένα του PDF αποθηκεύονταν στο Document Information Dictionary (Λεξικό Πληροφοριών Εγγράφου - η καταχώρηση /Info στο trailer). Αυτός ο απλός χώρος αποθήκευσης κλειδιού-τιμής (key-value store) περιείχε βασικές συμβολοσειρές (strings) όπως Title (Τίτλος), Author (Συγγραφέας) και CreationDate (Ημερομηνία Δημιουργίας). Ωστόσο, καθώς οι ροές εργασίας (workflows) γίνονταν πιο περίπλοκες, απαιτήθηκε ένα πιο επεκτάσιμο σύστημα

Εισαγωγή στο Extensible Metadata Platform (XMP). Το XMP ενσωματώνει μεταδεδομένα μορφοποιημένα σε XML απευθείας στο PDF. Αυτό επιτρέπει πλούσια, ιεραρχικά σχήματα δεδομένων (data schemas), επιτρέποντας τη διαλειτουργικότητα με άλλες μορφές αρχείων (όπως JPEG ή TIFF) και εξειδικευμένες ροές εργασίας (όπως το PDF/A για αρχειοθέτηση)

Πού θα βρείτε το XMP

Η κύρια ροή (stream) XMP αναφέρεται από το κλειδί /Metadata στο Document Catalog. Αποθηκεύεται ως ασυμπίεστη ροή Metadata. Το "ασυμπίεστη" είναι το κλειδί εδώ: σημαίνει ότι οι μηχανές αναζήτησης και τα απλά εργαλεία σάρωσης συμβολοσειρών μπορούν να εξάγουν την XML χωρίς να αναλύσουν (parse) τη δομή του PDF ή να εφαρμόσουν αλγόριθμους αποσυμπίεσης όπως το FlateDecode

// Εξαγωγή μεταδεδομένων XMP από ένα αρχείο PDF
var pdf = new losLab.PDF.Document("document.pdf");
var xmpStream = pdf.Catalog.GetDictionary().GetStream("Metadata");
var xmlString = System.Text.Encoding.UTF8.GetString(xmpStream.GetBytes());
// Ανάλυση περιεχομένου XML...

Περιγράμματα Εγγράφων (Σελιδοδείκτες - Bookmarks): Ιεραρχική Πλοήγηση

Αυτό που οι χρήστες ονομάζουν "Σελιδοδείκτες" (Bookmarks), η προδιαγραφή PDF το ονομάζει "Περίγραμμα Εγγράφου" (Document Outline). Πρόκειται για μια ιεραρχική δομή δέντρου που επιτρέπει στους χρήστες να πλοηγούνται γρήγορα σε συγκεκριμένες ενότητες ενός εγγράφου

Η Δομή του Δέντρου Outline

Η ρίζα του δέντρου outline είναι το Outline Dictionary (Λεξικό Περιγράμματος), που βρίσκεται μέσω του κλειδιού /Outlines στο Document Catalog. Αυτή η ρίζα δείχνει στα αντικείμενα /First (Πρώτο) και /Last (Τελευταίο) του κορυφαίου επιπέδου (top-level Outline Items)

Κάθε Outline Item (ένα λεξικό /Item) αντιπροσωπεύει έναν μόνο σελιδοδείκτη. Για να διατηρηθεί η δομή και η σειρά του δέντρου, ένα αντικείμενο (item) περιέχει δείκτες στα "αδέλφια" του (/Prev, /Next), στον δομικό γονέα του (/Parent) και στα δικά του "παιδιά" (/First, /Last)

Είναι σημαντικό ότι ένα Outline Item πρέπει να ορίζει μια Ενέργεια (Action). Αυτό γίνεται συνήθως μέσω μιας καταχώρησης /A (ένα λεξικό Action, συχνά μια ενέργεια /GoTo) ή μιας καταχώρησης /Dest (ένας πίνακας Destination (Προορισμός) ή Name (Όνομα)). Αυτή η ενέργεια λέει στο πρόγραμμα προβολής (viewer) τι να κάνει όταν ο χρήστης κάνει κλικ στον σελιδοδείκτη — συνήθως, να μεταβεί σε μια συγκεκριμένη σελίδα και συντεταγμένη προβολής

% Ένα Outline Item που δείχνει στη Σελίδα 5
25 0 obj
<< 
  /Title (Chapter 2: Data Structures)
  /Parent 24 0 R
  /Next 26 0 R
  /Dest [10 0 R /FitH 792]  % Το 10 0 R είναι το αντικείμενο σελίδας για τη Σελίδα 5, το /FitH καθορίζει οριζόντια προσαρμογή
>>
endobj

Σχολιασμοί (Annotations): Το Διαδραστικό Επίπεδο

Οι σχολιασμοί φέρνουν διαδραστικότητα (interactivity) στη στατική σελίδα του PDF. Χρησιμοποιούνται για σημειώσεις (sticky notes), επισημάνσεις (highlighting), υπερσυνδέσμους (hyperlinks), πεδία φόρμας και ακόμη και ενσωματωμένο βίντεο. Σε αντίθεση με τις εντολές σχεδίασης content stream (που "ζωγραφίζονται" απευθείας στον καμβά της σελίδας), οι σχολιασμοί είναι λογικά αντικείμενα που αιωρούνται (float) πάνω από το περιεχόμενο της σελίδας

Πώς Αποθηκεύονται οι Σχολιασμοί

Οι σχολιασμοί συνδέονται με συγκεκριμένες σελίδες. Ένα λεξικό Page περιέχει έναν πίνακα /Annots, ο οποίος παραθέτει τις αναφορές αντικειμένων για όλους τους σχολιασμούς σε αυτήν τη σελίδα

Κάθε λεξικό Annotation περιέχει τυπικές απαιτούμενες καταχωρήσεις:

  • /Type /Annot: Προσδιορίζει το αντικείμενο
  • /Subtype: Ο συγκεκριμένος τύπος σχολιασμού (π.χ., /Text για sticky notes, /Link για υπερσυνδέσμους, /Widget για πεδία φόρμας)
  • /Rect: Το πλαίσιο οριοθέτησης (bounding box) του σχολιασμού — ένας πίνακας τεσσάρων αριθμών [llx, lly, urx, ury] που ορίζει τις συντεταγμένες κάτω αριστερά και πάνω δεξιά στον Χώρο Χρήστη (User Space)
% Ένας απλός σχολιασμός κειμένου (Text Annotation - Sticky Note)
30 0 obj
<< 
  /Type /Annot
  /Subtype /Text
  /Rect [72 72 96 96]
  /Contents (Please review this paragraph.)
  /Open false
  /Name /Comment
>>
endobj

Η Ροή Εμφάνισης (Appearance Stream - AP)

Μια κρίσιμη έννοια στους σχολιασμούς είναι το Appearance Dictionary (Λεξικό Εμφάνισης - /AP). Ενώ τα τυπικά προγράμματα προβολής γνωρίζουν πώς να σχεδιάζουν τυπικούς σχολιασμούς (όπως ένα τυπικό εικονίδιο sticky note), η προδιαγραφή PDF επιτρέπει στις γεννήτριες (generators) να ορίσουν ακριβώς πώς θα πρέπει να φαίνεται ένας σχολιασμός παρέχοντας ένα προσαρμοσμένο content stream (ροή περιεχομένου)

Για παράδειγμα, ένα πεδίο ψηφιακής υπογραφής (το οποίο είναι ένας σχολιασμός /Widget) χρησιμοποιεί ένα appearance stream για να σχεδιάσει την οπτική αναπαράσταση της υπογραφής (όπως μια σαρωμένη εικόνα μιας χειρόγραφης υπογραφής ή μιας εταιρικής σφραγίδας). Εάν ένας αναλυτής θέλει να εξαγάγει την οπτική εμφάνιση ενός σχολιασμού, πρέπει να αναλύσει τη ροή /AP του, και όχι μόνο τις ιδιότητές του

Πρόσθετα παραδείγματα κώδικα

12 0 obj                                    % link to a destination
<< /Type /Annot /Subtype /Link
   /Rect [100 200 300 250]
   /Border [0 0 0]
   /Dest [5 0 R /XYZ null null null] >>
endobj
13 0 obj                                    % link that runs an action
<< /Type /Annot /Subtype /Link
   /Rect [50 50 200 100]
   /Border [0 0 0]
   /A << /Type /Action /S /URI /URI (https://www.example.com) >> >>
endobj