Artikel Teknis

Mengonversi gambar ke dokumen PDF dengan PDFium di Delphi

Sebuah tim pemrosesan klaim memiliki tiga puluh tahun file kertas yang melewati pemindai sheet-fed. Pemindai mengeluarkan satu JPEG per halaman ke dalam folder, bernama 0001.jpg, 0002.jpg, dan seterusnya. Yang sebenarnya dibutuhkan arsip adalah satu PDF per berkas kasus, dengan halaman dalam urutan, sehingga seorang peninjau bisa membuka satu dokumen alih-alih mengklik melalui seratus gambar mini. Langkah terakhir itu, mengubah tumpukan scan bernomor menjadi satu PDF terurut, adalah pekerjaan di sini

PDFium Component menanganinya secara langsung. Di luar rendering dan ekstraksi teks, komponen bisa membangun PDF dari nol: membuat dokumen kosong, menambahkan halaman kosong berukuran sesuka Anda, menjatuhkan gambar ke halaman itu dalam koordinat user-space, lalu menyimpan. Seluruh pipeline hidup pada komponen TPdf, jadi pengonversi batch adalah loop atas nama file ditambah segelintir panggilan

Pipeline batch Delphi memakai pemanggilan AddPage, PageNumber, AddImage, dan SaveAs PDFium Component untuk mengubah folder hasil pemindaian bernomor menjadi satu PDF terurut
Setiap hasil pindai menjadi satu halaman yang dibuat AddPage dan diarahkan PageNumber sebelum AddImage menggambarnya; SaveAs menulis dokumen jadi sekali

Bentuk konversi

Tiga hal harus terjadi untuk setiap scan. Anda memutuskan ukuran halaman, menempatkan gambar di dalam halaman menyisakan margin, dan maju ke halaman berikutnya. PDFium Component memberi Anda satu metode untuk masing-masing: AddPage membuat halaman kosong pada ukuran tertentu, AddImage (atau AddPicture jika Anda sudah memegang TPicture) menggambar bitmap ke halaman saat ini, dan PageNumber memberi tahu komponen halaman mana yang dituju panggilan gambar berikutnya

Satu detail yang menjebak orang adalah sistem koordinat. PDF user space menempatkan origin di sudut kiri-bawah halaman, dengan Y meningkat ke atas, kebalikan dari koordinat layar yang dijangkau pengembang Delphi secara refleks. X, Y yang Anda berikan ke AddImage adalah sudut kiri-bawah persegi panjang gambar, dan Width, Height adalah ukuran penempatan dalam poin, bukan ukuran piksel file sumber. Dapatkan itu terbalik dan scan Anda mendarat di luar halaman atau terbalik relatif terhadap di mana Anda mengharapkannya

Membuat dokumen dan satu halaman per scan

Mulai dengan dokumen kosong. CreateDocument mengalokasikan PDF segar dan meninggalkan komponen aktif, jadi tidak ada langkah buka terpisah. Dari sana Anda berjalan melalui daftar file yang dipindai, dan untuk setiap satu Anda menambahkan halaman, menjadikannya saat ini, dan menempatkan gambar. Dimensi halaman di sini adalah A4 dalam poin (595 × 842 potret), ukuran lembar standar untuk korespondensi yang diarsipkan

procedure TArchiveForm.ScansToPdf(const Files: TStrings; const OutputPath: string);
const
  PageW = 595.0;   // lebar A4 dalam poin
  PageH = 842.0;   // tinggi A4 dalam poin
  Margin = 36.0;   // batas setengah-inci di sekitar setiap scan
var
  I: Integer;
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.CreateDocument;                       // baru, kosong, sudah aktif
    for I := 0 to Files.Count - 1 do
    begin
      Pdf.AddPage(I + 1, PageW, PageH);       // indeks halaman berbasis 1
      Pdf.PageNumber := I + 1;                // jadikan halaman baru sebagai halaman saat ini
      PlaceScan(Pdf, Files[I], PageW, PageH, Margin);
    end;
    Pdf.SaveAs(OutputPath);
  finally
    Pdf.Free;
  end;
end;

Setiap iterasi membuat halaman dan segera menyetel PageNumber ke sana. Baris kedua itu penting: AddPage menyisipkan halaman tetapi metode gambar bertindak pada halaman mana pun yang saat ini, jadi menyetel PageNumber adalah yang mengarahkan AddImage ke halaman yang baru Anda buat. Lewati dan gambar Anda menumpuk ke halaman mana pun yang kebetulan dimuat sebelumnya

Satu asumsi bersembunyi dalam loop itu: urutan Files. Pemindai menamai halaman 0001.jpg sampai 0100.jpg, tetapi enumerasi direktori tidak selalu mengembalikannya terurut, dan saat Anda menekan page9.jpg di sebelah page10.jpg pengurutan string polos menempatkan halaman 10 sebelum halaman 9. Urutkan daftar secara eksplisit sebelum loop, dan lebih suka nama zero-padded saat pemindaian agar urutan leksikal cocok dengan urutan halaman. Urutan halaman adalah satu hal yang segera diperhatikan peninjau, dan itu kesalahan termurah untuk dicegah

Menempatkan scan dan menjaga rasio aspeknya

