Technický článek

Extrakce textu PDF v pořadí struktury v Delphi s HotPDF

Každý geometrický extraktor textu hádá. Čte glyfy, které stránka kreslí, řadí je podle baseline a vodorovné pozice a doufá, že vizuální uspořádání odpovídá pořadí, v jakém by člověk četl. U jednosloupcové sestavy je ten tip správně. U dvousloupcového článku časopisu, formuláře s postranní lištou nebo tabulky, jejíž buňky byly vypouštěny sloupec po sloupci, je chybně způsoby, které je těžké si všimnout a drahé odhalit downstream. HotPDF na to odpovídá ExtractLoadedPageStructureText, která ignoruje geometrii zcela: projde strom struktury dokumentu v pořadí autorství definovaném ISO 32000-1 §14.8.4 a poté složí glyfy stránky podle jejich marked-content identifikátoru. Pro tagované PDF to není heuristika, je to pořadí, které producentská aplikace deklarovala

Funkce vrací False, když stránka nemá použitelný strom struktury, což je signál spadnout zpět na geometrický extraktor, nikoli selhat. Toto dvoucestné uspořádání je důležitější než algoritmus: reálný příjem dokumentů vidí tagované vládní formuláře a výstup skeneru ve stejné složce a potrubí, které zvládá jen jedno z nich, není potrubí

Proč geometrická extrakce dostává pořadí čtení špatně?

Protože content stream PDF nenese žádné pořadí čtení. Je to sekvence kreslicích operátorů a producent je smí vypouštět v jakékoli sekvenci, která vyhovuje jeho vlastnímu layout enginu. Textové procesory obvykle vypouštějí v pořadí toku a geometrické řazení vypadá dobře. Layout nástroje, návrháři formulářů a generátory sestav často ne: zápatí stránky může být vypuštěno před tělem, tabulka může být plněna po sloupcích a dvousloupcová stránka může proplétat řádky z obou sloupců, protože composer je rozřešil dohromady

Srovnání dvousloupcové stránky PDF ukazující geometrickou extrakci řazenou podle baseline splétající sloupce proti extrakci MCID v pořadí struktury v HotPDF
Řazení glyfů podle baseline proplétá dva sloupce do nesmyslu, zatímco strom struktury přehraje pořadí, které producent deklaroval

Režim selhání je tichý. Geometrický extraktor nikdy nehlásí chybu, jen odevzdá prózu, jejíž věty jsou splétané ze dvou sloupců. Cokoli, co tento text konzumuje — vyhledávací index, mapper polí e-faktury, retrieval potrubí živící jazykový model — dědí škodu bez varování. HotPDF dodává také geometrické extraktory pro načtené dokumenty a ty zůstávají správným nástrojem pro netagované soubory; smysl cesty v pořadí struktury je přestat hádat, když už dokument nese odpověď

Co strom struktury skutečně uchovává

Tagované PDF drží druhý, rovnoběžný popis stránky. Katalog ukazuje na /StructTreeRoot, jehož děti /K tvoří strom prvků struktury: /Document, /Sect, /P, /Table, /TR, /TD a tak dále. Listy toho stromu jsou reference na marked content, celá čísla pojmenovávající úsek content streamu stránky. Na straně obsahu jsou tyto úseky otevřeny operátorem BDC nesoucím /MCID a zavřeny EMC. Každý prvek struktury také nese položku /Pg pojmenovávající stránku, do níž patří, což umožňuje procházení po stránkách v dokumentu, jehož strom struktury zabírá stovky stránek

Anatomie stromu struktury PDF vážící prvky StructTreeRoot jako Sect, Table, TR a TD na úseky BDC MCID v content streamu stránky HotPDF
Listy stromu jsou reference na marked content a každý prvek nese položku Pg, jež nechá průchod filtrovat na aktuální stránku

HotPDF projde ten strom se stropem hloubky 128 úrovní a filtruje na /Pg, takže přispívá jen aktuální stránka. Výstupem procházení není text, je to uspořádaný seznam hodnot MCID: pořadí autorství úseků marked content na této stránce. Složení textu je pak záležitostí přehrání glyfů v tom pořadí

MCID se zaznamenává při extrakci glyfů, nedohledává se potom

Toto je detail implementace, který činí funkci levnou. HotPDF už zaznamenává aktivní identifikátor marked content na každém glyfu, který extrahuje, v poli MCID struktury THPDFGlyphRecord, protože interpret content streamu ví, který rozsah BDC je otevřen v okamžiku, kdy zpracovává každý operátor Tj nebo TJ. Extrakce v pořadí struktury tedy nepotřebuje druhý průchod nad content streamem. Sesbírá sekvenci MCID ze stromu struktury, pak rozdělí už extrahované glyfy do košů podle MCID a vypustí je v té sekvenci

