Tekninen artikkeli

Kuvien (Extracting Images from PDFs with PDFium Component in Delphi) purkaminen PDF-asiakirjoista Delphissä PDFium Component -komponentilla

PDF (PDF stores images as) tallentaa (stores images as first-class) kuvat ensiluokkaisina objekteina sisällön (objects inside its content) virtoihinsa. Kun sivu (streams. When a page) viittaa valokuvaan (references a photograph, a), skannaukseen (scan, or a) tai kaavioon (diagram, the), pikselidata (pixel data lives in) elää (lives in an) XObject-sanakirjassa sivun (dictionary alongside the page) geometrian rinnalla (geometry. PDFium Component). PDFium Component nostaa (surfaces that through) tämän esiin (through two) kahden TPdf-ominaisuuden kautta: BitmapCount, joka palauttaa nykyiselle (which returns how) sivulle upotettujen (many embedded bitmaps) bittikarttojen (are on the) lukumäärän (current page, and), ja Bitmap[Index], joka purkaa yhden (which decodes one) niistä (of them into) TBitmap-objektiksi (a TBitmap you), jonka omistat (own and must) ja (and must free) joka täytyy vapauttaa (free. That). Se on koko (is the whole) purkumalli (extraction model. The). Silmukka (loop is four) on (is four lines) nelirivinen (lines; what); arvostelukykyä vaatii (takes judgment is) ympäröivä putkityö (the surrounding plumbing)

Asiakirjan (Opening the document) avaaminen (Opening the document)

