Tehnički članak

Izdvajanje slika iz PDF-ova pomoću PDFium Component-a u Delphiju

PDF pohranjuje slike kao prvoklasne (first-class) objekte unutar svojih tokova (streams) sadržaja. Kada stranica referencira (references) fotografiju, skeniranje ili dijagram, podaci o pikselima žive u rječniku (dictionary) XObject uz geometriju stranice (page geometry). PDFium Component iznosi to na površinu (surfaces that) kroz dva svojstva na TPdf: BitmapCount, koji vraća koliko ugrađenih bitmapa se nalazi na trenutnoj stranici, te Bitmap[Index], koji dekodira (decodes) jednu od njih u TBitmap koji vi posjedujete i morate ga osloboditi. To je cijeli model ekstrakcije. Petlja (loop) traje četiri linije; ono što zahtijeva prosudbu (judgment) jest okolni vodovod (surrounding plumbing)

Otvaranje dokumenta

Prva stvar koju treba znati o TPdf je ta da se Active := True nikada ne podiže (never raises). Neuspješna (failures) učitavanja, pogrešne lozinke, oštećene datoteke: sve se to guta interno i komponenta (component) jednostavno ostaje neaktivna (inactive). Vi morate provjeriti tu zastavicu (flag) sami nakon dodjele (assignment), ili ćete preći u petlju (loop) na stranici gdje će PageCount vratiti (returning) nulu a vi ćete se pitati zašto se ništa nije izdvojilo (extracted)

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;

Datoteke (files) zaštićene lozinkom (password-protected) slijede isti obrazac (pattern): dodijelite (assign) Pdf.Password prije postavljanja Active := True. Ako je lozinka pogrešna (wrong), Active ostaje False i nećete dobiti iznimku (exception) koju treba uhvatiti. U skupnom alatu (batch tool) koji obrađuje stotine (hundreds) datoteka, takvo je tiho (silent) ponašanje zapravo korisno: vi prikupljate propuste (failures) u popis (list), umjesto da odmotavate hrpu (unwinding the call stack) poziva (calls) za svaku (each one)

Ponavljanje stranica i povlačenje bitmapa

BitmapCount funkcionira na principu po stranici (per-page), tako da morate podesiti Pdf.PageNumber prije samog iščitavanja. Brojevi (numbers) na stranicama počivaju (based) na 1; zadana je vrijednost 0, što podrazumijeva da niti jedna od stranica nije učitana (loaded). Karakteristika za Bitmap[Index] zasniva (based) se na 0 te daje rezultat u obliku caller-owned tipa koji pripada formatu nazvanom - TBitmap. Međutim, to morate sami osloboditi (free). Ukoliko na to zaboravite u velikom dokumentu sa mnogo petlji - vaša memorija (memory) će se veoma brzo potrošiti i to zato što jedna takva bitmap slika u svom izvornom formatu može iznositi po nekoliko megabajta prije nego što uopće krene na sažimanje

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;

Svojstvo Assigned ima bitnu ulogu u cijeloj priči. Mali (small) broj PDF generatora piše slikovne XObjects s nul (zero) dimenzijama u obliku piksela ili pak neke druge neispravne oblike (malformed) po pitanju podataka; a kod ovakvih primjera ovakva sastavnica (component) povratno pruža podatke unutar oblike tipa - nil - prije nego bilo kakve vrste praznih bitmapa (empty bitmap). Zato obrada povratnih (return) podataka u ovakvim oblicima i automatsko pauziranje prikaza slike zasigurno pripada jednom posve krivom nagonu: preskočite to, zabilježite gdje staje (log the page and index), a onda ako i jeste potrebna nekakva istraga krenite po istom tragu i jednostavno nastavite po svome (continue). Ostatak na tom prikazu može još uvijek generirati važeće i potpuno funkcionalne prikaze na slici

Primijetite (Notice) da se na svaku iteraciju kod vanjske (outer) petlje (loop) nadodaje oznaka - Pdf.PageNumber. Takav je tip zadaće upravo ono što će upisati na samom sučelju u samim internim funkcijama te učiniti čitav proces korisnim u rubrici BitmapCount. Preskočite li (Skip) taj postupak onda se ponovo čita samo jedan te isti dio stranice u krug (repeatedly). Obrazac zna (feels) biti poprilično kompliciran kod samog ispisivanja međutim sveukupni se proces funkcioniranja samoga (how the API is designed) sustava bazira točno na tome: poanta stranice jest kursor a ne kolekcija (collection)

Odabir izlaznog formata

