PDFium Component iz Delphija dodaje sloj teksta za pretraživanje skeniranim PDF stranicama putem ApplyOcrSearchLayer. Renderira svaku odabranu stranicu, predaje piksele OCR pružatelju usluge koji sami osiguravate, i zapisuje prepoznate riječi natrag kao nevidljive tekstualne objekte pozicionirane preko riječi u skenu. Izvorna slika stranice nikada se ne dekodira, ponovno kodira ili zamjenjuje, pa je vizualni rezultat bajt po bajt jednak stranici od koje ste krenuli
Mehanizam za prepoznavanje namjerno nije dio knjižnice. PDFium izlaže renderiranje stranica, preslikavanje koordinata, učitavanje fontova, izradu tekstualnih objekata i nevidljive načine renderiranja, ali ne sadrži nikakav OCR mehanizam, a pretvaranje da sadrži značilo bi ugradnju tuđeg proizvoda za prepoznavanje u PDF komponentu. Umjesto toga, prepoznavanje živi iza sučelja IPdfOcrProvider: knjižnica prosljeđuje piksele fiksnog rasporeda u BGRA formatu s ishodištem u vrhu, a pružatelj usluge vraća Unicode tekst, vrijednosti pouzdanosti i četverokute riječi
Što je zapravo sloj teksta za pretraživanje?
Skenirani PDF slika je dokumenta. Sadržaj stranice jedna je velika slika, i nema ničega za odabrati, pretražiti, kopirati ili indeksirati. Sloj teksta za pretraživanje dodaje prave tekstualne objekte preko te slike s načinom renderiranja postavljenim na nevidljivo, pa preglednici ništa ne crtaju, dok odabir, pretraga i izdvajanje pronalaze riječi točno ondje gdje se pojavljuju
Pozicioniranje je cijela igra. Ako nevidljivi tekst stoji nekoliko točaka pomaknut, isticanja odabira slijeću pored riječi umjesto na njih, a kopiranje odlomka proizvodi tekst u pogrešnom redoslijedu. Zato geometrija mora dolaziti iz istih transformacija koje PDFium koristi za renderiranje stranice, a ne iz proporcionalne procjene
Implementacija pružatelja usluge
Ugovor pružatelja usluge jedna je metoda. Prima zapis slike stranice koji nosi dimenzije, stride, DPI, format piksela i same bajtove piksela, plus token za otkazivanje, te vraća riječi ili poruku o pogrešci:
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 sadrži BGRA retke s ishodištem u vrhu, duljine Image.Stride bajtova.
// Predajte ih svom mehanizmu, zatim popunite jedan unos po prepoznatoj riječi
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;
Četverokuti umjesto pravokutnika, jer sken rijetko stoji točno pravokutno na stranicu. Riječ na blago zakrenutoj stranici zauzima paralelogram, a TPdfOcrQuad nosi četiri kutne točke tako da nakošene i zakrenute riječi zadrže točno područje odabira. Mehanizmi koji izvještavaju samo okvire poravnate s osima mogu koristiti FromRectangle, koja gradi degenerirani četverokut
Zašto se pozicije riječi ne mogu proporcionalno skalirati?
Primamljivo je pretvoriti koordinatu piksela u koordinatu stranice dijeljenjem sa širinom renderiranja i množenjem sa širinom stranice. To funkcionira samo za stranice bez rotacije, s CropBox-om identičnim MediaBox-u i ishodištem na nuli, a mnogo skeniranih dokumenata ne zadovoljava barem jedan od tih uvjeta
PDFium Component preslikava svaku od četiriju kutnih točaka četverokuta pojedinačno kroz FPDF_DeviceToPage, isto preslikavanje koje je renderer koristio za proizvodnju piksela, pa se unosi /Rotate i pomaknuti okviri izreza obrađuju već po konstrukciji. Afina matrica za tekstualni objekt zatim se gradi iz tri preslikane točke, donjeg lijevog, donjeg desnog i gornjeg lijevog kuta, što je točno dovoljno za izražavanje pozicije, skaliranja, rotacije i nagiba
Sam tekstualni objekt stvara se u jediničnoj veličini fonta kako bi se mogle izmjeriti njegove stvarne granice fonta, a izmjerene granice objekta zatim se preslikavaju na ciljni četverokut. Dimenzioniranje pogođenom veličinom u točkama uz nadanje da će odgovarati skeniranoj riječi odstupalo bi sa svakom zamjenom fonta; prethodno mjerenje čini prilagodbu neovisnom o tome koji font sloj koristi
Pokretanje nad dokumentom
Zapis opcija kontrolira rezoluciju, filtriranje i svaki proračun. Filtriranje po pouzdanosti važnije je nego što izgleda: neispravne riječi niske pouzdanosti trajno onečišćuju rezultate pretrage, a za razliku od pogrešnog renderiranja, nitko to ne primijeti dok pretraga ne vrati besmislicu:
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; // rezolucija prepoznavanja
Options.MinConfidence := 0.60; // odbaci nesigurne riječi
Options.SkipPagesWithText := True; // ostavi izvorno digitalne stranice netaknutima
Options.ContinueOnError := True; // jedna loša stranica ne smije zaustaviti posao
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 zaslužuje naglasak u mješovitim arhivima. PDF koji već nosi pravi tekst, bilo izvorno digitalan ili prethodno obrađen, dobiva drugi sloj teksta ako nad njim slijepo pokrenete OCR, a duplikat uzrokuje da izdvajanje vrati svaku riječ dvaput. Status po stranici popsSkippedExistingText govori vam točno koje su stranice ostavljene netaknute
Proračuni, otkazivanje i obuzdavanje kvarova
Svaka veličina koju zlonamjeran ili tek golem dokument može naduti ima gornju granicu: piksele po stranici i ukupno, riječi po stranici i ukupno, te znakove po riječi. Sve se provjeravaju prije nego što je stranica zapisana, ne poslije, a procjena piksela izračunava se iz dimenzija stranice i DPI-ja prije nego što se alocira ijedna bitmapa. Podizanje DPI-ja sa 150 na 300 učetverostručuje memoriju po stranici, pa je gornja granica po stranici parametar koji treba prvi podesiti kada paketni posao počne zakazivati na velikim formatima
Token za otkazivanje provlači se kroz cijeli put: progresivno renderiranje, poziv pružatelja usluge i petlju umetanja po riječi. To znači da se korisnik koji otkaže tijekom prepoznavanja datoteke od 400 stranica zaustavlja unutar jedne stranice, a ne na kraju dokumenta, a isti obrazac tokena koji se koristi drugdje u komponenti, opisan u progresivnom renderiranju koje se može otkazati, ovdje se primjenjuje nepromijenjen
Obuzdavanje kvarova provodi se po stranici. Knjižnica prikuplja ručke objekata koje je umetnula na stranicu i poziva FPDFPage_GenerateContent jednom, nakon što su sve riječi postavljene. Ako nešto zakaže na pola puta, bilo greška pružatelja usluge ili problem s fontom, objekti umetnuti na toj stranici uklanjaju se obrnutim redoslijedom, a sadržaj stranice se ponovno generira, tako da se stranica koja nije uspjela vraća u izvorno stanje umjesto da zadrži polovicu sloja teksta. Petlja dokumenta zatim nastavlja ili se zaustavlja prema ContinueOnError, a aktivna stranica uvijek se obnavlja
Provjera je li slika doista ostala netaknuta
Najsnažnija dostupna provjera ujedno je i najjednostavnija: renderirajte stranicu prije i nakon primjene sloja u istoj veličini i usporedite bitmape. Trebale bi biti identične bajt po bajt, jer nevidljivi tekst ništa ne crta, a tok slike nikada nije dekodiran. Svaka razlika znači da je nešto drugo, ne sloj teksta, promijenilo stranicu
Nakon toga provjerite tekstualnu stranu izdvajanjem iz obrađene datoteke i potvrdom da pozicije riječi slijeću na sken. Put izdvajanja isti je onaj opisan u izdvajanju teksta iz PDF dokumenata, a za brzu vizualnu provjeru poravnanja, renderiranje stranica u slike kao u pretvaranju PDF stranica u JPEG omogućuje vam preklapanje okvira riječi na sken
Slojevanje OCR-a, renderiranje, izdvajanje i uređivanje svi rade nad istim objektom dokumenta u Delphiju, C++Builderu i Lazarusu; puna API površina opisana je na stranici PDFium Component za Delphi