Tekninen artikkeli

Skannattujen (Scanned Images into One PDF with PDFium Component in Delphi) kuvien yhdistäminen (Combine Scanned Images into One PDF with PDFium Component in Delphi) yhdeksi PDF-tiedostoksi Delphissä PDFium Component -komponentilla

Korvauskäsittelytiimillä oli kolmenkymmenen vuoden edestä paperikansioita, jotka kulkivat arkkisyöttöisen skannerin läpi. Skanneri sylkäisi ulos yhden JPEG-kuvan per sivu kansioon, nimettynä 0001.jpg, 0002.jpg ja niin edelleen. Mitä arkisto todella tarvitsi, oli yksi PDF per tapauskansio, sivut järjestyksessä, jotta arvioija voisi avata yhden asiakirjan satojen kuvapikkukuvien läpiklikkailun sijaan. Tuo viimeinen vaihe, numeroidun skannauspinon muuttaminen yhdeksi järjestetyksi PDF-tiedostoksi, on työ tässä

PDFium Component käsittelee sen suoraan. Hahmontamisen ja tekstin purkamisen lisäksi komponentti voi rakentaa PDF:n alusta alkaen: luoda tyhjän asiakirjan, lisätä tyhjän sivun halutun kokoisena, pudottaa kuvan kyseiselle sivulle käyttäjätilan koordinaateissa ja sitten tallentaa. Koko liukuhihna asuu TPdf-komponentissa, joten erämuunnin on silmukka tiedostonimien yli sekä kourallinen kutsuja

Delphi-eräputki käyttää PDFium Componentin AddPage-, PageNumber-, AddImage- ja SaveAs-kutsuja muuntaakseen numeroidut skannaukset kansiosta yhdeksi järjestyksessä olevaksi PDF:ksi
Jokainen skannaus muuttuu yhdeksi sivuksi, jonka AddPage luo ja jota PageNumber suuntaa ennen kuin AddImage piirtää sen; SaveAs kirjoittaa valmiin dokumentin kerran

Muunnoksen kulku

Kolme asiaa on tapahduttava jokaiselle skannaukselle. Päätät sivukoon, sijoitat kuvan sivun sisälle jättäen marginaalin, ja siirryt seuraavalle sivulle. PDFium Component antaa sinulle yhden metodin kutakin varten: AddPage luo tyhjän sivun tietyssä koossa, AddImage (tai AddPicture, jos sinulla on jo TPicture) piirtää bittikartan nykyiselle sivulle, ja PageNumber kertoo komponentille, mihin sivuun myöhemmät piirtokutsut kohdistuvat

Yksi yksityiskohta, joka yllättää ihmiset, on koordinaattijärjestelmä. PDF:n käyttäjätila asettaa origon sivun vasempaan alakulmaan, jolloin Y kasvaa ylöspäin, päinvastoin kuin näyttökoordinaatit, joihin Delphi-kehittäjät kurottavat refleksinomaisesti. AddImage-metodille välittämäsi X, Y on kuvasuorakulmion vasen alakulma, ja Width, Height ovat asetuskoko pisteinä, ei lähdetiedoston pikselikoko. Tee se väärinpäin, ja skannauksesi laskeutuvat sivun ulkopuolelle tai ylösalaisin suhteessa siihen, minne odotit niiden tulevan

Asiakirjan ja yhden sivun luominen per skannaus

Aloita tyhjällä asiakirjalla. CreateDocument varaa uuden PDF:n ja jättää komponentin aktiiviseksi, joten erillistä avausvaihetta ei ole. Sieltä käsin käyt läpi skannattujen tiedostojen luettelon, ja jokaiselle niistä lisäät sivun, teet siitä nykyisen ja sijoitat kuvan. Sivun mitat ovat tässä A4 pisteinä (595 × 842 pystysuunnassa), arkistoidun kirjeenvaihdon vakioarkkikoko

procedure TArchiveForm.ScansToPdf(const Files: TStrings; const OutputPath: string);
const
  PageW = 595.0;   // A4-leveys pisteinä
  PageH = 842.0;   // A4-korkeus pisteinä
  Margin = 36.0;   // puolen tuuman reunus kunkin skannauksen ympärillä