var
  Pdf: THotPDF;
  PageCount, I, Untagged: Integer;
  PageText, AllText: UnicodeString;
  Report: TStrings;   // diagnostické úložiště vlastněné volajícím
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
        // Pořadí autorství přímo ze stromu struktury
        if Untagged > 0 then
          Report.Add(Format('page %d: %d glyphs outside the structure tree',
            [I, Untagged]));
      end
      else
        // Na této stránce není použitelný strom struktury: geometrický fallback
        Pdf.ExtractLoadedPageText(I, PageText);
      AllText := AllText + PageText + #13#10;
    end;
  finally
    Pdf.Free;
  end;
end;

Netagované glyfy se počítají, nikdy se potichu nezahazují

Stránka může být tagována částečně. Producenti přidávají ozdobnou linku, číslo stránky nebo pozdní vodoznak mimo jakýkoli rozsah BDC a tyto glyfy nepatří žádnému MCID. Zahodit je by byl uhlazená implementace a špatná, protože stejná mezera se objevuje i tehdy, když producent otaguje tělo a zapomene tabulku, a přišli byste o tabulku, aniž byste si toho všimli

HotPDF připojuje neuplatněné glyfy jako geometrický ocas za textem uspořádaným strukturou a hlásí jejich počet přes výstupní parametr UntaggedGlyphCount. To číslo je kvalitní signál, na který lze reagovat. Hrstka glyfů na stránce dvou tisíc je page furniture a lze ji ignorovat. Čtyřicet procent stránky mimo strom struktury znamená, že tagování je ozdobné a geometrický extraktor je pro ten soubor čestnější odpověď

Rozhodovací tok extrakce textu struktury HotPDF s geometrickým fallbackem, když stránka nemá použitelný strom struktury nebo má ozdobné tagování
True znamená pořadí struktury s připojeným netagovaným ocasem a False směruje stránku na geometrický extraktor namísto selhání
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);
    // Stromu struktury věřte jen tehdy, když si nárokuje většinu stránky
    if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
    begin
      UsedStructure := True;
      Result := True;
      Exit;
    end;
  end;
  Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;

Co způsobuje, že funkce vrací False

Tři případy a stojí za rozlišení, protože jen jeden z nich je defekt dokumentu. První je obyčejné netagované PDF: žádný /StructTreeRoot, nic k procházení a False je prostě pravda. Druhé je skenovaná stránka, jejíž text pochází z OCR vrstvy, která nikdy nebyla tagována. Třetí je zajímavý: obsah nesoucí operátory BDC s hodnotami /MCID, ale jehož stránka nemá položku /StructParents a jehož strom struktury tyto identifikátory nikdy neodkazuje. Marked content existuje, strana struktury ne a žádné pořadí k obnovení tu není. HotPDF hlásí False namísto jednoho vynalézání

Ten poslední případ se objevuje v ručně upravovaných souborech a ve výstupu nástrojů, které vypouštějí marked content pro účely optional content nebo artifact bez budování stromu struktury. Pokud sami produkujete tagovaná PDF, totéž asymetrii kontroluje validace PDF/UA a stranu writeru pokrývá layout DOM vypouštějící tagovaný, stránkovaný výstup

Kde se pořadí struktury vyplatí

Audit přístupnosti je ten zjevný: certifikujete-li dokument proti PDF/UA, pořadí čtení, které oznámí čtečka obrazovky, je přesně pořadí struktury, takže jeho extrakce je způsob, jak jej revidovat bez čtečky obrazovky. Sběr dat je větší komerční případ. Tagované vládní formuláře, regulované disclosure a přílohy e-faktur nesou názvy polí a hodnoty v deklarovaném pořadí a čtení v tom pořadí odstraňuje celou třídu chyb mapování, kterou geometrická extrakce vytváří na vícesloupcových layoutech

Nejnovějším konzumentem je retrieval pro jazykové modely. Dělení dokumentu do chunků pro embedding je jen tak dobré, jaké je pořadí textu, a chunk splétající dva sloupce produkuje věty, které nikdy neexistovaly. Extrakce v pořadí struktury je nejlevnější dostupná oprava, protože u tagovaných dokumentů je správné pořadí už v souboru a stačí jej přečíst

HotPDF je nativní komponenta VCL pro Delphi a C++Builder, takže procházení stromu struktury i přehrávání glyfů běží v procesu nad načteným dokumentem bez zapojení externího rendereru. Kompletní detaily API rodiny extrakce z načtených dokumentů jsou na stránce produktu HotPDF Delphi PDF component