Teksto išgavimas iš PDF atrodo paprastas tol, kol nesusiduriate su dokumentu, kuriame tekstinis sluoksnis neegzistuoja, yra sugadintas arba išbarstytas po dešimtis smulkių simbolių eilučių be jokios prasmingos tvarkos. „PDFium VCL“ siūlo du prieigos taškus: Character[] masyvą tiesioginei prieigai prie kiekvieno puslapio simbolio pagal indeksą ir ReadablePageContent struktūrizuotam vaizdui gauti, kuris atkuria pastraipas ir antraštes iš PDF struktūros medžio arba heuristinės analizės. Nei vienas iš jų nėra universalus sprendimas, todėl svarbu suprasti, ką kiekvienas iš jų pateikia.
Dokumento atidarymas ir tyliosios nesėkmės spąstai
TPdf atidaro failą nustatant savybę FileName ir priskyrus reikšmę Active := True. Svarbi detalė: Active := True niekada nesukelia išimties klaidos. Jei failo nėra, jis apsaugotas slaptažodžiu arba sugadintas, PDFium apdoroja šią klaidą viduje, o Active savybė tiesiog lieka False. Tai reiškia, kad kiekvienas išgavimo ciklas turi turėti tokį apsauginį patikrinimą:
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.Active := True;
if not Pdf.Active then
begin
ShowMessage('Could not open PDF (damaged or wrong password)');
Exit;
end;
// extraction follows here
finally
Pdf.Active := False;
Pdf.Free;
end;
Slaptažodžiu apsaugotiems failams prieš nustatant Active := True reikia nurodyti Pdf.Password := '...'. Antro šanso nėra: jei Active nustatymas nepavyko, turite uždaryti komponentą ir atidaryti jį iš naujo su teisingu slaptažodžiu.
Teksto išgavimas po puslapį naudojant Character[]
Žemiausio lygio metodas apima kiekvieno puslapio simbolio patikrą. Nustatykite Pdf.PageNumber, kad įkeltumėte to puslapio tekstinį sluoksnį, o kavada atlikite ciklą per CharacterCount simbolių skaičių, naudodami savybę Character[]. Verta patikrinti dvi vėliavėles kiekvienam simboliui: CharacterGenerated[i] pažymi sintetinius simbolius, kuriuos įterpė atvaizdavimo variklis (pavyzdžiui, minkštuosius brūkšnelius eilučių sandūrose) ir kurie neturi tikros „Unicode“ reikšmės, o CharacterMapError[i] rodo, kad PDFium negalėjo susieti simbolio su kodu (taip nutinka su šriftų kodavimais, neturinčiais ToUnicode lentelės).
procedure ExtractAllText(Pdf: TPdf; Output: TStrings);
var
Page, I: Integer;
Line: string;
Ch: WideChar;
begin
for Page := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := Page;
Line := '';
for I := 0 to Pdf.CharacterCount - 1 do
begin
if Pdf.CharacterGenerated[I] or Pdf.CharacterMapError[I] then
Continue;
Ch := Pdf.Character[I];
if Ch = #13 then
Ch := #10; // normalize CR to LF
Line := Line + Ch;
end;
Output.Add(Line);
end;
end;
Rezultatas yra paprasta „Unicode“ simbolių eilutė ta tvarka, kuria juos pateikia PDFium. Ši tvarka atitinka turinio srauto eilę ir nebūtinai sutampa su skaitymo tvarka iš kairės į dešinę. Daugumai lotyniškų dokumentų, sukurtų standartiniais biuro įrankiais, šio metodo pakanka. Tačiau nuskenuotuose PDF failuose, kurie buvo atpažinti naudojant OCR programinę įrangą su neįprasta simbolių seka, arba tekste iš dešinės į kairę, tvarka gali būti klaidinga. Tokiais atvejais naudingiau naudoti ReadablePageContent.
Struktūrizuotas teksto išgavimas naudojant ReadablePageContent
Metodas ReadablePageContent veikia vienu lygiu aukščiau: jis grąžina TPdfReadableContent įrašą, kurio Fragments masyvas turi pažymėtus turinio fragmentus. Kiekvienas fragmentas turi tipą Kind, kuris identifikuoja pastraipas, antraštes, sąrašo elementus, lentelės langelius ir t. t. Kai PDF turi struktūros medį (patikrinkite Pdf.IsTagged), šaltinis yra rosStructure ir skaitymo tvarka yra autoritetinga. Nepažymėtuose failuose PDFium naudoja rosHeuristic režimą, kuris sugrupuoja simbolius pagal their bounding boxes (ribojančius rėmelius) į tikėtinus skaitymo blokus, tačiau negali garantuoti visiško tikslumo.
procedure ExtractStructured(Pdf: TPdf; Output: TStrings);
var
Page: Integer;
Content: TPdfReadableContent;
Fragment: TPdfContentFragment;
begin
for Page := 1 to Pdf.PageCount do
begin
Content := Pdf.ReadablePageContent(Page);
for Fragment in Content.Fragments do
begin
case Fragment.Kind of
cfHeading : Output.Add('# ' + Fragment.Text);
cfParagraph : Output.Add(Fragment.Text);
cfListItem : Output.Add('- ' + Fragment.Text);
else
Output.Add(Fragment.Text);
end;
end;
end;
end;
Jei Content.Source = rosHeuristic ir gautas rezultatas atrodo sumaišytas, tikriausiai dokumento tekstinis sluoksnis buvo įrašytas nepaisant skaitymo tvarkos. Tokiu atveju vienintelis patikimas sprendimas yra eksportuoti dokumentą iš naujo iš pradinės programos su teisingu žymėjimu arba pritaikyti papildomą apdorojimą, kuris surūšiuoja simbolių pradžios koordinates pagal Y ir X ašis.
Ką pateikia CharacterOrigin ir CharacterRectangle
Abi savybės grąžina simbolio poziciją puslapio erdvėje (taškais, atskaitos taškas yra apatiniame kairiajame kampe, Y ašis didėja į viršų). CharacterOrigin[i] nurodo simbolio bazinės linijos inkarą; CharacterRectangle[i] pateikia pilną ribojantį rėmelį. Tai yra pagrindiniai elementai kuriant sudėtingesnes funkcijas nei paprastas teksto nuskaitymas: stulpelių ribų nustatymui, simbolių grupavimui į eilutes lyginant Y koordinates su leistina paklaida arba teksto pasirinkimo žemėlapio kūrimui peržiūros lange. Jei norite sužinoti, koks simbolis yra po pelės paspaudimu, funkcija CharacterIndexAtPos(X, Y, ToleranceX, ToleranceY) atlieka šią paiešką tiesiogiai, todėl jums nereikia tikrinti visų stačiakampių rankiniu būdu.
DLL bibliotekos paruošimas darbui
„PDFium VCL“ visą PDF analizavimą perleidžia vietinei DLL bibliotekai – pdfium32.dll arba pdfium64.dll, priklausomai nuo jūsų taikomosios platformos. Komponentas platinamas kartu su CopyDlls.bat scenarijumi, kuris nukopijuoja reikiamą failą į „Windows“ sistemos katalogą. Kūrimo kompiuteryje užtenka vieną kartą paleisti jį administratoriaus teisėmis; diegimui pas klientus nukopijuokite DLL failą šalia vykdomojo programos failo. Variantai su V8 variklio palaikymu (pdfium32v8.dll, pdfium64v8.dll) yra žymiai didesni ir reikalingi tik tuo atveju, jei jūsų PDF dokumentuose yra JavaScript kodas, kurį būtina įvykdyti. Paprastam teksto išgavimui pilnai pakanka standartinės bibliotekos versijos.
Jei vykdymo metu DLL bibliotekos nebuvo, savybės Active := True nustatymas nepavyks tyliai, kaip ir trūkstamo failo atveju, nes komponentas klaidą apdoroja viduje. Prieš pateikdami programą klientams, visada patikrinkite ją švarioje operacinėje sistemoje.
FontSize[] ir Character[] naudojimas maketo analizei
Be paprasto teksto, simbolių lygio API pateikia savybę FontSize[i], kuri grąžina kiekvieno simbolio atvaizduojamą dydį taškais. Kartu su CharacterOrigin[i] ir CharacterRectangle[i] tai leidžia atskirti pagrindinį tekstą nuo antraščių nenaudojant struktūros medžio. Simbolių seka, kurioje šrifto dydis viršija nustatytą ribą, nepažymėtame dokumente beveik neabejotinai yra antraštė. Tačia technika tinka ir užrašams po vaizdais (mažas tekstas po vaizdo rėmeliu) arba išnašoms puslapio apačioje aptikti. Tam nereikia atlikti atvaizdavimo – visos trys savybės nuskaitomos tiesiai iš tekstinio sluoksnio, kurį sukuria PDFium priskyrus Active := True.
Vienas niuansas: FontSize[i] grąžina dydį pritaikius puslapio CTM (dabartinę transformacijos matricą), todėl dokumente, kur autorius pakeitė viso puslapio mastelį, dydžiai bus proporcingai pakoreguoti. Jei lyginate dydžius skirtingų matmenų puslapiuose, prieš priimdami sprendimus dėl ribinių reikšmių, normalizuokite juos pagal kiekvieno puslapio MediaBox aukštį.
Rezultato įrašymas į failą
„Delphi“ klasė TStringList tvarkingai apdoroja UTF-8 išvestį pradedant nuo XE versijos. Nustatykite savybę WriteBOM := False, jei reikia failo be BOM žymos (dauguma vėlesnių sistemų tikisi būtent tokio formato):
var
Lines: TStringList;
begin
Lines := TStringList.Create;
try
ExtractAllText(Pdf, Lines);
Lines.WriteBOM := False;
Lines.SaveToFile('output.txt', TEncoding.UTF8);
finally
Lines.Free;
end;
end;
Apdorojant labai didelius dokumentus, kai svarbu taupyti atmintį, geriau rašyti tiesiai į TStreamWriter su TEncoding.UTF8 puslapių cikle, užuot iš pradžių kaupus visą tekstą sąraše.
Šiame straipsnyje aprašyti Character[], CharacterCount, CharacterOrigin[], CharacterRectangle[], ReadablePageContent ir CharacterIndexAtPos metodai yra dalis bibliotekos PDFium VCL Component, skirtos Delphi ir C++Builder programuotojams.