Fiecare extractor geometric de text ghicește. Citește glifele pe care le desenează o pagină, le sortează după linia de bază și poziția orizontală și speră că aranjarea vizuală se potrivește cu ordinea în care ar citi un om. Pe un raport cu o singură coloană ghicirea este corectă. Pe un articol de revistă cu două coloane, un formular cu o bară laterală sau un tabel ale cărui celule au fost emise coloană cu coloană, este greșit în moduri greu de observat și scump de descoperit în aval. HotPDF răspunde la asta cu ExtractLoadedPageStructureText, care ignoră complet geometria: parcurge arborele de structură al documentului în ordinea de autor definită de ISO 32000-1 §14.8.4, apoi reasamblează glifele paginii după identificatorul lor de conținut marcat. Pentru un PDF marcat aceasta nu este o euristică, este ordinea pe care aplicația producătoare a declarat-o
Funcția returnează False când pagina nu are un arbore de structură folosibil, ceea ce este semnalul de a trece la extractorul geometric în loc să eșueze. Designul cu două căi contează mai mult decât algoritmul: intake-ul real de documente vede formulare guvernamentale marcate și rezultate de scaner în același dosar, iar un pipeline care gestionează doar unul dintre ele nu este un pipeline
De ce greșește extracția geometrică ordinea de lectură?
Pentru că un content stream PDF nu poartă deloc ordine de lectură. Este o secvență de operatori de desenare, iar un producător este liber să îi emită în orice secvență se potrivește motorului lui de layout. Procesoarele de text emit de obicei în ordinea fluxului, iar sortarea geometrică arată bine. Uneltele de layout, designerii de formulare și generatoarele de rapoarte frecvent nu: un subsol de pagină poate fi emis înaintea corpului, un tabel poate fi umplut coloană-major, iar o pagină cu două coloane poate împleti linii din ambele coloane pentru că compozitorul le-a rezolvat împreună
Modul de eșec este liniștit. Un extractor geometric nu raportează niciodată o eroare, doar returnează proză ale cărei propoziții sunt împletite din două coloane. Orice consumă acel text — un index de căutare, un mapper de câmpuri de e-factură, un pipeline de recuperare care hrănește un model de limbaj — moștenește dauna fără avertisment. HotPDF livrează de asemenea extractoarele geometrice pentru documente încărcate, și ele rămân unealta corectă pentru fișiere netagate; rostul căii în ordinea structurii este de a nu mai ghici când documentul poartă deja răspunsul
Ce stochează efectiv arborele de structură
Un PDF marcat păstrează o a doua descriere paralelă a paginii. Catalogul indică un /StructTreeRoot, ale cărui copii /K formează un arbore de elemente de structură: /Document, /Sect, /P, /Table, /TR, /TD și tot așa. Frunzele acelui arbore sunt referințe de conținut marcat, întregi care numesc un segment al content stream-ului paginii. Pe partea de conținut, acele segmente sunt deschise cu un operator BDC care poartă un /MCID și închise cu EMC. Fiecare element de structură poartă și o intrare /Pg care numește pagina căreia îi aparține, ceea ce face posibilă parcurgerea per pagină într-un document al cărui arbore de structură se întinde pe sute de pagini
HotPDF traversează acel arbore cu o limită de adâncime de 128 de niveluri și filtrează pe /Pg astfel încât doar pagina curentă contribuie. Rezultatul traversării nu este text, este o listă ordonată de valori MCID: ordinea de autor a segmentelor de conținut marcat de pe această pagină. Reasamblarea textului este apoi o chestiune de redare a glifelor în acea ordine
MCID este înregistrat în timpul extracției de glife, nu căutat după
Acesta este detaliul de implementare care face funcția ieftină. HotPDF înregistrează deja identificatorul de conținut marcat activ pe fiecare glifă extrasă, în câmpul MCID al lui THPDFGlyphRecord, deoarece interpretul de content stream știe ce scop BDC este deschis în momentul în care procesează fiecare operator Tj sau TJ. Extracția în ordinea structurii nu are deci nevoie de o a doua trecere peste content stream. Colectează secvența MCID din arborele de structură, apoi împarte în găleți glifele deja extrase după MCID și le emite în acea secvență
var
Pdf: THotPDF;
PageCount, I, Untagged: Integer;
PageText, AllText: UnicodeString;
Report: TStrings; // receptor de diagnostice deținut de apelant
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
// Ordine de autor direct din arborele de structură
if Untagged > 0 then
Report.Add(Format('page %d: %d glyphs outside the structure tree',
[I, Untagged]));
end
else
// Fără arbore de structură folosibil pe această pagină: rezervă geometrică
Pdf.ExtractLoadedPageText(I, PageText);
AllText := AllText + PageText + #13#10;
end;
finally
Pdf.Free;
end;
end;
Glifele netagate sunt numărate, niciodată pierdute în tăcere
O pagină poate fi parțial marcată. Producătorii adaugă o linie decorativă, un număr de pagină sau un filigran de ultimă oră în afara oricărui scop BDC, iar glifele acelea nu aparțin niciunui MCID. Pierderea lor ar fi implementarea îngrijită și cea greșită, deoarece aceeași lacună apare și când un producător marchează corpul, dar uită tabelul, și ați pierde tabelul fără să observați
HotPDF adaugă glifele nerevendicate ca o coadă geometrică după textul ordonat structural și raportează numărul lor prin parametrul de ieșire UntaggedGlyphCount. Numărul acela este un semnal de calitate pe care puteți acționa. Câteva glife pe o pagină de două mii sunt mobilier de pagină și pot fi ignorate. Patruzeci la sută din pagină în afara arborelui de structură înseamnă că marcajul este decorativ, iar extractorul geometric este răspunsul mai onest pentru acel fișier
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);
// Aveți încredere în arborele de structură doar când revendică majoritatea paginii
if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
begin
UsedStructure := True;
Result := True;
Exit;
end;
end;
Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;
Ce face funcția să returneze False
Trei cazuri, și merită distinse deoarece doar unul dintre ele este un defect al documentului. Primul este un PDF netagat obișnuit: niciun /StructTreeRoot, nimic de parcurs, iar False este pur și simplu adevărul. Al doilea este o pagină scanată al cărei text vine dintr-un strat OCR care nu a fost niciodată marcat. Al treilea este cel interesant: conținut care poartă operatori BDC cu valori /MCID, dar a cărui pagină nu are nicio intrare /StructParents, iar arborele lui de structură nu referențiază niciodată acei identificatori. Conținutul marcat există, partea de structură nu, și nu există nicio ordine de recuperat. HotPDF raportează False în loc să inventeze una
Ultimul caz apare în fișiere editate manual și în rezultatele uneltelor care emit conținut marcat în scopuri de optional-content sau artifact fără a construi un arbore de structură. Dacă produceți dumneavoastră PDF-uri marcate, aceeași asimetrie este ceea ce verifică validarea PDF/UA, iar contrapartea din partea scriitorului este tratată în DOM-ul de layout care emite rezultate marcate și paginate
Unde ordinea structurală își plătește singură prețul
Auditul de accesibilitate este cel evident: dacă certificați un document contra PDF/UA, ordinea de lectură pe care un cititor de ecran o va anunța este exact ordinea structurală, deci extragerea ei este felul în care o revizuiți fără un cititor de ecran. Captura de date este cazul comercial mai mare. Formularele guvernamentale marcate, divulgările reglementate și atașamentele de e-factură poartă etichete de câmp și valori în ordinea declarată, iar citirea lor în acea ordine elimină o clasă întreagă de erori de cartografiere pe care extracția geometrică le creează pe layouturi multi-coloană
Cel mai nou consumator este recuperarea pentru modele de limbaj. Fragmentarea unui document pentru embedding este bună doar cât ordinea textului, iar un fragment care împletește două coloane produce propoziții care nu au existat niciodată. Extracția în ordinea structurii este remedierea cea mai ieftină disponibilă pentru asta, deoarece pentru documentele marcate ordinea corectă este deja în fișier și are nevoie doar să fie citită
HotPDF este o componentă VCL nativă pentru Delphi și C++Builder, deci traversarea arborelui de structură și redarea glifelor rulează ambele în proces pe un document încărcat fără niciun randator extern implicat. Detaliile complete de API pentru familia de extracție din documente încărcate sunt pe pagina de produs HotPDF Delphi PDF component