HotPDF merender halaman PDF yang dimuat ke dalam TBitmap Delphi melalui panggilan tunggal: RenderLoadedPageToBitmap(PageIndex, DPI). Fungsi tersebut menafsirkan aliran konten halaman dan mengembalikan bitmap RGB 24-bit milik pemanggil pada resolusi yang Anda pilih, yang merupakan hal yang dibutuhkan oleh strip gambar mini (thumbnail strip), pratinjau cetak, atau alur kerja ekspor PDF-ke-gambar. Artikel ini membahas API tersebut, lalu membahas bagian yang membedakan perender yang berguna dari yang sekadar mainan: menggabar teks dari program font tersemat itu sendiri daripada dari font sistem yang serupa
Mengapa merender halaman PDF lebih sulit daripada menggambar gambar?
Halaman PDF bukanlah sebuah gambar. Ini adalah sebuah program: aliran operator yang membangun jalur, memilih font, menyetel warna, dan menempatkan glif, yang dieksekusi terhadap model grafis yang ditentukan dalam ISO 32000-1 §8. Tidak ada bagian dalam file yang menyatakan seperti apa piksel itu terlihat. Untuk menghasilkan bitmap, Anda harus menjalankan program tersebut — mempertahankan matriks transformasi saat ini, tumpukan status grafis (graphics-state stack) untuk q/Q, jalur kliping (clipping path), ruang warna fill dan stroke — dan meraster hasilnya. Itulah sebabnya "cukup tampilkan halaman 3 sebagai gambar" adalah penerjemah aliran konten, bukan konversi format file
Perender HotPDF, yang diperpermanen di v2.253.0, dibangun sebagai enam unit terpisah yang mencerminkan model tersebut: inti matriks afin untuk aljabar transformasi PDF [a b c d e f], tumpukan status grafis (graphics-state stack), pemecah ruang warna (DeviceRGB, DeviceGray, DeviceCMYK, Indexed), pembangun jalur yang menjembatani operator jalur PDF ke GDI, lapisan metrik font yang membaca larik /Widths untuk pergeseran yang benar, dan interpreter yang mengirimkan operator dan menggerakkan lima unit lainnya. Image XObjects melalui tumpukan dekode yang sama yang digunakan pustaka untuk ekstraksi, sehingga setiap filter gambar yang dapat didekodekan oleh HotPDF untuk ekstraksi — termasuk gambar JPEG 2000 terkompresi JPXDecode — juga muncul dalam keluaran yang dirender
Merender halaman yang dimuat ke TBitmap
RenderLoadedPageToBitmap menerima indeks halaman berbasis nol dan nilai DPI, di mana 72 DPI memetakan satu unit ruang pengguna PDF ke satu piksel. Ini mengembalikan nil jika gagal (indeks di luar jangkauan, sumber daya hilang) daripada memicu pengecualian, sehingga penampil dapat melewatkan halaman yang rusak dan terus berjalan. Pemanggil memiliki bitmap yang dikembalikan dan harus membebaskannya (free)
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report.pdf') > 0 then
begin
Bmp := Pdf.RenderLoadedPageToBitmap(0, 144); // halaman 1 pada 144 DPI
if Bmp <> nil then
try
Image1.Picture.Assign(Bmp);
finally
Bmp.Free; // pemanggil memiliki bitmap
end;
end;
finally
Pdf.Free;
end;
end;
Argumen DPI melakukan pekerjaan penskalaan untuk setiap skenario umum. Strip gambar mini merender pada 36 atau 48 DPI dan mendapatkan bitmap yang kecil dan cepat; pratinjau di layar pada 96 atau 144 DPI cocok dengan kerapatan tampilan tipikal; jalur ekspor pada 300 DPI menghasilkan gambar berkualitas cetak. Rotasi halaman dari entri /Rotate dan pembalikan asal /MediaBox (PDF menempatkan titik asal di kiri bawah, GDI di kiri atas) ditangani di dalam matriks halaman-ke-perangkat, sehingga halaman US Letter pada 72 DPI kembali tepat 612×792 piksel dengan arah yang benar
Mengapa gambar mini PDF yang dirender menunjukkan glif yang salah?
Glif yang salah atau mendekati dalam keluaran PDF yang dirender hampir selalu berarti perender mengganti font sistem alih-alih menggunakan font yang disematkan dalam file. Perender HotPDF pertama melakukan hal tersebut: ia menghapus awalan subset dari /BaseFont (mengubah ABCDEF+Arial menjadi Arial), meminta font sistem dengan nama tersebut ke GDI, dan menggambar teks dengannya. Untuk dokumen yang menggunakan Arial atau Times New Roman with pengodean standar, hasilnya terlihat mirip. Namun ini adalah pendekatan kasar, dan dapat rusak dengan cara yang jelas
Font tersemat subset adalah kasus terburuk. Font subset mungkin hanya membawa empat puluh glif yang sebenarnya digunakan oleh dokumen, dengan kode karakter yang ditetapkan dalam urutan privat untuk file tersebut — kode 1 mungkin berupa "T", kode 2 "h", dan seterusnya. Font sistem tidak tahu apa-apa tentang penetapan privat tersebut, sehingga teks dapat menghilang atau keluar sebagai karakter yang salah sekali. Pengodean khusus, font simbol, font kode batang, dan jenis huruf apa pun yang tidak terpasang di mesin perender akan gagal dengan cara yang sama. Perender yang berhenti pada penggantian font sistem menghasilkan gambar mini yang dapat dikenali sebagai halaman tersebut — sampai halaman tersebut menggunakan font yang membuat penyematan diperlukan sejak awal
Rendering glif tersemat: menggambar dari program font itu sendiri
HotPDF menutup celah tersebut di lima rilis (v2.268.0 hingga v2.272.0) dengan mengurai program font tersemat dan memutar ulang garis luar glif mereka sebagai jalur vektor GDI yang terisi. Teks dalam halaman yang dirender sekarang berasal dari data garis luar yang sama yang digunakan oleh penampil yang sesuai, yang berarti font subset, pengodean khusus, dan jenis huruf yang tidak terpasang akan dirender dengan bentuk persisnya. Cakupan tersebut dibangun berdasarkan jenis font:
Untuk font Type0/CIDFontType2 dengan program TrueType tersemat (FontFile2), perender mengurai tabel glyf dan loca secara langsung: kontur kuadratik dikonversi ke Bézier kubik yang dipahami GDI, titik on-curve tersirat di antara titik off-curve berurutan direkonstruksi, dan glif komposit diputar ulang secara rekursif. Baik tata letak Identity maupun aliran eksplisit CIDToGIDMap didukung, dan pergeseran CID menghormati entri lebar /W dan /DW width entries, so dua-bita Identity-H text steps correctly
Program CFF (FontFile3, baik CIDFontType0C, Type1C, atau pembungkus OpenType) mendapatkan interpreter charstring Type 2 lengkap: garis, kurva, keluarga flex, masker petunjuk (hint masks), dan panggilan subrutin lokal/global dengan bias subrutin yang benar. Program CFF berkunci CID memetakan kode karakter melalui kumpulan karakter (charset) font, yang penting untuk font subset yang urutan glifnya berbeda dari urutan CID, dan pemilihan font-DICT per glif melalui FDArray/FDSelect dihormati. Font TrueType sederhana (non-CID) menyelesaikan kode satu bita melalui tabel cmap milik font tersemat itu sendiri dengan rantai subtabel yang kuat — format Unicode 4 dan 12 terlebih dahulu, lalu subtabel simbol dengan cermin penggunaan pribadi F000, lalu format Macintosh usang — sedangkan font Type1 sederhana diselesaikan melalui pengodean bawaan program CFF
Dua penyempurnaan melengkapi gambaran ini. Pertama, kamus /Encoding font sederhana diselesaikan sesuai prioritas yang ditentukan ISO 32000-1 §9.6.6: larik /Differences mengesampingkan pengodean dasar, yang mengesampingkan peta milik program font itu sendiri — jalur yang bergantung pada toolchain turunan TeX dan PostScript, dengan nama glif diselesaikan melalui Adobe Glyph List, charset CFF, atau cmap TrueType. Kedua, font Type3, yang glifnya sendiri merupakan aliran konten kecil, diputar ulang melalui perender dengan matriks font, ukuran font, dan matriks teks yang disusun; ruang glif /Widths ditafsirkan melalui /FontMatrix seperti yang disyaratkan ISO 32000-1 §9.6.5, dan prosedur glif yang mendeklarasikan kotak pembatas d1 diklip ke kotak tersebut, sehingga glif kode batang yang cacat tidak dapat melukis di luar selnya. Ketika kode tidak dapat dipetakan — program yang rusak, karakter yang tidak terpetakan — perender beralih ke penggambaran font sistem untuk glif tersebut alih-alih membuang aliran teks (text run)
Bagaimana cara membuat perenderan berulang menjadi cepat?
Cache kedua bekerja di bawah cache halaman: Image XObjects yang didekodekan disimpan di penyimpanan dengan anggaran bita yang dibatasi oleh ImageCacheMaxBytes (bawaan 32 MB) dengan pengosongan yang paling jarang digunakan (least-recently-used eviction). Gambar logo atau kop surat yang diulang di setiap halaman didekodekan sekali per pemuatan dokumen alih-alih sekali per operator Do, yang secara kasar memotong setengah waktu rendering untuk halaman gambar bersama dan mempercepat ekspor TIFF multi-halaman dengan ukuran yang sama. InvalidateRenderedPageCache juga membersihkan cache ini
// Strip gambar mini: lintasan pertama merender, menggulir kembali mengenai cache
for I := 0 to ThumbCount - 1 do
begin
Bmp := Pdf.RenderLoadedPageToBitmapCached(I, 48);
if Bmp <> nil then
try
ThumbList.AddThumbnail(I, Bmp);
finally
Bmp.Free;
end;
end;
// Setelah mengedit halaman yang dimuat di tempat:
Pdf.InvalidateRenderedPageCache; // render berikutnya mencerminkan perubahan
Bersikaplah jujur tentang tagihan memori sebelum meningkatkan kapasitas. Halaman US Letter pada 300 DPI adalah 2550×3300 piksel, sekitar 25 MB sebagai bitmap 24-bit, sehingga delapan halaman yang disimpan dalam cache pada resolusi ekspor menampung sekitar 200 MB. Pada DPI gambar mini, delapan entri yang sama berbiaya jauh di bawah satu megabita. Sesuaikan ukuran RenderCacheCapacity untuk DPI tempat Anda benar-benar menyimpan cache, dan panggil InvalidateRenderedPageCache setelah pengeditan di tempat — cache hanya dikunci berdasarkan halaman dan DPI, dan tidak dapat melihat bahwa konten yang mendasarinya berubah. Memuat dokumen baru akan membersihkannya secara otomatis
Cache kedua bekerja di bawah cache halaman: Image XObjects yang didekodekan disimpan di penyimpanan dengan anggaran bita yang dibatasi oleh ImageCacheMaxBytes (bawaan 32 MB) dengan pengosongan yang paling jarang digunakan (least-recently-used eviction). Gambar logo atau kop surat yang diulang di setiap halaman didekodekan sekali per pemuatan dokumen alih-alih sekali per operator Do, yang secara kasar memotong setengah waktu rendering untuk halaman gambar bersama dan mempercepat ekspor TIFF multi-halaman dengan ukuran yang sama. InvalidateRenderedPageCache juga membersihkan cache ini
Apa yang masih dirender secara perkiraan
Perender menargetkan subset dokumen-PDF umum, dan patut diketahui di mana batas-batasnya. Ruang warna berbasis CalRGB, Lab, dan ICC diperkirakan alih-alih dikelola warna — ruang warna perangkat, palet Terindeks, dan pencarian warna fungsi Tipe 0 yang disampel ditangani, tetapi file produksi cetak yang mengandalkan maksud rendering (rendering intents) ICC tidak akan tepat secara kolorimetri. Pola bayangan (sh) dan mode campuran (blend modes) di luar alfa sederhana juga berada di luar cakupan, dan rekursi Form XObject dibatasi kedalamannya sebagai pelindung siklus. Untuk faktur, laporan, kontrak, dan formulir — halaman yang terbuat dari teks, jalur, dan gambar — keluarannya setia; untuk bukti desain yang penuh dengan gradien dan grup transparansi, perlakukan bitmap sebagai pratinjau, bukan sebagai bukti cetak (proof)
Bacaan praktisnya: jika alur kerja Anda menghasilkan dokumen dengan HotPDF atau mengonsumsi PDF bisnis biasa, RenderLoadedPageToBitmap melakukan perjalanan bolak-balik dengan bentuk glif tersemat yang persis, pergeseran CID yang benar, dan geometri halaman yang benar. Pendekatan kasar hanya berada di sudut-sudut model grafis yang jarang dikunjungi oleh dokumen bisnis
RenderLoadedPageToBitmap, varian cache-nya, dan alur perenderan glif tersemat yang dijelaskan di sini dikirimkan sebagai bagian dari HotPDF Component untuk Delphi dan C++Builder — pustaka VCL asli tanpa ketergantungan DLL eksternal, yang mencakup pembuatan, pengeditan, ekstraksi teks, dan perenderan halaman PDF dalam satu paket