PDF lagrer bilder som førsteklasses objekter inni innholdsstrømmene sine. Når en side refererer til et fotografi, en skanning eller et diagram, lever pikseldataene i en XObject-ordbok ved siden av sidegeometrien. PDFium-komponenten synliggjør det gjennom to egenskaper på TPdf: BitmapCount, som returnerer hvor mange innebygde bitkart som er på gjeldende side, og Bitmap[Index], som dekoder ett av dem til en TBitmap du eier og må frigjøre (free). Det er hele utvinningsmodellen. Løkken er fire linjer; det som krever skjønn er det omliggende rørarbeidet
Åpne dokumentet
Det første å vite om TPdf er at Active := True aldri hever unntak. Lastefeil, feil passord, korrupte filer: alle svelges internt og komponenten forblir rett og slett inaktiv. Du må sjekke flagget selv etter tilordningen, ellers vil du fortsette inn i sideløkken med PageCount som returnerer null og lure på hvorfor ingenting ble trukket ut
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');
// proceed to extraction
finally
Pdf.Free;
end;
end;
Passordbeskyttede filer følger det samme mønsteret: tilordne Pdf.Password før du setter Active := True. Hvis passordet er feil, forblir Active False og du får ingen unntak å fange. I et batchverktøy som behandler hundrevis av filer, er den stille oppførselen faktisk nyttig: du akkumulerer feilene i en liste i stedet for å vikle ut kallsstakken (call stack) for hver enkelt
Iterere sider og hente bitkart
BitmapCount er per side, så du setter Pdf.PageNumber før du leser den. Sidenumre er 1-baserte; standard er 0, noe som betyr at ingen side er lastet inn. Egenskapen Bitmap[Index] er 0-basert og returnerer en oppringereid (caller-owned) TBitmap. Du må frigjøre den. Forsømmer du Free inni en lang løkke over et stort dokument, klatrer minnet raskt, fordi hvert bitkart kan være flere megabyte med rå pikseldata før noen komprimering
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;
Assigned-vakten betyr noe. Et lite antall PDF-generatorer skriver bilde-XObjects med null pikseldimensjoner eller andre misdannede data; i de tilfellene returnerer komponenten nil i stedet for et tomt bitkart. Å behandle en nil-retur som en feil og stoppe utvinningen er feil refleks: hopp over den, loggfør siden og indeksen hvis du trenger revisjonssporet, og fortsett. Resten av siden kan fortsatt gi gyldige bilder
Legg merke til at den ytre løkken setter Pdf.PageNumber på hver iterasjon. Den tilordningen er det som laster siden inn i komponentens interne tilstand og gjør BitmapCount meningsfull. Hopper du over den, leser du den samme sidens antall gjentatte ganger. Mønsteret føles overflødig når du skriver det, men det er slik API-et er designet: siden er en markør, ikke en samling
Velge et utdataformat
BMP er tapsfritt og alltid tilgjengelig uten ekstra units, noe som gjør det til en sunn standard når du ennå ikke vet hva bildet inneholder. Når filstørrelsen betyr noe, forteller pikselformatet til det returnerte TBitmap deg hvilken kodek som passer. Et 32-biters bitkart bærer en alfakanal; PNG bevarer den uten tap. Et stort 24-biters bilde med kontinuerlig tone er en kandidat for JPEG. Mindre bilder eller bilder tegnet med en begrenset palett er generelt bedre å la være som BMP enn å kjøre gjennom JPEG, som legger til blokkartefakter ved lave kvalitetsinnstillinger og sparer lite ved høye
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: lossless, no extra units
end;
end;
I praksis drives formatvalg av Bmp.PixelFormat og dimensjoner. Hvis PixelFormat = pf32bit trenger du et format som bærer alfa; PNG er det åpenbare valget, selv om det krever PNGImage-uniten i eldre Delphi-versjoner. For 24-biters bilder bredere enn omtrent 300 piksler, gir JPEG på kvalitet 85 en tre-til-én-størrelsesreduksjon over BMP uten noe merkbart tap i de fleste fotografiske innhold. Under den terskelen er BMP sammenlignbar i størrelse og unngår enhver kvalitetsbeslutning helt
Hva BitmapCount teller og ikke teller
PDF skiller mellom bilde-XObjects og vektorgrafikk tegnet med baneoperatører. En side som ser visuelt kompleks ut, kan returnere en BitmapCount på null hvis hvert element er vektor. Skannede sider returnerer nesten alltid nøyaktig én: skanneren skriver hele skanningen som et enkelt helsides bilde-XObject ved den oppløsningen skanneren var satt til. Sider som blander satt tekst med innebygde fotografier returnerer én oppføring per fotografi. Dekorative skillelinjer, skyggelagte bakgrunner og tabellkanter vises vanligvis ikke i bitkartantallet i det hele tatt
Antallet inkluderer heller ikke innebygde (inline) bilder, en sjelden brukt PDF-konstruksjon der bildedata er bygget direkte inn i sidens innholdsstrøm i stedet for som et navngitt XObject. De faller utenfor det dette API-et synliggjør; de er uvanlige nok i virkelige dokumenter til at de fleste utvinningsverktøy rett og slett ikke håndterer dem
Én detalj verdt å huske på: BitmapCount du leser er for gjeldende side fra forrige PageNumber-tilordning. Hvis koden din forgrener seg eller kaller en funksjon som endrer PageNumber mellom telling og henting, kan du lese færre bilder enn du tildelte plass til, eller indeksere forbi slutten. Hold antallesingen og Bitmap[]-løkken på samme side uten å røre PageNumber i mellomtiden
Bruke TPdfView i en skjemapplikasjon (form)
TPdfView-komponenten eksponerer de samme BitmapCount- og Bitmap[]-egenskapene, men siden den leser fra er visningens (view) for øyeblikket viste side, ikke TPdf.PageNumber. De to sidepekerne er uavhengige; å sette den ene flytter ikke den andre. I en VCL-skjemapplikasjon med en direkte (live) visning, kan du kalle Pdf.PageNumber := N for å drive utvinning gjennom TPdf mens visningen blir værende på det brukeren sist scrollet til. Den atskillelsen er tilsiktet og holder visningens skjermtilstand ren mens en bakgrunnsutvinning kjører
Minne og ytelse i batch-jobber
Over et stort arkiv er minnebudsjettet hovedtingen å overvåke. Hvert Bitmap[]-kall tildeler en ny TBitmap på haugen (heap), og på en 300 DPI skannet side er det fort 25 MB med rå pikseldata før noen koding. Hvis du behandler sider i en stram løkke uten å frigjøre mellom iterasjoner, vokser arbeidssettet lineært med antall bilder. Den riktige formen er alltid: hent ett bitkart, gjør det du trenger, frigjør det, hent det neste. Hvis du trenger å holde referanser til flere bitkart samtidig for et sammenligningstrinn, tell dem først med BitmapCount og tildel beholderen din deretter, frigjør så hver enkelt så snart du er ferdig med den i stedet for å utsette til slutten av dokumentet-opprydding. På et dokument med 500 skannede sider kan den forskjellen bety forskjellen mellom 25 MB og 12 GB maksimal (peak) RSS
BitmapCount- og Bitmap[]-egenskapene vist her er en del av PDFium-komponenten for Delphi og C++Builder