PDF shranjuje slike kot objekte prvega razreda znotraj svojih vsebinskih tokov. Ko se stran sklicuje na fotografijo, skenirano sliko ali diagram, slikovni podatki živijo v slovarju XObject poleg geometrije strani. PDFium Component to izpostavlja prek dveh lastnosti v TPdf: BitmapCount, ki vrne število vgrajenih bitnih slik na trenutni strani, in Bitmap[Index], ki eno izmed njih dekodira v objekt TBitmap, ki je v vaši lasti in ga morate sami sprostiti. To je celoten model ekstrakcije. Zanka obsega le štiri vrstice, več premisleka pa zahteva okoljska koda
Odpiranje dokumenta
Prva stvar, ki jo morate vedeti o TPdf, je, da nastavitev Active := True nikoli ne sproži izjeme. Napake pri nalaganju, napačna gesla, poškodovane datoteke: vse to se ujame interno, komponenta pa preprosto ostane neaktivna. Po dodelitvi morate sami preveriti to zastavico, sicer boste nadaljevali v zanko strani, kjer bo PageCount vrnil nič, vi pa se boste spraševali, zakaj se nič ni izvleklo
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;
Z geslom zaščitene datoteke sledijo enakemu vzorcu: določite Pdf.Password pred nastavitvijo Active := True. Če je geslo napačno, ostane lastnost Active na False in ne prejmete nobene izjeme, ki bi jo lahko ulovili. Pri paketnem orodju, ki obdeluje stotine datotek, je to tiho delovanje dejansko uporabno: napake preprosto zberete na seznamu, namesto da bi za vsako datoteko posebej prekinjali izvajanje
Pomikanje po straneh in pridobivanje bitnih slik
Vrednost BitmapCount velja za posamezno stran, zato morate pred njenim branjem nastaviti Pdf.PageNumber. Številke strani se začnejo z 1; privzeta vrednost je 0, kar pomeni, da ni naložena nobena stran. Lastnost Bitmap[Index] ima indekse z začetkom pri 0 in vrne objekt TBitmap v vaši lasti. To bitno sliko morate sami sprostiti. Če sprostitev opustite znotraj dolge zanke čez obsežen dokument, bo poraba pomnilnika hitro narasla, saj lahko vsaka bitna slika pred stiskanjem predstavlja več megabajtov surovih slikovnih podatkov
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;
Varovanje z Assigned je pomembno. Majhno število generatorjev PDF zapiše slikovne XObjects z ničelnimi dimenzijami slikovnih pik ali drugače poškodovanimi podatki; v teh primerih komponenta vrne nil namesto prazne bitne slike. Obravnava vrnitve nil kot napake in ustavitev ekstrakcije je napačen refleks: preskočite jo, po potrebi zabeležite stran in indeks ter nadaljujte. Preostanek strani morda še vedno vrne veljavne slike
Upoštevajte, da zunanja zanka nastavi Pdf.PageNumber pri vsaki ponovitvi. Ta dodelitev je tista, ki naloži stran v notranje stanje komponente in naredi BitmapCount smiseln. Preskočite jo in pri vsaki ponovitvi berete število iste strani. Vzorec se pri pisanju zdi odveč, toda tako je zasnovan API: stran je kazalec, ne zbirka
Izbira izhodne oblike
BMP je brez izgub in vedno na voljo brez dodatnih enot, kar ga naredi za dobro privzeto vrednost, ko še ne veste, kaj slika vsebuje. Ko je velikost datoteke pomembna, format slikovnih pik vrnjene TBitmap pove, kateri kodek je primeren. 32-bitna bitna slika nosi alfa kanal; PNG ga ohranja brez izgub. Velika 24-bitna slika z zveznimi toni je kandidat za JPEG. Manjše slike ali tiste, narisane z omejeno paleto, so na splošno bolje puščene kot BMP kot pa obdelane z JPEG, ki pri nizkih nastavitvah kakovosti doda blokovne artefakte in pri visokih malo prihrani
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;
V praksi je izbira oblike odvisna od Bmp.PixelFormat in dimenzij. Če je PixelFormat = pf32bit, potrebujete obliko, ki nosi alfa; PNG je očitna izbira, čeprav zahteva enoto PNGImage v starejših različicah Delphija. Za 24-bitne slike, širše od približno 300 slikovnih pik, JPEG pri kakovosti 85 zagotovi trikratno zmanjšanje velikosti v primerjavi z BMP brez zaznavnih izgub pri večini fotografske vsebine. Pod to mejo je BMP primerljive velikosti in se izogne kakršni koli odločitvi o kakovosti
Kaj BitmapCount šteje in česa ne
PDF razlikuje med slikovnimi XObjects in vektorsko grafiko, narisano z operaterji poti. Stran, ki je videti vizualno kompleksna, morda vrne BitmapCount nič, če je vsak element vektorski. Skenirane strani skoraj vedno vrnejo natanko eno: optični bralnik zapiše celoten posnet kot eno samo sliko XObject v celotni velikosti strani pri kakršni koli ločljivosti je bil nastavljen. Strani, ki mešajo stavljeno besedilo z vgrajenimi fotografijami, vrnejo en vnos na fotografijo. Okrasne ločilne črte, senčena ozadja in obrobe tabel se v štetju bitnih slik praviloma sploh ne pojavijo
Število prav tako ne vključuje vgrajenih slik, redko uporabljenega konstrukta PDF, pri katerem so slikovni podatki vgrajeni neposredno v vsebinski tok strani in ne kot poimenovan XObject. Ti so zunaj tega, kar ta API izpostavlja; v resničnih dokumentih so dovolj redki, da jih večina orodij za ekstrakcijo preprosto ne obravnava
Ena podrobnost, ki jo je vredno imeti v mislih: vrednost BitmapCount, ki jo berete, velja za trenutno stran glede na zadnjo dodelitev PageNumber. Če vaša koda razveja ali kliče katero koli funkcijo, ki med štetjem in pridobivanjem spremeni PageNumber, morda preberete manj slik, kot ste rezervirali prostora, ali pa indeksirate mimo konca. Ohranite branje števila in zanko Bitmap[] na isti strani, ne da bi vmesno posegali v PageNumber
Uporaba TPdfView v aplikaciji obrazca
Komponenta TPdfView izpostavlja iste lastnosti BitmapCount in Bitmap[], toda stran, iz katere bere, je stran, ki je trenutno prikazana v pogledu, ne TPdf.PageNumber. Oba kazalca strani sta neodvisna; nastavitev enega ne premakne drugega. V aplikaciji VCL z živim pregledovalnikom lahko pokličete Pdf.PageNumber := N, da poganjate ekstrakcijo prek TPdf, medtem ko pregledovalnik ostane na tisti strani, ki jo je uporabnik nazadnje prelistal. Ta ločitev je namerna in ohranja stanje prikaza pregledovalnika čisto, medtem ko teče ekstrakcija v ozadju
Pomnilnik in zmogljivost v paketnih opravilih
Pri veliki arhivi je proračun pomnilnika glavna stvar, na katero je treba paziti. Vsak klic Bitmap[] dodeli nov TBitmap na kup, in na skeniranem listu pri 300 DPI je to zlahka 25 MB surovih slikovnih podatkov pred kakršnim koli kodiranjem. Če obdelujete strani v tesni zanki brez sproščanja med ponovitvami, delovna množica linearno narašča s številom slik. Pravilna oblika je vedno: pridobite eno bitno sliko, naredite kar potrebujete, jo sprostite, pridobite naslednjo. Če morate hkrati držati reference na več bitnih slik za korak primerjave, jih najprej preštejte z BitmapCount in ustrezno dodelite vsebnik, nato pa vsako sprostite takoj, ko ste z njo končali, namesto da odložite čiščenje na konec dokumenta. Pri dokumentu s 500 skeniranimi stranmi ta razlika pomeni razliko med 25 MB in 12 GB konice RSS
Lastnosti BitmapCount in Bitmap[], prikazane tukaj, so del komponente PDFium Component za Delphi in C++Builder