BMP je bez gubitaka (lossless) i uvijek je dostupan bez dodatnih jedinica, što ga čini zvučno postavljenom vrijednošću kad (when) još ne znate što se na slici uopće nalazi. Ukoliko vam je veličina datoteke vrlo bitna (When file size matters), piksel-format (pixel format) iz vraćenog obrasca iz vrste - TBitmap govorit će (tells) vam samo za sebe koje su vama tu sve komponente prijeko potrebne. Neka ovakva standardna bitmap slika čija gustoća na ekranu (bitmap carries) u potpunosti iznosi oko 32 bita u svakom trenutku posjeduje svoj specifični tip pod imenom i alfa kanalom; a on unutar PNG oblike zadržava te vrijednosti bez kakvih oštećenja. Velika takva datoteka (large 24-bit image) izražena sa 24 bitova s ​​određenom kontinuiranom notom u svojoj biti je idealan primjer upravo za - JPEG. Manje ovakve slike ili one iscrtane s ​​dodatnim paletama znatno se kvalitetnije iskazuju po ostavljanju u vidu same BMP vrste formata umjesto u nekakvom - obliku kod izvođenja, jer ukoliko je ovakva struktura na samoj kvaliteti nešto narušena sa svojim malim zahtjevima onda ne daje dovoljno dobre razultate kod velikih vrijednosti niti bi vam iste bilo isplativo pohranjivati na tim već spomenutim velikim (high ones)

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;

U praksi, odabir (selection) formata ovisi (driven) isključivo o mogućnostima veličine - i naravno same slike: (Bmp.PixelFormat) - što samo po sebi već znači - i dimenzija. Ukoliko je riječ o podudaranju po principu - PixelFormat = pf32bit vama apsolutno tu onda za nastavak rada treba takav jedan format da unutar te priče podržava (carries) takvu spomenutu alfa opciju; unutar koje onda logično PNG oblike same padaju na prvi mah (obvious choice), - no, opet morate imati za nju na starijim uređajima neophodan modul za ovakav program - koji nosi jedno svoje posebno obilježje pod imenom: PNGImage. A iznad same debljine od tih prosječnih 300 pixela kod fotografija od te visoke vrijednosti (24-bit images) JPEG sam nudi po svojim specifičnostima kvalitetu (quality) rezanja slike gdje ona oblike u omjeru tri - prema jedan znatno skraćuje cijeli sistem procesa veličine formata same - BMP vrste - na onaj najbolji način i to iz razloga bez takozvanog gubitaka ikakvog (perceptible loss) pa samim time u potpunosti čuva samu vrijednost unutar cijele slike na koju naiđe. Ne pređe li (Below) takvu postavljenu - liniju debljine same - BMP onda nudi identične rezultate pa vi ovdje bez ikakvih zadrški odmah onda možete izbjeći tu temu te i samu neupitnu kvalitetu (quality) opciju a to onda rezultira (entirely)

Što BitmapCount broji (counts) a što (does not) izostavlja kod brojanja

PDF zapravo radi jasnu (distinguishes) razliku između formata sa slikom (image) XObjects i vektorske grafike nacrtane uz pomoć - operatora. Jedna od onih stranica s prilično kompliciranim izgledom naočigled može (visually complex) unutar BitmapCount vratiti vrijednost nule, no sve to vrijedi jedino ukoliko za svaki tu raspoloživi dodatak ispadne ta činjenica (element) s predznakom - tipa vektor. A kod onih stranica koje pretrpe - skeniranje (Scanned pages) na uređaju skoro se uvijek povratno nudi iznos jedan: tako ispisane radnje onda zapisuju (writes) preko tog stroja upravo cijelo jedno polje do kojega može unutar svojih tehničkih karakteristika doseći preko samog XObject, na cijeli (entire scan) raspoloživi zaslon (full-page image), s onom rezolucijom (resolution) s kojom uopće ta aparatura (scanner) i može za to jedno vrijeme baratati obzirom na tehničku spremnost (set to). Na istim onim formatima na koje idu izmiksana (mix) razna pisana slova uz ubačene neke različite fotogafije u tekst on za uzvrat uvijek daje jednu ponudu ukoliko ste naišli (photograph) na opciju istaknutih fotografija u tekstu pa onda idu s njom u tom sistemu redovito jedan za jedan (one entry) na obližnju tu uvučenu fotogafiju samu sa tom linijom redoslijeda u brojanju - ali tu naravno nema - one dekoracijske (Decorative) ispise teksta i kojekakve zatamnjene pozadine pa onda granice kod tablica - koje se s druge pak stane većinom obično (usually do not appear) neće apsolutno tamo ni nalaziti

