PDFiumPas geeft paginatekst terug als structuur in plaats van als string. GetStructuredText produceert een TPdfStructuredTextPage met blokken, elk met regels, elk met gestileerde spans, met paginaruimte-grenzen op elk niveau en de bronkarakterindexen bewaard zodat elk fragment terug te herleiden is naar de onderliggende tekstpagina
De platte-string-extractie waar de meeste code mee begint, is er nog steeds en nog steeds correct voor haar doel. Ze houdt op voldoende te zijn zodra u moet weten welke woorden een kop waren, welke tot de linkerkolom behoorden, of waar op de pagina een treffer zich daadwerkelijk bevindt
Waarom is een platte string de verkeerde output voor de meeste taken?
Omdat de vragen die mensen aan geëxtraheerde tekst stellen bijna nooit "welke tekens staan op deze pagina" zijn. Ze zijn "wat is de titel", "is dit een tabel", "hoort deze alinea bij sectie 4", "waar teken ik de markering". Eén enkele string beantwoordt geen daarvan, en elk antwoord dat u eruit reconstrueert, is een heuristiek die nu van u is
Tweekolomslay-outs maken het punt concreet. Extraheer een tweekoloms artikel als string en, afhankelijk van hoe de producent de content stream schreef, krijgt u misschien kolom één gevolgd door kolom twee, of misschien regel één van kolom één, regel één van kolom twee, regel twee van kolom één, enzovoort de pagina af. Beide komen uit een conforme PDF. Geen van beide is fout op formaatniveau, want PDF beschrijft markeringen op een pagina, geen documentoverzicht. Een blokgebaseerd model laat de extractor de volgordekeuze expliciet maken en u vertellen welke keuze hij maakte
Inhoudsvolgorde of fysieke lay-out?
TPdfStructuredTextOptions.ReadingOrder kiest tussen roContentOrder en roPhysicalLayout, en het juiste antwoord hangt af van wat u meer vertrouwt, de producent of de geometrie
Inhoudsvolgorde geeft tekst terug in de volgorde waarin de content stream haar tekent. Dat is snel, en voor documenten gegenereerd door een goed gedragende producent doorgaans de bedoelde leesvolgorde. Fysieke lay-out negeert de streamvolgorde en reconstrueert de volgorde vanuit waar de tekens daadwerkelijk staan, geclusterd in regels en dan in kolommen. Dat is wat u wilt voor gescande en vervolgens OCR'de pagina's, voor output van tools die tekst in fontvolgorde in plaats van leesvolgorde uitspuwen, en voor alles waar het visuele resultaat het enige is waarop u kunt vertrouwen
uses
PDFium;
var
Pdf: TPdf;
Options: TPdfStructuredTextOptions;
Page: TPdfStructuredTextPage;
B, L: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'article.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 1; // 1-based
Options := TPdfStructuredTextOptions.Default;
Options.ReadingOrder := roPhysicalLayout;
Options.IncludeFontInfo := True;
Options.IncludeSemantics := True;
Options.MaxCharacters := 200000; // fail-closed budget
Page := Pdf.GetStructuredText(Options);
for B := 0 to High(Page.Blocks) do
begin
if Page.Blocks[B].Kind = cfHeading then
Emit(Format('H%d: %s',
[Page.Blocks[B].HeadingLevel, Page.Blocks[B].Text]))
else
for L := 0 to High(Page.Blocks[B].Lines) do
Emit(Page.Blocks[B].Lines[L].Text);
end;
finally
Pdf.Free;
end;
end;
Wat voegt tagging toe dat geometrie niet kan?
Intentie. Met IncludeSemantics ingeschakeld, dragen blokken uit een getagde PDF een Kind afkomstig uit de structuurboom, zodat een kop een kop is omdat de producent dat zei, niet omdat haar font groter dan gemiddeld was. De soorten dekken de vormen die van belang zijn voor hergebruik: cfParagraph, cfHeading met een HeadingLevel, cfListItem, cfTableCell, cfCaption, cfFigure en de ongetagde terugval cfPlain
Het veld Source registreert waar elke classificatie vandaan kwam, rosStructure voor de structuurboom en rosHeuristic voor gevolgtrekking, en dat is het veld om te loggen wanneer u beslist hoeveel vertrouwen u aan een extractiepijplijn geeft over een documentenset heen. Figuren zijn een bijzonder geval om te kennen: voor een cfFigure-blok komt de tekst uit de alternatieve beschrijving in plaats van uit glyphs, aangezien een figuur geen eigen tekens heeft. Niet-gekoppelde alternatieve tekst wordt nog steeds weergegeven in plaats van weggelaten, en dat is wat een toegankelijkheidsaudit laat zien dat een beschrijving bestaat, zelfs wanneer niets op de pagina haar tekent. Het taggingmodel zelf staat behandeld in PDF/UA-structuurboomvalidatie
Spans dragen de stijl en de herkomst
Elke TPdfStructuredTextSpan draagt haar tekst, haar paginaruimte-grenzen, FontName, FontSize, FontWeight en Angle, plus SourceStartIndex en SourceCharacterCount. Spans breken waar de stijl verandert, dus een zin met drie vetgedrukte woorden wordt drie spans, en het herbouwen van nadruk in HTML of Markdown is een kwestie van eigenschappen lezen in plaats van raden op basis van fontnamen
De twee bronindexvelden zijn degene die extractie in een functie veranderen in plaats van in een rapport. Ze wijzen terug naar de tekenreeks van de pagina, wat betekent dat een blok dat u in een zoekopdracht matchte, omgezet kan worden in tekenniveau-selectiegeometrie of een markeringsrechthoek zonder een tweede, anders geordende doorgang over de tekst; de mechaniek staat beschreven in visuele tekstregelselectie met tekenkaders. Het veld Angle is belangrijker dan het lijkt: geroteerde tekst in een stempel of watermerk komt in dezelfde coördinatenruimte terecht als hoofdtekst, en een pijplijn die hoek negeert, zal een diagonale "DRAFT" met plezier middenin een alinea samenvoegen
Budget, en de twee kwaliteitstellers
MaxCharacters is een fail-closed budget, geen afkapinstelling: een pagina die het overschrijdt, stopt in plaats van stilzwijgend een deel van de inhoud terug te geven. Op een niet-vertrouwd intakepad is dat het gedrag dat u wilt, want een pagina met een miljoen tekens is ofwel een machinaal gegenereerd monster ofwel een poging om uw extractor het traagste onderdeel van het systeem te maken
Twee tellers op de teruggegeven pagina beschrijven de extractiekwaliteit rechtstreeks. UnmappedCharacterCount telt tekens zonder bruikbare Unicode-mapping, het klassieke symptoom van een subset-font ingesloten zonder /ToUnicode-CMap; zulke tekst rendert perfect en extraheert als niets bruikbaars. GeometryFailureCount telt tekens waarvan het begrenzingskader niet bepaald kon worden, wat de volgorde bij fysieke lay-out degradeert. Log beide. Een documentenset waar die getallen consequent dicht bij nul liggen, kan met vertrouwen geïndexeerd worden, en een set waar dat niet zo is, vertelt u dat sommige producenten in uw pijplijn aandacht nodig hebben voordat enig downstream-resultaat betrouwbaar is
var
Page: TPdfStructuredTextPage;
B, S, L: Integer;
Emphasised: Boolean;
begin
Page := Pdf.GetStructuredText(Options);
if Page.UnmappedCharacterCount > 0 then
Log(Format('page %d: %d characters without a Unicode mapping',
[Page.PageNumber, Page.UnmappedCharacterCount]));
if Page.GeometryFailureCount > 0 then
Log(Format('page %d: %d characters without geometry',
[Page.PageNumber, Page.GeometryFailureCount]));
for B := 0 to High(Page.Blocks) do
for L := 0 to High(Page.Blocks[B].Lines) do
for S := 0 to High(Page.Blocks[B].Lines[L].Spans) do
begin
Emphasised := Page.Blocks[B].Lines[L].Spans[S].FontWeight >= 600;
AppendRun(Page.Blocks[B].Lines[L].Spans[S].Text, Emphasised,
Page.Blocks[B].Lines[L].Spans[S].SourceStartIndex);
end;
end;
Prestaties op echte pagina's
Extractie met fysieke lay-out is de dure modus, en de implementatie is gebouwd voor pagina's die werkelijk groot zijn: tekenordening draait in O(n log n) in plaats van via herhaald scannen, regel- en spanbuffers groeien geometrisch in plaats van per teken te herallokeren, Unicode-tekst wordt in buffers opgebouwd in plaats van door stringconcatenatie, en fontopzoekingen voor aangrenzende tekstobjecten worden gecachet. Die combinatie is wat een dichte pagina van 5.000 tekens voorspelbaar houdt in plaats van kwadratisch
Voor een job met veel pagina's loont het nog altijd om waar mogelijk de goedkopere modus te kiezen. Gebruik roContentOrder met semantiek ingeschakeld voor getagde documenten die u vertrouwt, en bewaar roPhysicalLayout voor het gescande en legacy-materiaal waar geometrie het enige signaal is. Als u alleen een platte string nodig heeft, blijft de eenvoudigere API beschreven in tekst uit PDF-documenten extraheren het snellere pad, en wanneer u tekst moet herleiden tot marked-content-identifiers, behandelt BDC en MCID marked content lezen en schrijven die laag
Het blokmodel past ook netjes op wat retrievalpijplijnen willen: een kop met haar alinea's is een chunk met een titel, en de grenzen laten een citaat naar een locatie op een pagina wijzen in plaats van naar een document. PDFiumPas is een Delphi- en Lazarus-component rond de PDFium-engine, gedocumenteerd met voorbeelden op de PDFium Delphi-componentpagina