Draudimo žalų apdorojimo komanda turėjo trisdešimties metų popierinių bylų archyvą, kurį reikėjo nuskenuoti lapiniu skeneriu. Skeneris į aplanką išsaugojo po vieną JPEG vaizdą kiekvienam puslapiui, pavadindamas juos 0001.jpg, 0002.jpg ir t. t. Tačiau archyvui iš tikrųjų reikėjo vieno PDF failo kiekvienai bylai su nuosekliai išdėstytais puslapiais, kad vertintojas galėtų atidaryti vieną dokumentą, o ne spaudinėti šimtą vaizdų miniatiūrų. Šis paskutinis žingsnis, sunumeruotų skenuotų vaizdų krūvos pavertimas vienu tvarkingu PDF dokumentu, ir yra mūsų užduotis.
„PDFium VCL“ tai atlieka tiesiogiai. Be atvaizdavimo ir teksto išgavimo, komponentas gali sukurti PDF dokumentą nuo nulio: sukurti tuščią dokumentą, pridėti norimo dydžio puslapį, įdėti vaizdą į tą puslapį naudotojo erdvės koordinatėmis ir išsaugoti. Visas procesas valdomas per TPdf komponentą, todėl paketinis keitiklis tėra failų pavadinimų ciklas ir keletas funkcijų iškvietimų.
Konvertavimo eiga
Kiekvienam skenuotam vaizdui reikia atlikti tris veiksmus. Nustatote puslapio dydį, įdedate vaizdą puslapio viduje palikdami paraštę ir pereinate prie kito puslapio. „PDFium VCL“ kiekvienam iš šių veiksmų siūlo po metodą: AddPage sukuria nurodyto dydžio tuščią puslapį, AddImage (arba AddPicture, jei jau turite TPicture) įbrėžia taškinį vaizdą į dabartinį puslapį, o PageNumber nurodo komponentui, kurį puslapį paveiks vėlesni piešimo iškvietimai.
Viena iš detalių, kuri dažnai klaidina programuotojus, yra koordinačių sistema. PDF naudotojo erdvėje atskaitos taškas (pradžia) yra apatiniame kairiajame puslapio kampe, o Y ašis didėja į viršų – priešingai nei ekrano koordinatėse, kurias „Delphi“ programuotojai naudoja intuityviai. Metodui AddImage perduodamos X, Y koordinatės nurodo apatinį kairįjį vaizdo stačiakampio kampą, o Width, Height yra vaizdo dydis taškais (points), o ne šaltinio failo pikseliais. Supainiojus šiuos parametrus, jūsų skenuoti vaizdai gali atsidurti už puslapio ribų arba būti apversti.
Dokumento kūrimas ir puslapio priskyrimas kiekvienam vaizdui
Pradėkite nuo tuščio dokumento. CreateDocument išskiria atmintį naujam PDF dokumentui ir palieka komponentą aktyvų, todėl nereikia atskiro atidarymo žingsnio. Toliau einate per nuskenuotų failų sąrašą ir kiekvienam iš jų pridedate puslapį, padarote jį dabartiniu bei įkeliate vaizdą. Puslapio matmenys čia nurodomi A4 formatu taškais (595 × 842 stačias formatas) – tai standartinis archyvuojamų dokumentų lapo dydis.
procedure TArchiveForm.ScansToPdf(const Files: TStrings; const OutputPath: string);
const
PageW = 595.0; // A4 width in points
PageH = 842.0; // A4 height in points
Margin = 36.0; // half-inch border around each scan
var
I: Integer;
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument; // new, empty, already active
for I := 0 to Files.Count - 1 do
begin
Pdf.AddPage(I + 1, PageW, PageH); // 1-based page index
Pdf.PageNumber := I + 1; // make the new page current
PlaceScan(Pdf, Files[I], PageW, PageH, Margin);
end;
Pdf.SaveAs(OutputPath);
finally
Pdf.Free;
end;
end;
Kiekviena ciklo iteracija sukuria puslapį ir iškart priskiria jį savybei PageNumber. Ši antroji eilutė yra labai svarbi: AddPage įterpia puslapį, tačiau piešimo metodai veikia tame puslapyje, kuris yra nustatytas kaip dabartinis, todėl PageNumber nustatymas nukreipia AddImage į ką tik sukurtą puslapį. Jei to nepadarysite, visi vaizdai bus sudėti vienas ant kito tame puslapyje, kuris buvo įkeltas prieš tai.
Šiame cikle slypi viena prielaida – failų Files tvarka. Skeneris puslapius gali pavadinti nuo 0001.jpg iki 0100.jpg, tačiau katalogų nuskaitymas ne visada grąžina juos surūšiuotus. Pavyzdžiui, kai failas page9.jpg atsiduria šalia page10.jpg, paprastas eilučių rūšiavimas gali pastatyti 10 puslapį prieš 9. Prieš paleisdami ciklą, aiškiai surūšiuokite sąrašą ir skenavimo metu naudokite nuliais papildytus pavadinimus, kad leksinė tvarka sutaptų su puslapių tvarka. Puslapių seka yra pirmasis dalykas, kurį pastebės vertintojas, ir tai yra lengviausiai ištaisoma klaida.
Skenuoto vaizdo įterpimas išlaikant jo kraštinių santykį
Skenuotas vaizdas retai kada tiksliai atitinka puslapio formą. Jei jį ištempsite, kad užpildytų visą lapą, iškraipysite tekstą; jei įterpsite pilnu pikselių dydžiu, jis netilps puslapyje. Sprendimas – pritaikyti mastelį pagal mažesnį iš dviejų koeficientų (pločio arba aukščio) ir centruoti likusią erdvę. Kadangi atskaitos taškas yra apatiniame kairiajame kampe, centravimas reiškia, kad likusi laisva vieta padalijama tolygiai ir pridedama prie X ir Y koordinačių.
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, etc. via the VCL graphics units
AvailW := PageW - 2 * Margin;
AvailH := PageH - 2 * Margin;
// Fit inside the margins without distorting the scan.
Scale := Min(AvailW / Pic.Width, AvailH / Pic.Height);
DrawW := Pic.Width * Scale;
DrawH := Pic.Height * Scale;
// Center: leftover space split evenly. Y measured from the page bottom.
X := (PageW - DrawW) / 2;
Y := (PageH - DrawH) / 2;
Pdf.AddImage(FileName, X, Y, DrawW, DrawH);
finally
Pic.Free;
end;
end;
Šis kodas vieną kartą įkelia failą, kad nustatytų jo pikselių matmenis, apskaičiuoja vienodą mastelį ir perduoda stačiakampio koordinates metodui AddImage. AddImage priima failo kelią tiesiogiai ir nukreipia jį per tą pačią vaizdų apdorojimo grandinę kaip ir AddPicture, obso bet koks formatas, kurį atpažįsta VCL grafikos moduliai, veikia be papildomų sąlygų. Jei jau turite iškoduotą vaizdą TPicture objekte (pavyzdžiui, iš peržiūros skydelio), iškvieskite AddPicture(Pic, X, Y, DrawW, DrawH) su tuo pačiu stačiakampiu ir išvenkite pakartotinio failo skaitymo.
Iškodavimo išvengimas naudojant JPEG skenuotus vaizdus
Skeneriai beveik visada pateikia JPEG vaizdus. Įkeliant JPEG į TPicture, jis iškoduojamas į taškinį vaizdą (bitmap), o išsaugant PDFium jį vėl užkoduoja iš naujo, tai sukelia du nereikalingus nuostolingus konvertavimo ciklus. Metodas AddJpegImage įterpia originalius suspaustus baitus tiesiai į puslapį iš srauto, o tai yra ir greičiau, ir vizualiai kokybiškiau apdorojant didelius kiekius.
var
Stream: TFileStream;
begin
// ... after AddPage + PageNumber for the current page ...
Stream := TFileStream.Create(FileName, fmOpenRead);
try
// Embeds the JPEG bytes as-is; no decode/re-encode cycle.
Pdf.AddJpegImage(Stream, X, Y, DrawW, DrawH);
finally
Stream.Free;
end;
end;
Koordinates X, Y, DrawW ir DrawH vis tiek apskaičiuojate taip pat, nes masteliui nustatyti reikalingi pikselių matmenys. Perskaitykite juos iš failo arba greitai išanalizuokite jo antraštę, o tada perduokite neapdorotą srautą metodui AddJpegImage. PNG arba TIFF vaizdams tinkamas kelias yra AddImage; JPEG trumpinį palikite tik šiam konkrečiam formatui.
Kiekvieno puslapio žymėjimas
Archyvuotus vaizdus lengviau patikrinti, kai kiekviename puslapyje yra nurodytas jo šaltinio failo pavadinimas. Metodas AddText nubrėžia eilutę naudotojo erdvės koordinatėse, todėl užrašas gali būti patalpintas tiesiai po vaizdu. Atsiminkite apverstą Y ašį: norėdami įdėti užrašą po vaizdu, turite atimti iš vaizdo apatinio krašto koordinatės, o ne pridėti prie jos.
// Caption below the scan: Y decreases toward the page bottom.
Pdf.AddText('File: ' + ExtractFileName(FileName), 'Helvetica', 9,
X, Y - 14, clGray);
Išsaugojimas ir apibendrinimas
Paskutinė pastaba apie išsaugojimą. SaveAs yra funkcija, kuri grąžina loginę reikšmę (Boolean), todėl gamybiniame kode patikrinkite jos rezultatą, o ne darykite prielaidą, kad įrašymas pavyko. Priešingu atveju pilnas diskas arba užblokuotas išvesties kelias sukels klaidą be jokio pranešimo. Ciklui pasibaigus ir failui įsirašius, turėsite būtent tai, ko reikėjo archyvui: vieną sutvarkytą PDF failą kiekvienai bylai su tinkamo mastelio puslapiais, paruoštą peržiūrėti bet kurioje programoje.
Tie patys komponentai tinka ir panašioms užduotims. Pakeiskite puslapio dydžio taisyklę ir gausite nuotraukų albumą su vienu vaizdu lape; palikite ciklą, bet skaitykite iš kelių puslapių TIFF šaltinio, ir turėsite faksų archyvo keitiklį. Jei norite sužinoti daugiau apie PDF failų kūrimą programiškai, žr. PDF dokumentų kūrimas nuo nulio naudojant PDFium VCL; norėdami vėliau atvaizduoti rezultatą ekrane, žr. PDF puslapių konvertavimas į JPEG vaizdus naudojant PDFium VCL.
„loslab.com“ siūlomas PDFium VCL Component sujungia dokumentų kūrimo, atvaizdavimo ir teksto apdorojimo API, naudojamus šioje straipsnių serijoje.