var
  I: Integer;
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.CreateDocument;                       // uusi, tyhjä, jo aktiivinen
    for I := 0 to Files.Count - 1 do
    begin
      Pdf.AddPage(I + 1, PageW, PageH);       // 1-pohjainen sivuindeksi
      Pdf.PageNumber := I + 1;                // aseta uusi sivu nykyiseksi
      PlaceScan(Pdf, Files[I], PageW, PageH, Margin);
    end;
    Pdf.SaveAs(OutputPath);
  finally
    Pdf.Free;
  end;
end;

Jokainen iteraatio luo sivun ja asettaa välittömästi PageNumber-ominaisuuden siihen. Tuo toinen rivi on tärkeä: AddPage lisää sivun, mutta piirtometodit vaikuttavat siihen sivuun, joka sattuu olemaan nykyinen, joten PageNumber-ominaisuuden asettaminen on se, mikä kohdistaa AddImage-kutsun juuri tekemääsi sivuun. Ohita se, ja kuvasi pinoutuvat sille sivulle, joka sattui olemaan ladattuna aiemmin

Yksi oletus piiloutuu tuohon silmukkaan: Files-luettelon järjestys. Skanneri nimeää sivut väliltä 0001.jpg0100.jpg, mutta hakemistoluettelointi ei aina palauta niitä lajiteltuina, ja sillä hetkellä kun osut tiedostoon page9.jpg tiedoston page10.jpg vieressä, tavallinen merkkijonolajittelu asettaa sivun 10 ennen sivua 9. Lajittele luettelo nimenomaisesti ennen silmukkaa, ja suosi nollatäytettyjä nimiä skannaushetkellä, jotta leksikaalinen järjestys vastaa sivujärjestystä. Sivujärjestys on se yksi asia, jonka arvioija huomaa välittömästi, ja se on halvin estettävissä oleva virhe

Skannauksen sijoittaminen ja kuvasuhteen säilyttäminen

Skannaus on harvoin samanmuotoinen kuin sivu. Jos venytät sen täyttämään arkin, vääristät tekstiä; jos sijoitat sen täydessä pikselikoossa, se pursuaa yli. Ratkaisu on skaalata kahdesta suhteesta, leveyssovituksesta tai korkeussovituksesta, pienemmällä, ja keskittää se, mitä jää yli. Koska origo on vasemmalla alhaalla, keskittäminen tarkoittaa jäännöstilan jakamista tasan ja sen lisäämistä sekä X- että Y-arvoon

A4-sivun kaavio osoittaa, miten PDFium Component AddImage sijoittaa skaalatun skannauksen marginaalien sisään Delphi-koodilla ja vasemman alakulman origolla
AddImage ottaa sijoittelusuorakulmion vasemman alakulman, joten sovittaminen ja keskittäminen lasketaan sivupisteinä origosta
procedure TArchiveForm.PlaceScan(Pdf: TPdf; const FileName: string;
  PageW, PageH, Margin: Double);
var
  Pic: TPicture;
  AvailW, AvailH, Scale, DrawW, DrawH, X, Y: Double;
begin
  Pic := TPicture.Create;
  try
    Pic.LoadFromFile(FileName);              // BMP, JPG, PNG jne. VCL:n grafiikkayksiköiden kautta

    AvailW := PageW - 2 * Margin;
    AvailH := PageH - 2 * Margin;

    // Sovita marginaalien sisään vääristämättä skannausta.
    Scale := Min(AvailW / Pic.Width, AvailH / Pic.Height);
    DrawW := Pic.Width * Scale;
    DrawH := Pic.Height * Scale;

    // Keskitys: ylijäävä tila jaetaan tasan. Y mitataan sivun alareunasta.
    X := (PageW - DrawW) / 2;
    Y := (PageH - DrawH) / 2;

    Pdf.AddImage(FileName, X, Y, DrawW, DrawH);
  finally
    Pic.Free;
  end;
end;

Tämä lataa tiedoston kerran lukeakseen sen pikselimitat, laskee yhden yhtenäisen skaalan ja välittää asettelusuorakulmion AddImage-kutsulle. AddImage hyväksyy tiedostopolun suoraan ja reitittää sen saman kuvaliukuhihnan kautta kuin AddPicture, joten mikä tahansa muoto, jonka VCL-grafiikkayksiköt tunnistavat, toimii ilman erikoiskäsittelyä. Jos kuva on jo purettu TPicture-objektiin esikatseluruudusta, kutsu AddPicture(Pic, X, Y, DrawW, DrawH) samalla suorakulmiolla ja ohita toinen tiedostonluku

