Techninis straipsnis

Struktūros eiliškumo PDF teksto ištraukimas Delphi su HotPDF

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

Dvistulpio PDF puslapio palyginimas, rodantis pagal pagrindą surūšiuotą geometrinę ištrauką, supinantį stulpelius, prieš struktūros eiliškumo MCID ištrauką HotPDF
Glifų rūšiavimas pagal pagrindą supina du stulpelius į beprasmį tekstą, o struktūros medis atkartoja gamintojo deklaruotą tvarką

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ų

PDF struktūros medžio anatomija, jungianti StructTreeRoot elementus, tokius kaip Sect, Table, TR ir TD, su BDC MCID iškarpomis HotPDF puslapio turinio sraute
Medžio lapai yra pažymėto turinio nuorodos, o kiekvienas elementas neša Pg įrašą, leidžiantį apėjimui filtruoti iki esamo puslapio

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

Sprendimų eiga HotPDF struktūros teksto ištraukai su geometrine atsarga, kai puslapis neturi tinkamo struktūros medžio arba turi dekoratyvinį žymėjimą
True reiškia struktūros tvarką su pridėta nepažymėta uodega, o False nukreipia puslapį geometriniam ištraukėjui, vietoj nepavykimo
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