Scan jarang berbentuk sama dengan halaman. Jika Anda meregangkannya untuk mengisi lembar Anda mendistorsi teks; jika Anda menempatkannya pada ukuran piksel penuh meluap. Perbaikannya adalah menskalakan dengan rasio yang lebih kecil dari dua rasio, fit-lebar atau fit-tinggi, dan memusatkan apa yang tersisa. Karena origin duduk di kiri-bawah, pemusatan berarti membagi ruang sisa secara merata dan menambahkannya ke X dan Y

Diagram halaman A4 menunjukkan bagaimana AddImage PDFium Component menempatkan hasil pemindaian yang diskalakan di dalam margin menggunakan kode Delphi dan titik asal kiri-bawah
AddImage mengambil sudut kiri-bawah persegi penempatan, sehingga fit dan center dihitung dalam point halaman dari origin
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, dll. melalui unit grafis VCL

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

    // Muat di dalam margin tanpa mendistorsi scan.
    Scale := Min(AvailW / Pic.Width, AvailH / Pic.Height);
    DrawW := Pic.Width * Scale;
    DrawH := Pic.Height * Scale;

    // Pusatkan: sisa ruang dibagi merata. Y diukur dari bawah halaman.
    X := (PageW - DrawW) / 2;
    Y := (PageH - DrawH) / 2;

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

Ini memuat file sekali untuk membaca dimensi pikselnya, menghitung satu skala seragam, dan meneruskan persegi panjang penempatan ke AddImage. AddImage menerima jalur file secara langsung dan merutekannya melalui pipeline gambar yang sama seperti AddPicture, jadi format apa pun yang dikenali unit grafis VCL bekerja tanpa penanganan khusus. Jika Anda sudah memiliki gambar terdekode dalam TPicture dari panel pratinjau, panggil AddPicture(Pic, X, Y, DrawW, DrawH) dengan persegi panjang yang sama dan lewati pembacaan file kedua

Melewati decode untuk scan JPEG

Pemindai hampir selalu memancarkan JPEG. Memuat JPEG ke TPicture mendekodekannya ke bitmap, lalu PDFium mengenkodenya kembali saat simpan, dua perjalanan bolak-balik lossy yang tidak Anda butuhkan. AddJpegImage menanam byte terkompresi asli langsung ke halaman dari stream, yang lebih cepat dan lebih bersih secara visual untuk batch volume tinggi

AddJpegImage menyematkan byte JPEG asli di halaman PDFium Component sementara AddImage mendekode dan SaveAs mengenkode ulang piksel di Delphi
AddJpegImage menyematkan byte terkompresi milik scanner apa adanya, menghindari lintasan dekode dan re-encode yang dilakukan AddImage dan SaveAs
var
  Stream: TFileStream;
begin
  // ... setelah AddPage + PageNumber untuk halaman saat ini ...
  Stream := TFileStream.Create(FileName, fmOpenRead);
  try
    // Menanam byte JPEG apa adanya; tanpa siklus decode/re-encode.
    Pdf.AddJpegImage(Stream, X, Y, DrawW, DrawH);
  finally
    Stream.Free;
  end;
end;

Anda tetap menghitung X, Y, DrawW, dan DrawH dengan cara yang sama, karena Anda membutuhkan dimensi piksel untuk menskalakan. Baca itu dari file atau parse header cepat, lalu serahkan stream mentah ke AddJpegImage. Untuk scan PNG atau TIFF jalur AddImage adalah yang benar; cadangkan pintasan JPEG untuk format yang benar-benar berlaku

Melabeli setiap halaman

Scan yang diarsipkan lebih mudah diaudit ketika setiap halaman membawa nama file sumbernya. AddText menggambar string pada koordinat user-space, jadi keterangan duduk tepat di bawah gambar. Ingatlah sumbu Y yang terbalik: untuk meletakkan label di bawah scan, Anda mengurangi dari tepi bawah gambar alih-alih menambahkannya

// Keterangan di bawah scan: Y menurun menuju bawah halaman.
Pdf.AddText('File: ' + ExtractFileName(FileName), 'Helvetica', 9,
  X, Y - 14, clGray);

Satu poin terakhir tentang penyimpanan. SaveAs adalah fungsi yang mengembalikan Boolean, jadi dalam kode produksi periksa hasilnya alih-alih mengasumsikan penulisan berhasil; disk penuh atau jalur keluaran terkunci gagal diam-diam jika tidak. Setelah loop selesai dan file ditulis, Anda memiliki persis yang dibutuhkan arsip: satu PDF terurut per berkas kasus, halaman diskalakan agar pas, siap dibaca di penampil mana pun

Bangunan pembangun yang sama mencakup pekerjaan terkait. Tukar aturan penentuan ukuran per-halaman dan Anda mendapatkan buku foto dengan satu gambar per lembar; pertahankan loop tetapi baca dari sumber TIFF multi-halaman dan Anda memiliki pengonversi arsip-faks. Jika Anda menginginkan gambaran yang lebih luas tentang membangun PDF secara terprogram, lihat membuat dokumen PDF dari nol dengan PDFium Component; untuk merender hasil kembali ke layar nanti, lihat mengonversi halaman PDF ke gambar JPEG dengan PDFium Component

PDFium Component dari loslab.com membundel API pembuatan-dokumen, rendering, dan teks yang digunakan di sepanjang seri ini