Purun ohittaminen JPEG-skannauksissa

Skannerit tuottavat lähes aina JPEG-muotoa. JPEG:n lataaminen TPicture-objektiin purkaa sen bittikartaksi, ja sitten PDFium koodaa sen uudelleen tallennettaessa, kaksi häviöllistä edestakaista matkaa, joita et tarvitse. AddJpegImage upottaa alkuperäiset pakatut tavut suoraan sivulle virrasta, mikä on sekä nopeampaa että visuaalisesti siistimpää suuren volyymin eräajossa

AddJpegImage upottaa alkuperäiset JPEG-tavut PDFium Component -sivulle, kun taas AddImage dekoodaa ja SaveAs koodaa pikselit uudelleen Delphissä
AddJpegImage upottaa skannerin pakatut tavut sellaisenaan välttäen purku- ja uudelleenkoodauskierrokset, jotka AddImage ja SaveAs suorittavat
var
  Stream: TFileStream;
begin
  // ... AddPage + PageNumber -kutsujen jälkeen nykyiselle sivulle ...
  Stream := TFileStream.Create(FileName, fmOpenRead);
  try
    // Upottaa JPEG-tavut sellaisenaan; ei purku-/uudelleenkoodauskiertoa.
    Pdf.AddJpegImage(Stream, X, Y, DrawW, DrawH);
  finally
    Stream.Free;
  end;
end;

Lasket silti X-, Y-, DrawW- ja DrawH-arvot samalla tavalla, koska tarvitset pikselimitat skaalausta varten. Lue ne tiedostosta tai nopealla otsikon jäsennyksellä, ja anna sitten raaka virta AddJpegImage-kutsulle. PNG- tai TIFF-skannauksille AddImage-polku on oikea; varaa JPEG-pikakuvake sille muodolle, johon se todella soveltuu

Jokaisen sivun merkitseminen

Arkistoituja skannauksia on helpompi auditoida, kun jokainen sivu kantaa lähdetiedostonsa nimeä. AddText piirtää merkkijonon käyttäjätilan koordinaattiin, joten kuvateksti asettuu juuri kuvan alapuolelle. Muista käännetty Y-akseli: asettaaksesi otsikon skannauksen alle, vähennät kuvan alareunasta sen sijaan, että lisäisit siihen

// Kuvateksti skannauksen alla: Y pienenee kohti sivun alareunaa.
Pdf.AddText('File: ' + ExtractFileName(FileName), 'Helvetica', 9,
  X, Y - 14, clGray);

Vielä yksi huomio tallentamisesta. SaveAs on funktio, joka palauttaa Boolen arvon, joten tarkista tuotantokoodissa sen tulos sen sijaan, että olettaisit kirjoituksen onnistuneen; täysi levy tai lukittu tulostepolku epäonnistuu muuten hiljaisesti. Kun silmukka päättyy ja tiedosto on kirjoitettu, sinulla on täsmälleen se, mitä arkisto tarvitsi: yksi järjestetty PDF per tapauskansio, sivut skaalattuna sopiviksi, valmiina luettavaksi missä tahansa katseluohjelmassa

Samat rakennuspalikat kattavat vastaavia töitä. Vaihda sivukohtainen mitoitussääntö, niin saat valokuvakirjan, jossa on yksi kuva per arkki; pidä silmukka, mutta lue monisivuisesta TIFF-lähteestä, ja sinulla on faksiarkiston muunnin. Jos haluat laajemman kuvan PDF-tiedostojen rakentamisesta ohjelmallisesti, katso PDF-asiakirjojen luominen tyhjästä PDFium Component -komponentilla; hahmontaaksesi tuloksen takaisin näytölle myöhemmin, katso PDF-sivujen muuntaminen JPEG-kuviksi PDFium Component -komponentilla

PDFium Component osoitteesta loslab.com niputtaa yhteen tässä sarjassa käytetyt asiakirjan luonti-, hahmonnus- ja teksti-API:t