Takvi popisi redovito ne dodaju sa ovim nikakve slike koje su unutar retka iz same datoteke tj. one (inline images) kod kojih je tu jedan samo od rijetko primjenjenih procesa strukture a unutar koje - se zapravo svi ti ugrađeni (embedded) dijelovi na onim istim (construct) slikama jednostavno postave točno direktno po tom samom ispisanom sučelju od te izbačene slike pa stoga se baš zato oni redovito prilično razlikuju (rather) od uobičajene ponude kod tog tzv - XObject sistema pretrage i očitavanja. Ove nabrojane situacije padaju izvan (outside) oblike koje je pokriveno od same - ove API programske potpore a s druge stane takvi (surfaces;) dokumenti postaju itekako toliko neuobičajeni u pravim formatima izrade - zbog toga - kod - praksi, pa (uncommon enough) onda ne bi trebalo čuditi što kod onih drugih većina i ostalih općenito dostupnih opcija ekstrakcijskih - izvođenja to nikada ne mogu tako uspješno učitati te oni zapravo po pitanju toga vrlo često kod njih (handle them) zakazuju u tome pogledu i izradi za samu - opciju korištenja

Ovdje bi jedan ovakav doista sitan detalj svakako bilo uputno na jedan od načina ovdje uzeti - (worth keeping in mind) u razmatranje: podatak za očitavanje (read) - iz ponude preko ove značajke za BitmapCount tu je sa opcijom isključivo za stranicu u kojoj je već izvršena i zadnja provjera dodijeljenosti (assignment) funkcije: pod onom PageNumber oznakom na tom ekranu u tom iznosu. Ukoliko (If) tu i započne nečim takvim jedna dodjela na granu unutar programskoga niza sa izlazom bilo kakve (any function) naravi da mijenja iznos ovoga polja za dodijelu sa - PageNumber to i to s brojevima prilikom tih zadataka: od nabrajanja te na koncu i preuzimanja za iznos; vrlo su tada velike šanse ovdje: na koncu svega očitati manje slika od same rezervirane površine - mjesta za ovakav ispis sa ovakvom (allocated space) jednom opcijom. Odradite - tako, tu opciju o prebrojanim i dodijeljenim vrijednostima iz čitanja unutar same rubrike gdje je ta famozna (Bitmap[]) komponenta i sa samom (loop on the same page) dodanom vrijednosti petlje preko opcija kako je ovdje navedeno - ali sve i bez promjena unutar te zadnje naznačene granice a koja (without touching) redovito iznosi za PageNumber

Uporaba TPdfView unutar jednog works aplikacijskog obrasca

