Kiekvienas geometrinis teksto ištraukėjas spėlioja. Jis skaito glifus, kuriuos piešia puslapis, rūšiuoja juos pagal pagrindo liniją ir horizontalią padėtį ir tiki, kad vizualinis išdėstymas sutampa su tvarka, kurią skaitytų žmogus. Vienstulpėje ataskaitoje tas spėjimas teisingas. Dvistulpėje žurnalo straipsnyje, formoje su šonine juosta arba lentelėje, kurios langeliai skleisti stulpeliu po stulpelio, jis neteisingas taip, kad sunku pastebėti ir brangu atrasti toliau grandinėje. HotPDF į tai atsako ExtractLoadedPageStructureText, visiškai nepaisančia geometrijos: ji apeina dokumento struktūros medį autoriaus eiliškumu, kaip apibrėžta ISO 32000-1 §14.8.4, tada sujungia puslapio glifus pagal jų pažymėto turinio identifikatorių. Pažymėtam PDF tai nėra heuristika — tai tvarka, kurią deklaravo gaminanti programa
Funkcija grąžina False, kai puslapis neturi tinkamo naudoti struktūros medžio — tai signalas grįžti prie geometrinio ištraukėjo, o ne nepavykti. Tas dviejų kelių projektas svarbesnis už algoritmą: tikras dokumentų priėmimas tame pačiame aplanke mato pažymėtas valstybines formas ir skenerio išvestį, o konvejeris, kuris moka tik vieną iš jų, nėra konvejeris
Kodėl geometrinė ištrauka skaito tvarką neteisingai?
Nes PDF turinio srautas apskritai neneša jokios skaitymo tvarkos. Tai braižymo operatorių seka, ir gamintojas laisvas jas skleisti tokia seka, kokia tinka jo paties išdėstymo varikliui. Teksto rengyklės paprastai skleidžia srauto tvarka, ir geometrinis rūšiavimas atrodo gerai. Išdėstymo įrankiai, formų kūrėjai ir ataskaitų generatoriai dažnai ne: puslapio poraštė gali būti skleista prieš pagrindinį tekstą, lentelė gali būti pildoma stulpeliu pagrindu, o dvistulpis puslapis gali persipinti abiejų stulpelių eilutes, nes komponuotojas jas išsprendė kartu
Gedimo režimas tylus. Geometrinis ištraukėjas niekada nepraneša klaidos — jis tiesiog grąžina prozą, kurios sakiniai supinti iš dviejų stulpelių. Bet kas, vartojantis tą tekstą — paieškos indeksas, el. sąskaitų laukų atvaizduotojas, kalbos modelį maitinantis paieškos konvejeris — paveldi žalą be įspėjimo. HotPDF taip pat tiekia geometrinius ištraukėjus įkeltiems dokumentams, ir jie lieka tinkamu įrankiu nepažymėtiems failams; struktūros eiliškumo kelio prasmė — nustoti spėlioti, kai dokumentas jau neša atsakymą
Ką struktūros medis iš tikrųjų saugo
Pažymėtas PDF laiko antrą, lygiagretų puslapio aprašymą. Katalogas rodo į /StructTreeRoot, kurio /K vaikai sudaro struktūros elementų medį: /Document, /Sect, /P, /Table, /TR, /TD ir taip toliau. To medžio lapai yra pažymėto turinio nuorodos — sveikieji skaičiai, įvardijantys puslapio turinio srauto iškarpą. Turinio pusėje tos iškarpos atveriamos BDC operatoriumi, nešančiu /MCID, ir uždaromos EMC. Kiekvienas struktūros elementas taip pat neša /Pg įrašą, įvardijantį puslapį, kuriam jis priklauso — būtent todėl įmanomas apėjimas puslapiu puslapio dokumente, kurio struktūros medis driekiasi per šimtus puslapių
HotPDF apeina tą medį su 128 lygių gylio riba ir filtruoja pagal /Pg, kad prisidėtų tik esamas puslapis. Apėjimo išvestis nėra tekstas — tai surūšiuotas MCID reikšmių sąrašas: pažymėto turinio iškarpų autoriaus tvarka šiame puslapyje. Teksto sujungimas tada yra glifų atkartojimo toje tvarkoje reikalas
MCID užrašomas glifų ištraukos metu, o ne ieškomas vėliau
Tai įgyvendinimo detalė, padaranti savybę pigia. HotPDF jau užrašo aktyvų pažymėto turinio identifikatorių kiekvienam ištraukiamam glifui — THPDFGlyphRecord lauke MCID — nes turinio srauto interpretatorius žino, kuri BDC apimtis atidaryta tuo metu, kai jis apdoroja kiekvieną Tj arba TJ operatorių. Struktūros eiliškumo ištraukai todėl nereikia antro pravažiavimo per turinio srautą. Ji surenka MCID seką iš struktūros medžio, tada išdėlioja jau ištrauktus glifus į krepšelius pagal MCID ir skleidžia juos ta seka
var
Pdf: THotPDF;
PageCount, I, Untagged: Integer;
PageText, AllText: UnicodeString;
Report: TStrings; // iškviečiančiojo valdomas diagnostikos surinkėjas
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
// Autoriaus tvarka tiesiai iš struktūros medžio
if Untagged > 0 then
Report.Add(Format('page %d: %d glyphs outside the structure tree',
[I, Untagged]));
end
else
// Nėra tinkamo struktūros medžio šiame puslapyje: geometrinė atsarga
Pdf.ExtractLoadedPageText(I, PageText);
AllText := AllText + PageText + #13#10;
end;
finally
Pdf.Free;
end;
end;
Nepažymėti glifai skaičiuojami, niekada tyloje nenumetami
Puslapis gali būti iš dalies pažymėtas. Gamintojai prideda dekoratyvinę liniją, puslapio numerį arba vėlyvo etapo vandens ženklą už bet kokios BDC apimties ribų, ir tie glifai nepriklauso jokiam MCID. Jų numetimas būtų tvarkingas įgyvendinimas ir neteisingas, nes ta pati spraga pasirodo ir tada, kai gamintojas pažymi pagrindinį tekstą, bet užmiršta lentelę, ir jūs prarastumėte lentelę nepastebėję
HotPDF priedžia neatituktus glifus kaip geometrinę uodegą po struktūros eiliškumo tekstu ir praneša jų skaičių per UntaggedGlyphCount išvesties parametrą. Tas skaičius yra kokybės signalas, į kurį galima reaguoti. Saujelė glifų dviejų tūkstančių puslapyje yra puslapio baldai ir gali būti nepaisoma. Keturiasdešimt procentų puslapio už struktūros medžio ribų reiškia, kad žymėjimas dekoratyvinis, o geometrinis ištraukėjas tam failui yra sąžiningesnis atsakymas
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);
// Pasitikėkite struktūros medžiu tik tada, kai jis teigia daugumą puslapio
if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
begin
UsedStructure := True;
Result := True;
Exit;
end;
end;
Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;
Kas priverčia funkciją grąžinti False
Trys atvejai, ir jie verti atskyrimo, nes tik vienas iš jų yra dokumento defektas. Pirmasis yra paprastas nepažymėtas PDF: nėra /StructTreeRoot, nėra ko apeiti, o False yra tiesiog tiesa. Antrasis yra skenuotas puslapis, kurio tekstas ateina iš niekada nepažymėto OCR sluoksnio. Trečiasis — įdomusis: turinys, nešantis BDC operatorius su /MCID reikšmėmis, bet kurio puslapis neturi /StructParents įrašo ir kurio struktūros medis niekada nenurodo tų identifikatorių. Pažymėtas turinys egzistuoja, struktūros pusės — ne, ir nėra tvarkos, kurią atkurti. HotPDF praneša False, vietoj jos išgalvoti
Tas paskutinis atvejis pasirodo rankomis redaguotuose failuose ir įrankių išvestyje, skleidžiančioje pažymėtą turinį pasirinktamo turinio arba artefaktų tikslais, nesukūrusios struktūros medžio. Jeigu pats gaminate pažymėtus PDF, ta pati asimetrija yra tai, ką tikrina PDF/UA tikrinimas, o rašyklės pusės atitikmuo dengiamas išdėstymo DOM, skleidžiančiu pažymėtą, puslapiuotą išvestį
Kur struktūros tvarka atsiperka
Prieinamumo auditas yra akivaizdusis: jeigu sertifikuojate dokumentą pagal PDF/UA, skaitymo tvarka, kurią paskelbs ekrano skaitytuvas, yra lygiai struktūros tvarka, todėl jos ištraukimas yra būdas ją peržiūrėti be ekrano skaitytuvo. Duomenų fiksavimas yra didesnis komercinis atvejis. Pažymėtos valstybinės formos, reguliuojami atskleidimai ir el. sąskaitų priedai neša laukų etiketes ir reikšmes deklaruota tvarka, ir jų skaitymas ta tvarka pašalina visą atvaizdavimo klaidų klasę, kurią geometrinė ištrauka sukuria kelių stulpelių išdėstymuose
Naujausias vartotojas yra paieška kalbos modeliams. Dokumento dalijimas į gabalėlius įterpimui (embedding) yra tik toks geras, kokia tekstų tvarka, ir gabalėlis, supinantis du stulpelius, duoda sakinių, kurių niekada nebūta. Struktūros eiliškumo ištrauka yra pigiausias prieinamas tam ištaisymas, nes pažymėtuose dokumentuose teisinga tvarka jau yra faile ir tereikia ją perskaityti
HotPDF yra savoji VCL komponentė Delphi ir C++Builder aplinkoms, todėl struktūros medžio apėjimas ir glifų atkartojimas abu vyksta procese prieš įkeltą dokumentą be jokio išorinio atvaizduotojo. Pilnos API detalės įkeltų dokumentų ištraukos šeimai yra HotPDF Delphi PDF component produkto puslapyje