PDF menyimpan gambar sebagai objek kelas satu di dalam aliran kontennya. Ketika halaman merujuk ke foto, hasil pemindaian, atau diagram, data piksel berada dalam kamus XObject bersama geometri halaman. PDFium Component mengeksposnya melalui dua properti pada TPdf: BitmapCount, yang mengembalikan jumlah bitmap tertanam pada halaman saat ini, dan Bitmap[Index], yang mendekode salah satunya menjadi TBitmap yang menjadi milik Anda dan harus dibebaskan. Itulah seluruh model ekstraksinya. Perulangannya hanya empat baris; yang memerlukan pertimbangan adalah infrastruktur di sekelilingnya
Membuka dokumen
Hal pertama yang perlu diketahui tentang TPdf ialah bahwa Active := True tidak pernah memunculkan pengecualian. Kegagalan memuat, kata sandi yang salah, atau file yang rusak semuanya ditangani secara internal dan komponen hanya tetap tidak aktif. Anda harus memeriksa sendiri statusnya setelah penugasan, atau Anda akan memasuki perulangan halaman dengan PageCount yang mengembalikan nol lalu bertanya-tanya mengapa tidak ada yang diekstrak
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');
// lanjutkan ke ekstraksi
finally
Pdf.Free;
end;
end;
File yang dilindungi kata sandi mengikuti pola yang sama: tetapkan Pdf.Password sebelum mengatur Active := True. Jika kata sandinya salah, Active tetap False dan tidak ada pengecualian untuk ditangkap. Dalam alat batch yang memproses ratusan file, perilaku diam ini justru berguna: Anda mengumpulkan kegagalan dalam sebuah daftar alih-alih membongkar tumpukan pemanggilan untuk setiap file
Mengiterasi halaman dan mengambil bitmap
BitmapCount berlaku per halaman, sehingga Anda perlu mengatur Pdf.PageNumber sebelum membacanya. Nomor halaman berbasis 1; nilai bawaan 0 berarti tidak ada halaman yang dimuat. Properti Bitmap[Index] berbasis 0 dan mengembalikan TBitmap yang menjadi milik pemanggil. Anda harus membebaskannya. Jika pembebasan diabaikan dalam perulangan panjang untuk dokumen besar, penggunaan memori akan cepat naik karena setiap bitmap dapat memuat beberapa megabita data piksel mentah sebelum kompresi
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;
Pemeriksaan Assigned penting. Sejumlah kecil pembuat PDF menulis XObject gambar dengan dimensi piksel nol atau data yang rusak dengan cara lain; dalam kasus tersebut komponen mengembalikan nil, bukan bitmap kosong. Menganggap hasil nil sebagai galat lalu menghentikan ekstraksi bukanlah tindakan yang tepat: lewati gambar itu, catat halaman dan indeksnya bila Anda memerlukan jejak audit, lalu lanjutkan. Bagian lain dari halaman tersebut mungkin masih menghasilkan gambar yang valid
Perhatikan bahwa perulangan luar menetapkan Pdf.PageNumber pada setiap iterasi. Penugasan itulah yang memuat halaman ke status internal komponen dan membuat BitmapCount bermakna. Jika dilewati, Anda akan membaca jumlah gambar halaman yang sama berulang kali. Pola ini mungkin terasa berlebihan saat ditulis, tetapi demikianlah rancangan API: halaman adalah kursor, bukan koleksi
Memilih format keluaran
BMP bersifat lossless dan selalu tersedia tanpa unit tambahan, sehingga menjadi pilihan bawaan yang baik ketika Anda belum mengetahui isi gambar. Ketika ukuran file penting, format piksel dari TBitmap yang dikembalikan menunjukkan codec yang sesuai. Bitmap 32-bit membawa kanal alfa; PNG mempertahankannya tanpa kehilangan kualitas. Gambar 24-bit besar dengan rona yang berkelanjutan cocok untuk JPEG. Gambar yang lebih kecil atau yang dibuat dengan palet terbatas umumnya lebih baik dibiarkan sebagai BMP daripada diproses melalui JPEG, yang menambahkan artefak blok pada pengaturan kualitas rendah dan hanya sedikit menghemat ukuran pada pengaturan tinggi
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;
Dalam praktiknya, pemilihan format ditentukan oleh Bmp.PixelFormat dan dimensi. Jika PixelFormat = pf32bit, Anda membutuhkan format yang membawa alfa; PNG adalah pilihan yang jelas, meski memerlukan unit PNGImage pada versi Delphi yang lebih lama. Untuk gambar 24-bit yang lebarnya lebih dari sekitar 300 piksel, JPEG pada kualitas 85 memberikan pengurangan ukuran tiga banding satu dibandingkan BMP tanpa kehilangan yang terlihat pada sebagian besar konten fotografis. Di bawah ambang itu, ukuran BMP sebanding dan tidak memerlukan keputusan kualitas apa pun
Apa yang dihitung dan tidak dihitung oleh BitmapCount
PDF membedakan XObject gambar dari grafik vektor yang digambar dengan operator jalur. Halaman yang tampak kompleks secara visual dapat mengembalikan BitmapCount bernilai nol apabila setiap elemennya berupa vektor. Halaman hasil pemindaian hampir selalu mengembalikan tepat satu: pemindai menulis seluruh hasil pindai sebagai satu XObject gambar satu halaman penuh pada resolusi yang dipilih pemindai. Halaman yang memadukan teks tersusun huruf dengan foto tertanam mengembalikan satu entri untuk setiap foto. Garis dekoratif, latar berarsir, dan batas tabel biasanya sama sekali tidak muncul dalam jumlah bitmap
Jumlah ini juga tidak mencakup gambar sebaris, sebuah konstruksi PDF yang jarang digunakan ketika data gambar disematkan langsung dalam aliran konten halaman alih-alih sebagai XObject bernama. Gambar tersebut berada di luar cakupan API ini; dalam dokumen nyata, penggunaannya cukup jarang sehingga sebagian besar alat ekstraksi memang tidak menanganinya
Satu detail yang perlu diingat: BitmapCount yang dibaca berlaku untuk halaman saat ini berdasarkan penugasan PageNumber terakhir. Jika kode Anda bercabang atau memanggil fungsi yang mengubah PageNumber antara penghitungan dan pengambilan, Anda dapat membaca gambar lebih sedikit daripada ruang yang telah dialokasikan, atau mengakses indeks melewati batas. Pertahankan pembacaan jumlah dan perulangan Bitmap[] pada halaman yang sama tanpa menyentuh PageNumber di antaranya
Menggunakan TPdfView dalam aplikasi formulir
Komponen TPdfView mengekspos properti BitmapCount dan Bitmap[] yang sama, tetapi halaman yang dibacanya adalah halaman yang sedang ditampilkan oleh tampilan, bukan TPdf.PageNumber. Kedua penunjuk halaman tersebut berdiri sendiri; mengatur satu tidak memindahkan yang lain. Dalam aplikasi formulir VCL dengan penampil langsung, Anda dapat memanggil Pdf.PageNumber := N untuk menjalankan ekstraksi melalui TPdf sementara penampil tetap berada pada halaman terakhir yang digulir pengguna. Pemisahan ini disengaja dan menjaga status tampilan penampil tetap bersih saat ekstraksi latar belakang berjalan
Memori dan performa dalam pekerjaan batch
Dalam arsip besar, anggaran memori adalah hal utama yang perlu diperhatikan. Setiap pemanggilan Bitmap[] mengalokasikan TBitmap baru di heap, dan pada halaman hasil pindai 300 DPI ukurannya dapat dengan mudah mencapai 25 MB data piksel mentah sebelum penyandian. Jika halaman diproses dalam perulangan ketat tanpa pembebasan antariterasi, kumpulan kerja bertumbuh secara linear dengan jumlah gambar. Pola yang benar selalu: ambil satu bitmap, lakukan yang diperlukan, bebaskan, lalu ambil bitmap berikutnya. Jika Anda perlu menyimpan referensi ke beberapa bitmap sekaligus untuk langkah perbandingan, hitung dahulu dengan BitmapCount dan alokasikan kontainer Anda sesuai kebutuhan, kemudian bebaskan masing-masing segera setelah selesai alih-alih menundanya hingga pembersihan di akhir dokumen. Pada dokumen dengan 500 halaman hasil pindai, perbedaan itu dapat berarti puncak RSS 25 MB alih-alih 12 GB
Properti BitmapCount dan Bitmap[] yang ditampilkan di sini merupakan bagian dari PDFium Component untuk Delphi dan C++Builder