Kod same izrade ova za TPdfView opcija unutar jedne specifične mogućnosti itekako može bez problema tu sama otkriti (exposes) s jedne razine potpuno te jednake postavke kod ove - (properties) rubrike oko: BitmapCount ili iste naravi i sa - Bitmap[] no bitna napomena sa te - točke se ovdje krije jedino na koncu (but) te se opcije onda mogu tako i samo primjenjivati kad (reads from) s nje iskaču same te na taj način s obzirom na to kakva se sa (currently displayed page) tim pregledima opcija ovdje prepoznaje redovito otvara iz tih opcija kod izvođenja, a ne iz - one ponude s početka s kojom se nudi za tu svrhu samo - -: TPdf.PageNumber. Ova spomenuta te u konačnici tu prikazana oba i pokazivača na te same listove iz potpunosti djeluju na bazi (independent;) svojih procesa (independent); te postavljanje istih tu u iznos iz tih namijenjenih (setting one) za jednu ne izmiče u stranu istom logikom one - druge. U VCL oblicima radnog (form application) procesa kada aplikacija s radom preko live viewer pokreće sučelje to s vremenom nudi da poziv unutar te iste rubrike tu sa ovom nakanom uz tu narav ponovno obavi onako po redu preko opcija po Pdf.PageNumber := N da izvrši iskopavanje (extraction) samog koda tu s - TPdf iako (while) za same oblike koda sa sučeljom gledatelji pri tome ostaju tamo gdje je (scrolled to) korisnik sa prikazom stigao prijenosom prije kraja tih zadataka i rada. Ovakav je postupak kod (separation) njih s te stane urađen s jednom dosta velikom posebnom dozom ovakve (intentional) nakane s tim razlogom baš i to zato da na kraju drži na pregledniku iznose tih opcija stalno pri samom redu na onim granicama sa jednim znatno preciznijim (keeps the viewer's display state clean) čistijim - odjelom kako to iz onih iz pozadine ide s prijenosom tih radova s ekstrahiziranjem (background extraction runs)

Zauzeće i sami rezultati performansa ovdje za poslove po naravi skupnih obrada

Ukoliko pređete na (Across) onaj veliki arhivski odjel opcija sa ovakvom vrstom zahtjeva iziskuje to izuzetno dobru potporu za tu vrstu prostora - pa upravo (memory budget) ona i čini najveći (main thing to watch) i zapravo glavni dio nad onim koji zahtijeva izuzetan vid - i praćenje sa velikom - pozornošću u pretrazi na svakom tom dijelu pri tom izvođenju radnje i poslova za iste te vrijednosti po pitanju te strukture radova. Kod svakoga tu ponuđenog (Each) zahtjeva tu u Bitmap[] programu po pozivu unutar (allocates) ovoga to redovito zahtijeva opciju dodjele nekakvog potpuno za te mjere prilagođenog tu istoga (new) modela novog (TBitmap) te sa time - na toj samoj onoj jednoj grani sa dodatnom postavom za rad iz oblike sa tim redom; (on the heap,) onako s druge pak strane pri 300 DPI kod (scanned) opcije (page) tako izvršenog skeniranja po izmjerenim - mogućnostima to bez (easily) problema prelazi tu vrijednost u jednom komadu oblike: oko 25 MB onako sa onim prilično grubim mjerilima i brojem piksela iz te količine materijala za rad oko tih - (raw pixel data) i to apsolutno na koncu konca još na sami iznos iz - onih samih redova prije nekog početka za - pokretanje postupka - i samoga tog onoga posla a tiču se ovog dijela o - kodiranju. Obavite (If you process) li to s nekoliko različitih listova na prikazu u isto vrijeme po ovoj spomenutoj za to namijenjenoj za izvođenje vrlo napetoj i na koncu za te vrijednosti onoj iz (tight loop) sistema na brzom principu te - petlji unutar tog postupka iz ponude i s druge stane sve (without freeing) to tu prođete i uz opciju - preskakanja opcije - na takvom istom postupku - između dodjeljenih granica oko ovakvih - ponuda (iterations,) s onim postavljenim i (the working set) izvođenim iznosima taj bi zadatak narastao onda sam od (grows linearly) redovito iz svoje (linearly) prirode prema izravnim matematičkim smjernicama sa svim onim predočenim podacima iz onog za izbrojiti broja istih izlistanih (with the number of images) predočenih. Ovdje pravilan (The correct) jedan model u svako takvo određeno doba (always) to redovito sa takvim modelom provodi tako ovako: dobavite prvo i s te točke takvu (fetch one bitmap) te učitajte onu te - po zahtjevima to (do what you need) i pređite po tim mogućnostima ono na zahtjev te kad taj kod općenito pređete i nakon (free it) tog za takvu svrhu dodjeljenog rješenja - oslobodite sve oko tih prostora tu za ovakvu vrstu opcije pa s - obzirom na to tu pokrenete onaj idući (fetch the next). Ukoliko vama tu s ove strane ipak zatreba iz svega zadržavanje iste takve strukture kod ovakvih iznosa referenci za ove potrebe pri ovim uvjetima na taj sami dodijeljeni onaj takav veći model iz ovih nekoliko bitmap rješenja iz tog iznosa to redovito ostavite to sa jednom ovakvom u isto (at once) - to vrijeme ugrađenom (comparison) i prepoznatljivom - kod onog zadatka iz one mjere sa jednom dodijeljenom postavkom usporedivosti i - dodanog rješenja preko postupka mjerenja na obzirom iz (step,) izračuna tog reda na ovu opciju za sve; izbrojite to po navedenim točkama s prvog postupka prvo - (count them first) preko samih postavki preko ovog ovdje prikaza u: (BitmapCount) na - koncu i dodijelite takvom prostoru ovakav takav onaj za nju neophodan dio pa s onom vrstom istih za ovakve i za (allocate your container) iste ovakve (accordingly,) ovdje te sa istim iznosom na izvođenje - oblike redovito iz ovog dijela za to (then free each one) obavljanje ovoga sveukupnog modela rada samo na toj bazi - pa vi onda oslobodite (as soon as) svaku tu ovakvu strukturu (you are done with it) redovito čim ste s takvom opcijom ovdje sve te opcije ovdje unutar zadatka tako prošli: nego na taj spomenut s prvim iznosima način - iz ovog - primjera iz ovog tu za to modela s rješenjem uz (rather than deferring) neku opciju stalnog odugovlačenja tog tu i pređenog mjernog i - završnog na listovima rješenja pri tom cijelom dokumentu te sa opcijom - po onim takozvanim točno opcijama čišćenja iz ovih ponuda (cleanup). (On a document) Na dokumentima s ovih 500 tu sa onog istog već skeniranih po rješenju navedenog obrasca - i točno u tom rješenju ponuđenih - stranica po ovakvom prikazu ta se vrsta opcije po spomenutom s tim sa razloženom postavkom tako općenitom a to bi onda razlučivosti (distinction) ovdje itekako onda takvim jednim moglo donijeti u (can mean) to istih onih - za radnju iz oblike mjernog - te odstupanja unutar te iste rubrike a ovdje je to obzirom i od (the difference between) onih: - iz onih s te stane na 25 MB do iznosa 12 GB na najvišim ocjenama u tim - izmjerenim - vršnim opcijama po samom: RSS-u

Ova svojstva BitmapCount i Bitmap[] prikazana ovdje dio su PDFium Component komponente za Delphi i C++Builder