PDFiumPas returnerer sidetekst som en struktur fremfor en streng. GetStructuredText produserer en TPdfStructuredTextPage som inneholder blokker, hver med linjer, hver med stilte spenn, med grenser i sideplassen på hvert nivå, og kildens tegnindekser bevart slik at ethvert fragment kan mappes tilbake til den underliggende tekstsiden
Den flate strenguttrekkingen de fleste koder starter med, er fortsatt der og fortsatt korrekt for sitt formål. Den slutter å være nok i det øyeblikket du trenger å vite hvilke ord som var en overskrift, hvilke som hørte til venstre spalte, eller hvor på siden et treff faktisk befinner seg
Hvorfor er en flat streng feil utdata for de fleste jobber?
Fordi spørsmålene folk stiller til uttrukket tekst, nesten aldri er «hvilke tegn er på denne siden». De er «hva er tittelen», «er dette en tabell», «hører dette avsnittet til del 4», «hvor skal jeg tegne markeringen». En enkelt streng svarer på ingen av dem, og hvert svar du rekonstruerer fra den, er en heuristikk du nå eier
To-spalte-layouter gjør poenget konkret. Trekk ut en to-spalte-artikkel som en streng, og avhengig av hvordan produsenten skrev innholdsstrømmen, kan du få spalte én etterfulgt av spalte to, eller du kan få linje én i spalte én, linje én i spalte to, linje to i spalte én, og så videre nedover siden. Begge kommer ut av en konform PDF. Ingen av dem er feil på formatnivå, fordi PDF beskriver merker på en side, ikke en dokumentdisposisjon. En blokkbasert modell lar uttrekkeren ta rekkefølgebeslutningen eksplisitt og fortelle deg hvilken beslutning den tok
Innholdsrekkefølge eller fysisk layout?
TPdfStructuredTextOptions.ReadingOrder velger mellom roContentOrder og roPhysicalLayout, og det riktige svaret avhenger av hva du stoler mest på, produsenten eller geometrien
Innholdsrekkefølge returnerer tekst i den sekvensen innholdsstrømmen tegner den. Det er raskt, og for dokumenter generert av en veloppdragen produsent, vanligvis den tiltenkte leserekkefølgen. Fysisk layout ignorerer strømsekvensen og rekonstruerer rekkefølgen fra hvor tegnene faktisk befinner seg, ved å klynge dem inn i linjer og deretter inn i spalter. Det er det du vil ha for sider som er skannet og deretter OCR-behandlet, for utdata fra verktøy som skriver ut tekst i skriftrekkefølge fremfor leserekkefølge, og for alt der det visuelle resultatet 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-basert
Options := TPdfStructuredTextOptions.Default;
Options.ReadingOrder := roPhysicalLayout;
Options.IncludeFontInfo := True;
Options.IncludeSemantics := True;
Options.MaxCharacters := 200000; // fail-closed budsjett
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;
Hva legger tagging til som geometri ikke kan?
Intensjon. Med IncludeSemantics aktivert bærer blokker fra en tagget PDF en Kind hentet fra strukturtreet, slik at en overskrift er en overskrift fordi produsenten sa det, ikke fordi skriften var større enn gjennomsnittet. Typene dekker formene som betyr noe for gjenbruk: cfParagraph, cfHeading med en HeadingLevel, cfListItem, cfTableCell, cfCaption, cfFigure og den utaggede reserveløsningen cfPlain
Feltet Source registrerer hvor hver klassifisering kom fra, rosStructure for strukturtreet og rosHeuristic for slutning, som er feltet å logge når du avgjør hvor langt du kan stole på en uttrekkingspipeline på tvers av et dokumentsett. Figurer er et spesialtilfelle verdt å kjenne til: for en cfFigure-blokk kommer teksten fra den alternative beskrivelsen fremfor fra noen glyffer, siden en figur ikke har egne tegn. Umatchet alternativ tekst representeres likevel i stedet for å droppes, som er det som lar en tilgjengelighetsrevisjon se at en beskrivelse finnes selv når ingenting på siden tegner den. Selve taggingmodellen dekkes i validering av PDF/UA-strukturtreet
Spenn bærer stilingen og opprinnelsen
Hvert TPdfStructuredTextSpan holder sin tekst, sine grenser i sideplassen, FontName, FontSize, FontWeight og Angle, pluss SourceStartIndex og SourceCharacterCount. Spenn brytes der stilingen endres, slik at en setning med tre uthevede ord blir tre spenn, og å gjenoppbygge utheving i HTML eller Markdown er et spørsmål om å lese egenskaper fremfor å gjette ut fra skriftnavn
De to kildeindeksfeltene er de som gjør uttrekking om til en funksjon fremfor en rapport. De peker tilbake inn i sidens tegnsekvens, noe som betyr at en blokk du matchet i et søk, kan konverteres til geometri for tegnnivå-utvalg eller et markeringsrektangel uten en andre, annerledes ordnet gjennomgang av teksten; mekanikken er beskrevet i visuelt tekstlinjeutvalg med tegnbokser. Feltet Angle betyr mer enn det ser ut som: rotert tekst i et stempel eller et vannmerke havner i samme koordinatrom som brødtekst, og en pipeline som ignorerer vinkelen, vil villig fusjonere en diagonal «DRAFT» midt inn i et avsnitt
Budsjett, og de to kvalitetstellerne
MaxCharacters er et fail-closed-budsjett, ikke en avkuttingsinnstilling: en side som overskrider det, stopper i stedet for stille å returnere en del av innholdet. På en utiltrodd mottaksvei er det atferden du vil ha, fordi en side med en million tegn enten er et maskingenerert monster eller et forsøk på å gjøre uttrekkeren din til den tregeste delen av systemet
To tellere på den returnerte siden beskriver uttrekkingskvaliteten direkte. UnmappedCharacterCount teller tegn uten brukbar Unicode-mapping, som er det klassiske symptomet på en subsett-skrift bygget inn uten et /ToUnicode-CMap; tekst av den typen rendres perfekt og trekkes ut som ingenting brukbart. GeometryFailureCount teller tegn hvis avgrensningsboks ikke kunne bestemmes, noe som forringer den fysiske layout-rekkefølgen. Logg begge. Et dokumentsett der disse tallene konsekvent ligger nær null, kan indekseres med selvtillit, og ett der de ikke gjør det, forteller deg at noen produsenter i pipelinen din trenger oppmerksomhet før noe 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;
Ytelse på reelle sider
Fysisk-layout-uttrekking er den kostbare modusen, og implementasjonen er bygget for sider som faktisk er store: tegnrekkefølgen kjører i O(n log n) fremfor ved gjentatt skanning, linje- og spennbuffere vokser geometrisk i stedet for å reallokeres per tegn, Unicode-tekst bygges i buffere fremfor ved strengsammenslåing, og skriftoppslag for tilstøtende tekstobjekter caches. Den kombinasjonen er det som holder en tett side med 5000 tegn forutsigbar i stedet for kvadratisk
For en jobb med mange sider lønner det seg fortsatt å velge den billigere modusen der du kan. Bruk roContentOrder med semantikk aktivert for taggede dokumenter du stoler på, og reserver roPhysicalLayout for skannet og eldre materiale der geometri er det eneste signalet. Hvis alt du trenger er en vanlig streng, forblir det enklere API-et beskrevet i uttrekking av tekst fra PDF-dokumenter den raskere veien, og når du trenger å spore tekst tilbake til merket-innhold-identifikatorer, dekker lesing og skriving av BDC- og MCID-merket innhold det laget
Blokkmodellen mapper også rent til det hentepipeliner ønsker seg: en overskrift med sine avsnitt er en bit med en tittel, og grensene lar et sitat peke på en plassering på en side fremfor på et dokument. PDFiumPas er en komponent for Delphi og Lazarus bygget rundt PDFium-motoren, dokumentert med eksempler på PDFium sin side for Delphi-komponent