Κάθε γεωμετρικός εξαγωγέας κειμένου μαντεύει. Διαβάζει τα glyphs που ζωγραφίζει μια σελίδα, τα ταξινομεί κατά baseline και οριζόντια θέση, και ελπίζει η οπτική διάταξη να ταιριάζει με τη σειρά που θα διάβαζε ένας άνθρωπος. Σε μια αναφορά μίας στήλης η μαντεψιά είναι σωστή. Σε ένα άρθρο περιοδικού δύο στηλών, μια φόρμα με πλευρική στήλη, ή έναν πίνακα του οποίου τα κελιά εκπέμφθηκαν στήλη προς στήλη, είναι λάθος με τρόπους δύσκολους να προσέξει κανείς και ακριβούς να ανακαλυφθούν κατάντη. Το HotPDF απαντά σε αυτό με την ExtractLoadedPageStructureText, που αγνοεί τελείως τη γεωμετρία: διατρέχει το δέντρο δομής του εγγράφου σε σειρά συγγραφής όπως ορίζεται στο ISO 32000-1 §14.8.4, και μετά επανασυνθέτει τα glyphs της σελίδας κατά το marked-content identifier τους. Για ένα tagged PDF αυτό δεν είναι ευριστική, είναι η σειρά που δήλωσε η εφαρμογή παραγωγής
Η συνάρτηση επιστρέφει False όταν η σελίδα δεν έχει χρηστικό δέντρο δομής, που είναι το σήμα να πέσετε στη γεωμετρική εξαγωγή αντί να αποτύχετε. Ο σχεδιασμός των δύο διαδρομών έχει μεγαλύτερη σημασία από τον αλγόριθμο: η πραγματική εισροή εγγράφων βλέπει tagged κυβερνητικές φόρμες και έξοδο scanner στον ίδιο φάκελο, και ένας αγωγός που χειρίζεται μόνο το ένα δεν είναι αγωγός
Γιατί η γεωμετρική εξαγωγή παίρνει λάθος τη σειρά ανάγνωσης;
Επειδή ένα content stream PDF δεν κουβαλά καθόλου σειρά ανάγνωσης. Είναι μια αλληλουχία τελεστών ζωγραφικής, και ένας παραγωγός είναι ελεύθερος να τους εκπέμψει σε όποια αλληλουχία βολεύει τη δική του μηχανή διάταξης. Οι επεξεργαστές κειμένου εκπέμπουν συνήθως σε σειρά ροής και η γεωμετρική ταξινόμηση φαίνεται καλή. Τα εργαλεία διάταξης, οι σχεδιαστές φορμών και οι γεννήτριες αναφορών συχνά όχι: ένας υποσέλιδος σελίδας μπορεί να εκπεμφθεί πριν από το κυρίως σώμα, ένας πίνακας μπορεί να γεμίσει κατά στήλες, και μια σελίδα δύο στηλών μπορεί να πλέκει γραμμές και από τις δύο στήλες επειδή ο συνθέτης τις έλυσε μαζί
Η λειτουργία αστοχίας είναι ήσυχη. Ένας γεωμετρικός εξαγωγέας δεν αναφέρει ποτέ σφάλμα, απλώς παραδίδει πεζό κείμενο του οποίου οι προτάσεις είναι ραμμένες από δύο στήλες. Οτιδήποτε καταναλώνει αυτό το κείμενο, ένα ευρετήριο αναζήτησης, ένας mapper πεδίων ηλεκτρονικού τιμολογίου, ένας αγωγός ανάκτησης που τροφοδοτεί γλωσσικό μοντέλο, κληρονομεί τη ζημιά χωρίς προειδοποίηση. Το HotPDF διανέμει επίσης τους γεωμετρικούς εξαγωγείς για φορτωμένα έγγραφα, και αυτοί παραμένουν το σωστό εργαλείο για αρχεία χωρίς ετικέτες· το νόημα της διαδρομής σειράς δομής είναι να σταματήσει η μαντεψιά όταν το έγγραφο κουβαλά ήδη την απάντηση
Τι αποθηκεύει πραγματικά το δέντρο δομής
Ένα tagged PDF κρατά μια δεύτερη, παράλληλη περιγραφή της σελίδας. Ο κατάλογος δείχνει σε ένα /StructTreeRoot, του οποίου τα τέκνα /K σχηματίζουν δέντρο στοιχείων δομής: /Document, /Sect, /P, /Table, /TR, /TD, και ούτω καθεξής. Τα φύλλα εκείνου του δέντρου είναι αναφορές marked-content, ακέραιοι που ονομάζουν ένα τμήμα του content stream της σελίδας. Στην πλευρά περιεχομένου, εκείνα τα τμήματα ανοίγουν με τελεστή BDC που κουβαλά /MCID και κλείνουν με EMC. Κάθε στοιχείο δομής κουβαλά επίσης εγγραφή /Pg που ονομάζει τη σελίδα στην οποία ανήκει, που είναι αυτό που κάνει δυνατή τη διάτρεξη ανά σελίδα σε ένα έγγραφο του οποίου το δέντρο δομής απλώνεται σε εκατοντάδες σελίδες
Το HotPDF διατρέχει εκείνο το δέντρο με όριο βάθους 128 επιπέδων και φιλτράρει στο /Pg ώστε μόνο η τρέχουσα σελίδα να συνεισφέρει. Η έξοδος της διάτρεξης δεν είναι κείμενο, είναι μια διατεταγμένη λίστα τιμών MCID: η σειρά συγγραφής των τμημάτων marked-content σε αυτή τη σελίδα. Η επανασύνθεση κειμένου είναι τότε θέμα αναπαραγωγής των glyphs σε εκείνη τη σειρά
Το MCID καταγράφεται κατά την εξαγωγή glyphs, δεν αναζητείται μετά
Αυτή είναι η λεπτομέρεια υλοποίησης που κάνει τη λειτουργία φθηνή. Το HotPDF καταγράφει ήδη το ενεργό marked-content identifier σε κάθε glyph που εξάγει, στο πεδίο MCID του THPDFGlyphRecord, επειδή ο διερμηνέας content stream ξέρει ποιο εύρος BDC είναι ανοιχτό τη στιγμή που επεξεργάζεται κάθε τελεστή Tj ή TJ. Η εξαγωγή σειράς δομής χρειάζεται επομένως κανένα δεύτερο πέρασμα πάνω στο content stream. Συλλέγει την αλληλουχία MCID από το δέντρο δομής, και μετά κατηγοριοποιεί τα ήδη εξαχμένα glyphs ανά MCID και τα εκπέμπει σε εκείνη την αλληλουχία
var
Pdf: THotPDF;
PageCount, I, Untagged: Integer;
PageText, AllText: UnicodeString;
Report: TStrings; // δοχείο διαγνωστικών υπό ιδιοκτησία καλούντος
begin
Pdf := THotPDF.Create(nil);
try
PageCount := Pdf.LoadFromFile('accessible-form.pdf');
AllText := '';
for I := 0 to PageCount - 1 do
begin
if Pdf.ExtractLoadedPageStructureText(I, PageText, Untagged) then
begin
// Σειρά συγγραφής ευθεία από το δέντρο δομής
if Untagged > 0 then
Report.Add(Format('page %d: %d glyphs outside the structure tree',
[I, Untagged]));
end
else
// Χωρίς χρηστικό δέντρο δομής στη σελίδα: γεωμετρική εναλλακτική
Pdf.ExtractLoadedPageText(I, PageText);
AllText := AllText + PageText + #13#10;
end;
finally
Pdf.Free;
end;
end;
Τα glyphs χωρίς ετικέτα μετριούνται, δεν εγκαταλείπονται ποτέ σιωπηλά
Μια σελίδα μπορεί να είναι μερικώς με ετικέτες. Οι παραγωγοί προσθέτουν μια διακοσμητική γραμμή, έναν αριθμό σελίδας, ή ένα υδατογράφημα τελευταίας στιγμής έξω από κάθε εύρος BDC, και εκείνα τα glyphs δεν ανήκουν σε κανένα MCID. Η εγκατάλειψή τους θα ήταν η τακτοποιημένη υλοποίηση και η λάθος, γιατί το ίδιο κενό εμφανίζεται και όταν ένας παραγωγός βάζει ετικέτα στο σώμα αλλά ξεχνά τον πίνακα, και θα χάνατε τον πίνακα χωρίς να το προσέξετε
Το HotPDF προσαπτά τα ανεκδίκαφα glyphs ως γεωμετρική ουρά μετά το κείμενο σειράς δομής και αναφέρει τον αριθμό τους μέσω της παραμέτρου εξόδου UntaggedGlyphCount. Ο αριθμός εκείνος είναι σήμα ποιότητας στο οποίο μπορείτε να δράσετε. Μια χούφτα glyphs σε σελίδα των δύο χιλιάδων είναι έπιπλο σελίδας και μπορεί να αγνοηθεί. Σαράντα τοις εκατό της σελίδας έξω από το δέντρο δομής σημαίνει ότι η ετικετοποίηση είναι διακοσμητική και ο γεωμετρικός εξαγωγέας είναι η πιο ειλικρινής απάντηση για εκείνο το αρχείο
function ExtractPageBestEffort(Pdf: THotPDF; PageIndex: Integer;
out AText: UnicodeString; out UsedStructure: Boolean): Boolean;
var
Untagged, TotalGlyphs: Integer;
Glyphs: THPDFGlyphArray;
begin
UsedStructure := False;
if Pdf.ExtractLoadedPageStructureText(PageIndex, AText, Untagged) then
begin
TotalGlyphs := 0;
if Pdf.ExtractLoadedPageGlyphs(PageIndex, Glyphs) then
TotalGlyphs := Length(Glyphs);
// Εμπιστευτείτε το δέντρο δομής μόνο όταν διεκδικεί το μεγαλύτερο
// μέρος της σελίδας
if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
begin
UsedStructure := True;
Result := True;
Exit;
end;
end;
Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;
Τι κάνει τη συνάρτηση να επιστρέφει False
Τρεις περιπτώσεις, και αξίζει να διακριθούν επειδή μόνο μία από αυτές είναι ελάττωμα στο έγγραφο. Η πρώτη είναι ένα συνηθισμένο PDF χωρίς ετικέτες: κανένα /StructTreeRoot, τίποτα προς διάτρεξη, και το False είναι απλώς η αλήθεια. Η δεύτερη είναι μια σαρωμένη σελίδα του οποίου το κείμενο προέρχεται από στρώμα OCR που δεν ετικετοποιήθηκε ποτέ. Η τρίτη είναι η ενδιαφέρουσα: περιεχόμενο που κουβαλά τελεστές BDC με τιμές /MCID αλλά του οποίου η σελίδα δεν έχει εγγραφή /StructParents και του οποίου το δέντρο δομής δεν αναφέρεται ποτέ σε εκείνα τα αναγνωριστικά. Το marked content υπάρχει, η πλευρά δομής δεν υπάρχει, και δεν υπάρχει σειρά προς ανάκτηση. Το HotPDF αναφέρει False αντί να εφεύρει μία
Εκείνη η τελευταία περίπτωση εμφανίζεται σε αρχεία επεξεργασμένα με το χέρι και σε έξοδο από εργαλεία που εκπέμπουν marked content για σκοπούς optional-content ή artifact χωρίς να χτίζουν δέντρο δομής. Αν παράγετε εσείς tagged PDF, η ίδια ασυμμετρία είναι αυτό που ελέγχει η επικύρωση PDF/UA, και το αντίστοιχο πλευράς writer καλύπτεται στο layout DOM που εκπέμπει tagged, σελιδοποιημένη έξοδο
Πού η σειρά δομής αποπληρώνεται
Ο έλεγχος προσβασιμότητας είναι το προφανές: αν πιστοποιείτε ένα έγγραφο απέναντι στο PDF/UA, η σειρά ανάγνωσης που θα ανακοινώσει ένας screen reader είναι ακριβώς η σειρά δομής, οπότε η εξαγωγή της είναι ο τρόπος να την εξετάσετε χωρίς screen reader. Η σύλληψη δεδομένων είναι η μεγαλύτερη εμπορική περίπτωση. Tagged κυβερνητικές φόρμες, ρυθμιζόμενες γνωστοποιήσεις, και συνημμένα ηλεκτρονικών τιμολογίων κουβαλούν ετικέτες πεδίων και τιμές σε δηλωμένη σειρά, και η ανάγνωσή τους σε εκείνη τη σειρά αφαιρεί μια ολόκληρη κατηγορία σφαλμάτων αντιστοίχισης που η γεωμετρική εξαγωγή δημιουργεί σε διατάξεις πολλών στηλών
Ο νεότερος καταναλωτής είναι η ανάκτηση για γλωσσικά μοντέλα. Ο τεμαχισμός ενός εγγράφου για embedding είναι καλός όσο η σειρά κειμένου, και ένα τμήμα που ράβει δύο στήλες παράγει προτάσεις που δεν υπήρξαν ποτέ. Η εξαγωγή σειράς δομής είναι η φθηνότερη διαθέσιμη διόρθωση για αυτό, γιατί για tagged έγγραφα η σωστή σειρά είναι ήδη στο αρχείο και χρειάζεται μόνο να διαβαστεί
Το HotPDF είναι ένα εγγενές στοιχείο VCL για Delphi και C++Builder, οπότε η διάτρεξη δέντρου δομής και η αναπαραγωγή glyphs τρέχουν και οι δύο εντός διεργασίας πάνω σε φορτωμένο έγγραφο χωρίς εμπλοκή εξωτερικού renderer. Οι πλήρεις λεπτομέρειες API της οικογένειας εξαγωγής φορτωμένων εγγράφων βρίσκονται στη σελίδα προϊόντος HotPDF Delphi PDF component