PDFiumPas returnerer sidetekst som en struktur frem for en streng. GetStructuredText producerer en TPdfStructuredTextPage, der indeholder blokke, hver med linjer, hver med formaterede spans, med sidebaserede grænser på hvert niveau og de originale tegnindekser bevaret, så ethvert fragment kan afbildes tilbage til den underliggende tekstside
Den flade streng-udtrækning, de fleste kodebaser starter med, er der stadig og stadig korrekt til sit formål. Den holder op med at være nok, i det øjeblik du skal vide, hvilke ord der var en overskrift, hvilke der hørte til venstre spalte, eller hvor på siden en match faktisk sidder
Hvorfor er en flad streng det forkerte output til de fleste opgaver?
Fordi de spørgsmål, folk stiller til udtrukket tekst, næsten aldrig er "hvilke tegn er på denne side". De er "hvad er titlen", "er dette en tabel", "hører dette afsnit til afsnit 4", "hvor tegner jeg fremhævningen". Én streng besvarer ingen af dem, og hvert svar, du genopbygger ud fra den, er en heuristik, du nu ejer
To-spalters layouts gør pointen konkret. Udtræk en to-spalters artikel som en streng, og afhængigt af hvordan producenten skrev indholdsstrømmen, kan du få spalte ét efterfulgt af spalte to, eller du kan få linje ét i spalte ét, linje ét i spalte to, linje to i spalte ét, og så videre ned ad siden. Begge kommer ud af en konform PDF. Ingen af dem er forkerte på formatniveau, fordi PDF beskriver mærker på en side, ikke en dokumentdisposition. En blokbaseret model lader udtrækkeren træffe rækkefølgesbeslutningen eksplicit og fortælle dig, hvilken beslutning den traf
Indholdsrækkefølge eller fysisk layout?
TPdfStructuredTextOptions.ReadingOrder vælger mellem roContentOrder og roPhysicalLayout, og det rigtige svar afhænger af, hvad du stoler mest på, producenten eller geometrien
Indholdsrækkefølge returnerer tekst i den rækkefølge, indholdsstrømmen tegner den. Det er hurtigt, og for dokumenter genereret af en velopdragen producent typisk den tilsigtede læserækkefølge. Fysisk layout ignorerer strømsekvensen og genopbygger rækkefølge ud fra, hvor tegnene faktisk sidder, ved at klynge til linjer og derefter til spalter. Det er, hvad du vil have til scannet-og-OCR'et sider, til output fra værktøjer, der udsender tekst i skrifttyperækkefølge frem for læserækkefølge, og til alt, hvor det visuelle resultat er det eneste, du kan stole på
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;
Hvad tilføjer tagging, som geometri ikke kan?
Hensigt. Med IncludeSemantics aktiveret bærer blokke fra en tagget PDF et Kind, hentet fra strukturtræet, så en overskrift er en overskrift, fordi producenten sagde det, ikke fordi dens skrifttype var større end gennemsnittet. Typerne dækker de former, der betyder noget for genbrug: cfParagraph, cfHeading med et HeadingLevel, cfListItem, cfTableCell, cfCaption, cfFigure og det utaggede fallback cfPlain
Feltet Source registrerer, hvor hver klassifikation kom fra, rosStructure for strukturtræet og rosHeuristic for udledning, hvilket er feltet at logge, når du beslutter, hvor langt du skal stole på en udtrækningspipeline på tværs af et dokumentsæt. Figurer er et specialtilfælde, det er værd at kende: for en cfFigure-blok kommer teksten fra den alternative beskrivelse frem for fra nogen glyffer, da en figur ikke har egne tegn. Umatchet alternativ tekst er stadig repræsenteret i stedet for at blive droppet, hvilket er det, der lader en tilgængelighedsaudit se, at en beskrivelse eksisterer, selv når intet på siden tegner den. Selve taggingmodellen dækkes i PDF/UA-strukturtræ-validering
Spans bærer formateringen og oprindelsen
Hver TPdfStructuredTextSpan holder sin tekst, sine sidebaserede grænser, FontName, FontSize, FontWeight og Angle, plus SourceStartIndex og SourceCharacterCount. Spans brydes, hvor formatering ændrer sig, så en sætning med tre fede ord bliver til tre spans, og genopbygning af fremhævning i HTML eller Markdown er et spørgsmål om at læse egenskaber frem for at gætte ud fra skrifttypenavne
De to kilde-indeks-felter er dem, der gør udtrækning til en funktion frem for en rapport. De peger tilbage ind i sidens tegnsekvens, hvilket betyder, at en blok, du matchede i en søgning, kan konverteres til tegn-niveau-markeringsgeometri eller et fremhævningsrektangel uden en anden, anderledes rækkefølgesat gennemløbning af teksten; mekanikken beskrives i visuel tekstlinjemarkering med tegnbokse. Feltet Angle betyder mere, end det ser ud til: roteret tekst i et stempel eller et vandmærke lander i samme koordinatsystem som brødtekst, og en pipeline, der ignorerer vinkel, vil gerne smelte et diagonalt "DRAFT" sammen med midten af et afsnit
Budget, og de to kvalitetstællere
MaxCharacters er et fail-closed-budget, ikke en afkortningsindstilling: en side, der overskrider det, stopper frem for lydløst at returnere en del af indholdet. På en ubetroet indtagelsessti er det den adfærd, du ønsker, fordi en side med en million tegn enten er et maskingenereret uhyre eller et forsøg på at gøre din udtrækker til den langsomste del af systemet
To tællere på den returnerede side beskriver udtrækningskvalitet direkte. UnmappedCharacterCount tæller tegn uden nogen brugbar Unicode-afbildning, hvilket er det klassiske symptom på en subset-skrifttype, indlejret uden et /ToUnicode-CMap; sådan tekst gengives perfekt og udtrækkes som intet brugbart. GeometryFailureCount tæller tegn, hvis afgrænsningsboks ikke kunne bestemmes, hvilket forringer den fysiske-layout-rækkefølge. Log begge. Et dokumentsæt, hvor de tal konsekvent er tæt på nul, kan indekseres med tillid, og et, hvor de ikke er det, fortæller dig, at nogle producenter i din pipeline har brug for opmærksomhed, før noget nedstrøms resultat kan stoles på
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;
Ydelse på rigtige sider
Fysisk-layout-udtrækning er den dyre tilstand, og implementeringen er bygget til sider, der faktisk er store: tegnordning kører i O(n log n) frem for ved gentagen scanning, linje- og span-buffere vokser geometrisk i stedet for at blive reallokeret pr. tegn, Unicode-tekst bygges i buffere frem for ved strengsammenkædning, og skrifttypeopslag for tilstødende tekstobjekter caches. Den kombination er det, der holder en tæt 5.000-tegns-side forudsigelig i stedet for kvadratisk
For et sidetal-tungt job er det stadig værd at vælge den billigere tilstand, hvor du kan. Brug roContentOrder med semantik aktiveret til taggede dokumenter, du stoler på, og reservér roPhysicalLayout til det scannede og legacy-materiale, hvor geometri er det eneste signal. Hvis alt, du har brug for, er en almindelig streng, forbliver den simplere API, beskrevet i udtrækning af tekst fra PDF-dokumenter, den hurtigere sti, og når du skal spore tekst tilbage til markeret-indhold-identifikatorer, dækker læsning og skrivning af BDC- og MCID-markeret indhold det lag
Blokmodellen afbildes også rent på det, hentningspipelines vil have: en overskrift med sine afsnit er en chunk med en titel, og grænserne lader et citat pege på en placering på en side frem for på et dokument. PDFiumPas er en Delphi- og Lazarus-komponent omkring PDFium-motoren, dokumenteret med eksempler på PDFium's produktside for Delphi-komponenten