PDF stochează imaginile ca obiecte de prim rang în interiorul fluxurilor lui de conținut. Când o pagină face referire la o fotografie, la o scanare sau la o diagramă, datele de pixeli trăiesc într-un dicționar XObject, alături de geometria paginii. PDFium Component scoate asta la suprafață prin două proprietăți de pe TPdf: BitmapCount, care returnează câte imagini bitmap încorporate se află pe pagina curentă, și Bitmap[Index], care decodează una dintre ele într-un TBitmap pe care îl dețineți și trebuie să îl eliberați. Acesta este tot modelul de extragere. Bucla are patru rânduri; ce cere judecată este instalația din jur
Deschiderea documentului
Primul lucru de știut despre TPdf este că Active := True nu ridică niciodată excepție. Eșecuri de încărcare, parole greșite, fișiere corupte: toate sunt înghițite intern, iar componenta rămâne pur și simplu inactivă. Trebuie să verificați singur indicatorul după atribuire, altfel veți intra în bucla de pagini cu PageCount returnând zero și vă veți întreba de ce nu s-a extras nimic
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.Active := True;
if not Pdf.Active then
begin
Writeln('Failed to open: ', Pdf.FileName);
Exit;
end;
Writeln(Pdf.PageCount, ' pages');
// treci la extragere
finally
Pdf.Free;
end;
end;
Fișierele protejate cu parolă urmează același tipar: atribuiți Pdf.Password înainte de a seta Active := True. Dacă parola este greșită, Active rămâne False și nu primiți nicio excepție de prins. Într-un instrument de lot care procesează sute de fișiere, acel comportament tăcut este de fapt util: adunați eșecurile într-o listă în loc să desfășurați stiva de apeluri pentru fiecare
Parcurgerea paginilor și extragerea imaginilor
BitmapCount este per pagină, așa că setați Pdf.PageNumber înainte să îl citiți. Numerele de pagină pornesc de la 1; valoarea implicită este 0, adică nicio pagină încărcată. Proprietatea Bitmap[Index] pornește de la 0 și returnează un TBitmap deținut de apelant. Trebuie să îl eliberați. Neglijați eliberarea într-o buclă lungă peste un document mare și memoria urcă rapid, pentru că fiecare imagine poate însemna câțiva megaocteți de date brute de pixeli înainte de orice compresie
procedure ExtractAllImages(Pdf: TPdf; const OutputDir: string);
var
Page, Idx: Integer;
Bmp: TBitmap;
OutPath: string;
begin
for Page := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := Page;
for Idx := 0 to Pdf.BitmapCount - 1 do
begin
Bmp := Pdf.Bitmap[Idx];
if not Assigned(Bmp) then
Continue;
try
OutPath := Format('%s\p%d_img%d.bmp', [OutputDir, Page, Idx + 1]);
Bmp.SaveToFile(OutPath);
finally
Bmp.Free;
end;
end;
end;
end;
Paza cu Assigned contează. Un număr mic de generatoare PDF scriu imagini XObject cu dimensiuni de pixeli zero sau cu date altfel malformate; în acele cazuri componenta returnează nil, nu o imagine goală. Tratarea unui nil ca eroare și oprirea extragerii este reflexul greșit: săriți peste el, notați pagina și indexul dacă vă trebuie traseul de audit, și continuați. Restul paginii poate produce în continuare imagini valide
Observați că bucla exterioară setează Pdf.PageNumber la fiecare iterație. Acea atribuire este cea care încarcă pagina în starea internă a componentei și dă sens lui BitmapCount. Dacă o săriți, citiți la nesfârșit numărul aceleiași pagini. Tiparul pare redundant când îl scrieți, dar așa este proiectat API-ul: pagina este un cursor, nu o colecție
Alegerea unui format de ieșire
BMP este fără pierderi și mereu disponibil, fără unități suplimentare, ceea ce îl face o valoare implicită solidă când încă nu știți ce conține imaginea. Când contează dimensiunea fișierului, formatul de pixel al TBitmap-ului returnat vă spune ce codec este potrivit. O imagine pe 32 de biți poartă un canal alfa; PNG îl păstrează fără pierderi. O imagine mare pe 24 de biți, cu ton continuu, este candidat pentru JPEG. Imaginile mai mici sau cele desenate cu o paletă limitată este în general mai bine să rămână BMP decât să treacă prin JPEG, care adaugă artefacte de blocuri la setări de calitate mici și economisește puțin la cele mari
procedure SaveBitmap(Bmp: TBitmap; const FileName: string);
var
Jpg: TJPEGImage;
begin
case UpperCase(ExtractFileExt(FileName)) of
'.JPG', '.JPEG':
begin
Jpg := TJPEGImage.Create;
try
Jpg.Assign(Bmp);
Jpg.CompressionQuality := 85;
Jpg.SaveToFile(FileName);
finally
Jpg.Free;
end;
end;
else
Bmp.SaveToFile(FileName); // BMP: fără pierderi, fără unități în plus
end;
end;
În practică, alegerea formatului este condusă de Bmp.PixelFormat și de dimensiuni. Dacă PixelFormat = pf32bit, aveți nevoie de un format care poartă alfa; PNG este alegerea evidentă, deși în versiunile mai vechi de Delphi cere unitatea PNGImage. Pentru imagini pe 24 de biți mai late de aproximativ 300 de pixeli, JPEG la calitatea 85 dă o reducere de dimensiune de trei la unu față de BMP, fără pierdere perceptibilă pentru majoritatea conținutului fotografic. Sub acel prag, BMP este comparabil ca dimensiune și evită complet orice decizie de calitate
Ce numără și ce nu numără BitmapCount
PDF distinge între imaginile XObject și grafica vectorială desenată cu operatori de traseu. O pagină care arată complex vizual poate returna un BitmapCount egal cu zero dacă fiecare element este vectorial. Paginile scanate returnează aproape întotdeauna exact una: scanerul scrie întreaga scanare ca un singur XObject de imagine pe toată pagina, la rezoluția la care era setat scanerul. Paginile care amestecă text tehnoredactat cu fotografii încorporate returnează câte o intrare per fotografie. Liniile decorative, fundalurile umbrite și bordurile de tabel de obicei nu apar deloc în numărul de imagini
Numărătoarea nu include nici imaginile inline, o construcție PDF rar folosită, în care datele imaginii sunt încorporate direct în fluxul de conținut al paginii, nu ca XObject cu nume. Acelea cad în afara a ce expune acest API; sunt destul de rare în documentele reale cât majoritatea instrumentelor de extragere să nu le trateze deloc
Un detaliu care merită ținut minte: BitmapCount-ul pe care îl citiți este pentru pagina curentă, așa cum era la ultima atribuire a lui PageNumber. Dacă între numărare și aducere codul dumneavoastră se ramifică sau apelează vreo funcție care schimbă PageNumber, puteți citi mai puține imagini decât ați alocat spațiu sau puteți indexa dincolo de capăt. Țineți citirea numărului și bucla Bitmap[] pe aceeași pagină, fără să atingeți PageNumber între ele
Folosirea lui TPdfView într-o aplicație cu formulare
Componenta TPdfView expune aceleași proprietăți BitmapCount și Bitmap[], dar pagina din care citește este pagina afișată în acel moment de vizualizare, nu TPdf.PageNumber. Cei doi indicatori de pagină sunt independenți; setarea unuia nu îl mută pe celălalt. Într-o aplicație VCL cu formular și cu un vizualizator viu, puteți apela Pdf.PageNumber := N pentru a conduce extragerea prin TPdf, în timp ce vizualizatorul rămâne pe ce a derulat ultima dată utilizatorul. Acea separare este intenționată și păstrează curată starea de afișare a vizualizatorului în timp ce rulează o extragere în fundal
Memorie și performanță în lucrările pe loturi
Pe o arhivă mare, bugetul de memorie este principalul lucru de urmărit. Fiecare apel Bitmap[] alocă un TBitmap nou pe heap, iar pe o pagină scanată la 300 DPI asta înseamnă lejer 25 MB de date brute de pixeli, înainte de orice codare. Dacă procesați paginile într-o buclă strânsă fără să eliberați între iterații, setul de lucru crește liniar cu numărul de imagini. Forma corectă este întotdeauna: aduceți o imagine, faceți ce aveți de făcut, eliberați-o, aduceți-o pe următoarea. Dacă trebuie să țineți referințe la mai multe imagini deodată pentru un pas de comparație, numărați-le întâi cu BitmapCount și alocați containerul în consecință, apoi eliberați fiecare imagine imediat ce ați terminat cu ea, în loc să amânați curățarea până la finalul documentului. Pe un document cu 500 de pagini scanate, acea distincție poate însemna diferența dintre 25 MB și 12 GB de memorie rezidentă de vârf
Proprietățile BitmapCount și Bitmap[] arătate aici fac parte din PDFium Component pentru Delphi și C++Builder