PDFium Component legger til et søkbart tekstlag i skannede PDF-sider fra Delphi gjennom ApplyOcrSearchLayer. Den gjengir hver valgte side, gir pikslene til en OCR-leverandør du selv sørger for, og skriver de gjenkjente ordene tilbake som usynlige tekstobjekter plassert over ordene i skanningen. Det originale sidebildet blir aldri dekodet, re-kodet eller erstattet, så det visuelle resultatet er byte-for-byte den siden du startet med
Gjenkjenningsmotoren er bevisst ikke en del av biblioteket. PDFium eksponerer sidegjengivelse, koordinatkartlegging, fontinnlasting, opprettelse av tekstobjekter og usynlige gjengivelsesmoduser, men den inneholder ingen OCR-motor, og å late som noe annet ville betydd å bunte noen andres gjenkjenningsprodukt inn i en PDF-komponent. I stedet bor gjenkjenning bak IPdfOcrProvider-grensesnittet: biblioteket sender fastlayout, øvre-opprinnelse BGRA-piksler, og leverandøren returnerer Unicode-tekst, konfidensverdier og ordfirkanter
Hva er egentlig et søkbart tekstlag?
En skannet PDF er et bilde av et dokument. Sideinnholdet er ett stort bilde, og det finnes ingenting å velge, søke i, kopiere eller indeksere. Et søkbart tekstlag legger til ekte tekstobjekter oppå det bildet med gjengivelsesmodusen satt til usynlig, slik at visningsklienter ikke tegner noe, men markering, søk og uthenting finner ordene nøyaktig der de opptrer
Posisjonering er hele spillet. Hvis den usynlige teksten sitter noen punkter feil, lander markeringsutheving ved siden av ordene i stedet for på dem, og å kopiere et avsnitt produserer tekst i feil rekkefølge. Det er derfor geometrien må komme fra de samme transformasjonene PDFium bruker for å gjengi siden, snarere enn fra en proporsjonal gjetning
Implementere leverandøren
Leverandørkontrakten er én metode. Den mottar en sidebilde-post som bærer dimensjoner, stride, DPI, pikselformat og selve pikselbytene, pluss et kanselleringstoken, og returnerer ord eller en feilmelding:
uses
PDFium;
type
TMyOcrProvider = class(TInterfacedObject, IPdfOcrProvider)
public
function RecognizePage(const Image: TPdfOcrImage;
const CancellationToken: IPdfCancellationToken;
out Words: TPdfOcrWords; out ErrorMessage: string): Boolean;
end;
function TMyOcrProvider.RecognizePage(const Image: TPdfOcrImage;
const CancellationToken: IPdfCancellationToken;
out Words: TPdfOcrWords; out ErrorMessage: string): Boolean;
var
I: Integer;
begin
// Image.Pixels inneholder BGRA-rader med øvre opprinnelse på Image.Stride byte.
// Gi dem til motoren din, og fyll deretter én oppføring per gjenkjent ord
SetLength(Words, RecognisedCount);
for I := 0 to RecognisedCount - 1 do
begin
Words[I].Text := EngineWordText(I);
Words[I].Confidence := EngineWordConfidence(I); // 0..1
Words[I].Quad := TPdfOcrQuad.FromRectangle(
EngineLeft(I), EngineTop(I), EngineRight(I), EngineBottom(I));
end;
ErrorMessage := '';
Result := True;
end;
Firkanter snarere enn rektangler, fordi en skanning sjelden er kvadratisk mot siden. Et ord på en litt rotert side opptar et parallellogram, og TPdfOcrQuad bærer fire hjørnepunkter, slik at skjeve og roterte ord beholder en nøyaktig markeringsregion. Motorer som bare rapporterer akse-justerte bokser, kan bruke FromRectangle, som bygger den degenererte firkanten
Hvorfor kan ikke ordposisjoner skaleres proporsjonalt?
Det er fristende å konvertere en pikselkoordinat til en sidekoordinat ved å dele på gjengivelsesbredden og multiplisere med sidebredden. Det fungerer bare for sider uten rotasjon, en CropBox identisk med MediaBox, og en opprinnelse på null, og mange skannede dokumenter feiler på minst én av de betingelsene
PDFium Component kartlegger hvert av de fire firkanthjørnene enkeltvis gjennom FPDF_DeviceToPage, den samme kartleggingen gjengiveren brukte for å produsere pikslene, slik at /Rotate-oppføringer og forskjøvne beskjæringsbokser håndteres ved konstruksjon. Den affine matrisen for tekstobjektet bygges deretter fra tre av de kartlagte punktene, nedre venstre, nedre høyre og øvre venstre hjørne, som er nøyaktig nok til å uttrykke posisjon, skalering, rotasjon og skjærstilling
Selve tekstobjektet opprettes med enhetsfontstørrelse, slik at dets ekte fontgrenser kan måles, og de målte objektgrensene kartlegges deretter på målfirkanten. Å dimensjonere med en gjettet punktstørrelse og håpe den matcher det skannede ordet, ville drevet av gårde med hver fonterstatning; å måle først gjør tilpasningen uavhengig av hvilken font laget bruker
Kjøre det over et dokument
Alternativposten styrer oppløsning, filtrering og hvert budsjett. Konfidensfiltrering betyr mer enn det ser ut til: søppelord med lav konfidens forurenser søkeresultater permanent, og i motsetning til en feil gjengivelse legger ingen merke til det før et søk returnerer nonsens:
var
Pdf: TPdf;
Options: TPdfOcrOptions;
Report: TPdfOcrReport;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'scanned-contract.pdf';
Pdf.LoadDocument;
Options := TPdfOcrOptions.Default;
Options.Dpi := 300; // gjenkjenningsoppløsning
Options.MinConfidence := 0.60; // slett usikre ord
Options.SkipPagesWithText := True; // la digitalt fødte sider være i fred
Options.ContinueOnError := True; // én dårlig side skal ikke stoppe jobben
Options.MaxPixelsPerPage := 40 * 1000 * 1000;
if Pdf.ApplyOcrSearchLayer(TMyOcrProvider.Create, Options, Report) then
Pdf.SaveAs('scanned-contract-searchable.pdf');
for I := 0 to High(Report.Pages) do
if Report.Pages[I].Status = popsFailed then
Writeln(Format('page %d failed: %s',
[Report.Pages[I].PageNumber, Report.Pages[I].ErrorMessage]));
Writeln(Format('%d word(s) inserted, %d rejected, %d page(s) skipped',
[Report.InsertedWordCount, Report.RejectedWordCount,
Report.SkippedPageCount]));
finally
Pdf.Free;
end;
end;
SkipPagesWithText fortjener vektlegging i blandede arkiver. En PDF som allerede bærer ekte tekst, enten digitalt født eller tidligere behandlet, får et andre tekstlag hvis du kjører OCR over den blindt, og duplikatet gjør at uthenting returnerer hvert ord to ganger. Statusen per side, popsSkippedExistingText, forteller deg nøyaktig hvilke sider som ble latt være i fred
Budsjetter, kansellering og feilbegrensning
Hver mengde en fiendtlig eller bare enorm dokument kan blåse opp, har et tak: piksler per side og totalt, ord per side og totalt, og tegn per ord. Alle sjekkes før siden skrives, ikke etterpå, og pikselanslaget beregnes fra sidedimensjoner og DPI før noe bitmap blir allokert. Å heve DPI fra 150 til 300 firedobler minnebruken per side, så taket per side er parameteren å justere først når en batch-jobb begynner å feile på store formater
Kanselleringstokenet tråkler gjennom hele banen: progressiv gjengivelse, leverandørkallet og innsettingsløkken per ord. Det betyr at en bruker som kansellerer under gjenkjenning av en fil på 400 sider, stopper innenfor én side i stedet for ved slutten av dokumentet, og det samme tokenmønsteret brukt andre steder i komponenten, beskrevet i kansellerbar progressiv gjengivelse, gjelder her uendret
Feilbegrensning er per side. Biblioteket samler objekthåndtakene det satte inn på en side og kaller FPDFPage_GenerateContent én gang, etter at alle ordene er plassert. Hvis noe feiler underveis, enten en leverandørfeil eller et fontproblem, fjernes objektene som ble satt inn på den siden i motsatt rekkefølge, og sideinnholdet genereres på nytt, slik at en mislykket side går tilbake til sin opprinnelige tilstand i stedet for å beholde et halvt tekstlag. Dokumentløkken fortsetter eller stopper deretter i henhold til ContinueOnError, og den aktive siden gjenopprettes alltid
Verifisere at bildet virkelig var urørt
Den sterkeste sjekken tilgjengelig er også den enkleste: gjengi siden før og etter påføring av laget i samme størrelse og sammenlign bitmapene. De skal være identiske byte for byte, fordi usynlig tekst ikke tegner noe, og bildestrømmen aldri ble dekodet. Enhver forskjell betyr at noe annet enn tekstlaget endret siden
Etter det, verifiser tekstsiden ved å hente ut fra den behandlede filen og bekrefte at ordposisjonene lander på skanningen. Uthentingsbanen er den samme som beskrevet i uthenting av tekst fra PDF-dokumenter, og for en rask visuell sjekk av justering lar gjengivelse av sider til bilder, som i konvertering av PDF-sider til JPEG, deg legge ordbokser over skanningen
OCR-lagdeling, gjengivelse, uthenting og redigering kjører alle mot det samme dokumentobjektet i Delphi, C++Builder og Lazarus; hele API-overflaten er beskrevet på PDFium Component for Delphi-siden