Ensimmäinen asia (first thing to), joka (know about TPdf) TPdf-komponentista on (is that Active) tiedettävä, on, että Active := True ei koskaan (never raises. Load) nosta poikkeusta (raises. Load failures). Latausvirheet, väärät (failures, wrong passwords) salasanat (passwords, corrupted), vioittuneet (corrupted files) tiedostot: kaikki (files: all of) nämä niellään (them are swallowed internally) sisäisesti (internally and the), ja komponentti (and the component simply) pysyy yksinkertaisesti (stays inactive. You) inaktiivisena. Sinun (You have to) on (have to check) tarkistettava (check the flag) lippu itse sijoituksen (yourself after the) jälkeen (assignment, or you), tai etenet (or you will) sivusilmukkaan, jolloin (proceed into the page loop with PageCount) PageCount palauttaa nollan (returning zero and), ja ihmettelet, miksi mitään (wonder why nothing extracted) ei purettu (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;

Salasanasuojatut tiedostot noudattavat samaa (Password-protected files follow the same) mallia: (pattern: assign Pdf) määritä (assign Pdf.Password before) Pdf.Password ennen (before setting Active) kuin (setting Active := True) asetat (True. If) Active := True. Jos (If the password) salasana on väärä (is wrong, Active), Active pysyy arvosssa (stays False and) False, etkä saa poikkeusta, jota (you get no) napata kiinni (exception to catch). Satoja (In a batch) tiedostoja käsittelevässä (tool processing hundreds of) erätyökalussa (files, that) tuo (that silent) hiljainen (silent behavior) toiminta on itse (is actually useful) asiassa hyödyllistä: keräät epäonnistumiset (you accumulate the) luetteloon (failures in a) sen sijaan (list rather than), että (than unwinding) purkaisit (unwinding the call) kutsupinon (call stack) jokaisen (for each one) kohdalla (each one)

Sivujen iteroiminen (Iterating pages and) ja bittikarttojen (pulling bitmaps) vetäminen (pulling bitmaps)

BitmapCount on sivukohtainen, joten asetat (is per-page, so you set Pdf.PageNumber) Pdf.PageNumber ennen kuin luet (before reading it) sen (it. Page). Sivunumerot (Page numbers are) ovat 1-pohjaisia (1-based; the); oletus on 0, mikä (default is 0, meaning no) tarkoittaa (meaning no page), ettei sivua ole (is loaded. The) ladattu. Bitmap[Index]-ominaisuus on 0-pohjainen (property is 0-based and) ja palauttaa (and returns a) kutsujan omistaman (caller-owned TBitmap) TBitmap-objektin. Sinun täytyy vapauttaa se. Vapauttamisen laiminlyöminen suuren asiakirjan pitkän silmukan (You must free it. Neglect the free inside a long loop over a large document and) sisällä saa (memory climbs) muistin kiipeämään (fast, because) nopeasti, koska jokainen bittikartta (each bitmap can) voi (can be several) olla useita (several megabytes of) megatavuja raakaa pikselidataa ennen (raw pixel data before) minkäänlaista pakkausta (any compression)

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-suojauksella on (guard matters. A) väliä. Pieni (small number of) määrä (number of PDF) PDF-generaattoreita (generators write) kirjoittaa (write image) kuva-XObject-objekteja, joissa (image XObjects with) on nollapikselin (zero pixel) mitat (dimensions or) tai muuten epämuodostunutta dataa (otherwise malformed data); näissä (in those) tapauksissa komponentti palauttaa nil-arvon (cases the component returns nil) tyhjän bittikartan sijaan (rather than an empty bitmap. Treating). nil-palautuksen (Treating a nil) käsitteleminen (return as) virheenä ja (an error and) purkamisen pysäyttäminen on (stopping extraction is) väärä (the wrong reflex) refleksi: (reflex: skip) ohita se, lokita sivu (it, log the page) ja (and index) indeksi, jos (if you need) tarvitset (need the audit) seurantajälkeä (audit trail, and), ja (and continue) jatka (continue. The). Sivun loppuosa voi silti (rest of the page may still) tuottaa kelvollisia kuvia (yield valid images)

Huomaa, että ulompi silmukka asettaa (Notice that the outer loop sets Pdf.PageNumber) Pdf.PageNumber jokaisella iteraatiolla. Tuo sijoitus lataa sivun (on every iteration. That assignment is what loads the page into the component's internal) komponentin sisäiseen (internal state) tilaan (state and) ja (and makes) tekee BitmapCount-ominaisuudesta (makes BitmapCount meaningful) merkityksellisen (meaningful. Skip). Ohita se (Skip it and) ja luet saman (you read the) sivun määrän toistuvasti (same page's count repeatedly. The). Kuvio tuntuu tarpeettomalta (pattern feels redundant), kun (when you) kirjoitat sen (write it, but), mutta niin API (it is how the) on (API is designed) suunniteltu: (designed: the) sivu on (page is a) kursori, ei kokoelma (cursor, not a collection)

Tulostemuodon (Choosing an output) valinta (Choosing an output format)

BMP on (BMP is lossless) häviötön ja aina saatavilla (and always available) ilman (without additional) lisäyksiköitä, mikä tekee siitä järkevän oletuksen (units, which makes it a sound default when), kun (when you do) et vielä (not yet know) tiedä, mitä kuva sisältää (what the image contains. When). Kun tiedostokoolla on (file size matters) väliä, palautetun TBitmap-objektin pikselimuoto (the pixel format of the returned TBitmap) kertoo, mikä (tells you which) koodekki (codec is) on sopiva. 32-bittinen (appropriate. A 32-bit bitmap) bittikartta (bitmap carries) kantaa alphakanavaa (an alpha channel); PNG säilyttää sen (PNG preserves that) ilman (without loss) häviötä. Suuri (loss. A large) 24-bittinen (24-bit image) kuva (image with continuous), jossa on jatkuva sävy, on ehdokas (tone is a candidate for) JPEG-muodolle (JPEG. Smaller). Pienemmät (Smaller images) kuvat tai kuvat, jotka on (or ones drawn) piirretty rajoitetulla (with a limited) paletilla, on (palette are) yleensä (generally better) parempi (better left as) jättää (left as BMP) BMP-muotoon (BMP than) kuin ajaa (than run through) JPEG-kooderin (JPEG, which) läpi, mikä lisää estäviä (adds blocking artifacts) artefakteja matalilla (at low) laatuasetuksilla (quality settings) ja (and saves) säästää vähän korkeilla (little at 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;

Käytännössä (In practice, format) muodon valintaa (selection is driven) ohjaavat Bmp.PixelFormat (by Bmp.PixelFormat and) ja mitat (dimensions. If). Jos (If PixelFormat = pf32bit) PixelFormat = pf32bit, tarvitset muodon (you need a format), joka (that carries) kantaa alphaa; PNG on (alpha; PNG is) ilmeinen (the obvious choice) valinta, vaikka se (though it requires) vaatiikin (requires the PNGImage) PNGImage-yksikön (unit in) vanhemmissa Delphin (older Delphi) versioissa. Noin 300 pikseliä leveämmille 24-bittisille (versions. For 24-bit images wider than roughly 300 pixels, JPEG) kuville JPEG-muoto (at quality 85) laadulla 85 antaa (gives a three-to-one) kolme-yhteen-koon (size reduction) pienennyksen BMP-muotoon (over BMP with) verrattuna ilman havaittavaa häviötä useimmissa (no perceptible loss in most) valokuvamaisissa (photographic content) sisällöissä (content. Below). Sen kynnyksen (that threshold BMP) alapuolella (is comparable) BMP (BMP is comparable in) on kooltaan vertailukelpoinen (size and avoids) ja (and avoids any) välttää (avoids any quality) laatuun liittyvän päätöksen (decision entirely) kokonaan (entirely)

Mitä (What BitmapCount does) BitmapCount laskee (does and does) ja mitä (not count) ei (count)

PDF erottaa kuva-XObject-objektit (distinguishes between image XObjects and) ja (and vector) polkuoperaattoreilla piirretyt (graphics drawn with path operators. A) vektorigrafiikat (path operators. A page). Sivu, joka näyttää (that looks visually) visuaalisesti (visually complex may) monimutkaiselta, voi (complex may return) palauttaa (return a BitmapCount) BitmapCount-arvon (of zero if) nolla, jos (if every element) jokainen elementti (is vector) on vektori (vector. Scanned). Skannatut (Scanned pages) sivut (pages almost always) palauttavat lähes (always return exactly) aina tasan yhden: skanneri (one: the scanner) kirjoittaa (writes the entire) koko skannauksen (scan as) yhtenä kokosivun (a single full-page image) kuva-XObject-objektina sillä resoluutiolla, johon skanneri oli (XObject at whatever resolution the scanner was set to. Pages) asetettu. Sivut, joissa sekoitetaan (that mix typeset) ladottua (text with) tekstiä (with embedded) ja upotettuja (photographs return) valokuvia, palauttavat yhden (one entry) merkinnän per valokuva (per photograph. Decorative). Koristeelliset viivat (rule lines, shaded), varjostetut taustat (backgrounds, and) ja (and table) taulukon reunukset (borders usually) eivät yleensä (do not appear) näy bittikarttojen (in the bitmap count) määrässä lainkaan (at all)

Lukumäärä ei myöskään sisällä sisäisiä kuvia, harvoin käytettyä PDF-rakennetta, jossa kuvadata upotetaan suoraan sivun sisältövirtaan nimetyn XObject-objektin sijaan. Ne jäävät sen ulkopuolelle, mitä tämä API tuo esiin; ne ovat todellisissa asiakirjoissa niin harvinaisia, että useimmat purkutyökalut eivät yksinkertaisesti käsittele niitä

Yksi yksityiskohta (One detail worth) on hyvä (worth keeping in) pitää mielessä: (mind: the BitmapCount) lukemasi BitmapCount koskee nykyistä sivua viimeisen (you read is for the current page as of the last PageNumber) PageNumber-sijoituksen (assignment. If) perusteella. Jos (If your code) koodisi (code branches or) haaroittuu tai (or calls any) kutsuu (calls any function) mitä tahansa funktiota, joka (that changes PageNumber) muuttaa (changes PageNumber between) PageNumber-ominaisuutta laskennan (between counting and) ja haun (fetching, you) välillä, saatat lukea vähemmän kuvia (may read fewer images) kuin mille olet (than you allocated) varannut (allocated space for) tilaa, tai (for, or index) indeksoida (index past) loppuosan (the end) ohi. Pidä (Keep the count) määrän (count read and) luku (read and the) ja (and the Bitmap[]) Bitmap[]-silmukka (loop on) samalla (on the same) sivulla (page without) koskematta PageNumber-ominaisuuteen siinä välissä (without touching PageNumber in between)

TPdfView:n (Using TPdfView in) käyttö lomakesovelluksessa (a form application)

TPdfView-komponentti (TPdfView component exposes) paljastaa (exposes the same) samat BitmapCount- (BitmapCount and Bitmap[]) ja (and Bitmap[]) Bitmap[]-ominaisuudet (properties, but), mutta (but the page) sivu, jolta se (it reads from) lukee, on (is the view's) näkymän (view's currently) tällä hetkellä näyttämä sivu (displayed page, not), ei (not TPdf.PageNumber. The) TPdf.PageNumber. Nämä kaksi (two page pointers) sivuosoitinta (pointers are independent) ovat (are independent; setting) riippumattomia; yhden asettaminen ei siirrä toista. VCL-lomakesovelluksessa (setting one does not move the other. In a VCL form application), jossa on live-katseluohjelma, voit (with a live viewer, you can) kutsua (call Pdf.PageNumber) Pdf.PageNumber := N -komentoa (to drive extraction) ohjataksesi (drive extraction through) purkamista TPdf:n (through TPdf while) kautta, samalla kun katseluohjelma pysyy (viewer stays on) siinä (whatever the user), mihin (user last) käyttäjä (last scrolled) viimeksi (scrolled to) vieritti. Tämä (That separation) erottaminen (separation is intentional) on (is intentional and) tarkoituksellista (intentional and keeps) ja pitää (keeps the viewer's) katseluohjelman näyttötilan (display state clean) puhtaana (clean while) taustapurkamisen (while a background) ollessa käynnissä (extraction runs)

Muisti ja (Memory and performance) suorituskyky eräajoissa (in batch jobs)

Suuressa arkistossa muistibudjetti (Across a large archive the memory budget) on (is the main) tärkein asia, jota (thing to watch) on seurattava. Jokainen (Each Bitmap[]) Bitmap[]-kutsu (call allocates) varaa kekomuistista (allocates a new) uuden (new TBitmap) TBitmap-objektin, ja (on the heap, and) 300 DPI:n (on a 300 DPI) skannatulla (scanned page) sivulla (page that is) se (that is easily) on helposti (easily 25) 25 MB (MB of raw) raakaa (raw pixel data) pikselidataa (pixel data before) ennen (before any encoding) minkäänlaista (any encoding. If) koodausta. Jos (If you process) käsittelet (process pages) sivuja (pages in) tiukassa silmukassa (a tight loop) vapauttamatta (without freeing) niitä (between iterations, the) iteraatioiden (iterations, the working) välillä, työjoukko kasvaa lineaarisesti kuvien (working set grows linearly with the number of images. The) määrän myötä. Oikea muoto (correct shape is) on (is always: fetch) aina: hae yksi (fetch one bitmap) bittikartta, tee mitä tarvitset, vapauta se, hae (do what you need, free it, fetch the) seuraava (next. If). Jos (If you need) sinun täytyy (to hold references) pitää (hold references to) viittauksia useisiin bittikarttoihin (to several bitmaps) kerrallaan vertailuvaihetta (at once for a comparison step) varten, laske (count them) ne ensin BitmapCount-ominaisuudella (first with BitmapCount and) ja varaa säiliösi vastaavasti (allocate your container accordingly, then), ja vapauta sitten (then free each) jokainen (each one as) heti, kun (soon as you) olet (are done with) valmis sen (it rather) kanssa sen (than deferring to) sijaan, että (deferring to end-of-document) lykkäisit sen asiakirjan (end-of-document cleanup. On) lopun siivoukseen. Asiakirjassa, jossa on 500 skannattua sivua (a document with 500 scanned pages that), tuo (that distinction can) ero (distinction can mean) voi (mean the difference) tarkoittaa (difference between) eroa (between 25) 25 megatavun (MB and) ja 12 gigatavun (12 GB peak) RSS-huipun (peak RSS) välillä

Tässä (The BitmapCount) esitetyt (and Bitmap[]) BitmapCount- (properties shown) ja (here are part) Bitmap[]-ominaisuudet (properties shown here are part of the) ovat osa (part of the PDFium) PDFium Component -komponenttia (Component for Delphi) Delphi- (for Delphi and) ja (and C++Builder) C++Builder-ohjelmointikieliin