Untuk mengurangi ukuran file PDF di Delphi, losLab PDF Library menyediakan tiga API yang menyerang tiga sumber pembengkakan terbesar: SubsetEmbeddedFonts menulis ulang setiap program font TrueType yang disematkan menjadi hanya glif yang sebenarnya dirender oleh dokumen, DownsampleImages melakukan resample gambar raster yang melebihi DPI target, dan NormalizeLZWStreams mengganti kompresi LZWDecode usang dengan FlateDecode. Masing-masing mengembalikan jumlah objek yang diubahnya, sehingga nilai nol memberi tahu Anda bahwa lintasan tersebut adalah no-op alih-alih kegagalan senyap
PDF yang digabungkan atau dihasilkan secara terprogram biasanya berukuran terlalu besar karena salah satu dari tiga alasan: font yang disematkan sepenuhnya, gambar yang di-sample jauh di atas resolusi tampilannya, dan aliran data yang masih dikompresi dengan filter LZW usang. ISO 32000-1 §9.9 memungkinkan produsen menyematkan program font lengkap, dan sebagian besar produsen melakukan hal tersebut karena ini adalah standar bawaan yang aman. Program Arial FontFile2 lengkap berukuran ratusan kilobita; sematkan di selusin file sumber, gabungkan, dan Anda membawa selusin salinan garis luar glif untuk karakter yang tidak diketik siapa pun. Penggabungan itu sendiri tidak menciptakan limbah tersebut, melainkan hanya memusatkannya ke dalam satu file di mana totalnya akhirnya terlihat
Gambar adalah pelanggar kedua. Pindaian selebar 4800 piksel yang ditempatkan ke dalam bingkai seperempat halaman mengirimkan sekitar 40 kali lebih banyak data piksel daripada yang dapat digunakan oleh alur cetak 300 DPI. Yang ketiga lebih tenang: aliran data yang difilter dengan LZWDecode. ISO 32000-1 §7.4.4 menetapkan LZWDecode dan FlateDecode, dan mencatat bahwa Flate biasanya mengompresi setidaknya sama baiknya; dalam praktiknya, keluaran Flate secara konsisten lebih kecil pada data yang sama, dan LZW sebagian besar bertahan di file yang melewati perkakas era 1990-an di beberapa titik dalam sejarahnya. Sisa artikel ini membahas tiga lintasan losLab PDF Library yang memperbaiki setiap masalah, lalu menggabungkannya menjadi satu alur kerja
Subsetting font dengan SubsetEmbeddedFonts
SubsetEmbeddedFonts memperkecil setiap font TrueType tersemat di dokumen yang dimuat menjadi hanya karakter yang sebenarnya digunakan oleh dokumen tersebut, dan ini tidak memerlukan argumen karena daftar yang dipertahankan diambil dari aliran konten itu sendiri. Secara internal, lintasan menelusuri aliran konten setiap halaman dengan GetTextRuns, mengumpulkan kode karakter yang direferensikan di bawah setiap sumber daya font, membangun daftar pertahankan, dan menyerahkan program font asli ke mesin Windows FontSub (CreateFontPackage) untuk menghasilkan subset. Program yang ditulis ulang menggantikan aliran FontFile2 di tempatnya, dan nama BaseFont mendapatkan tag LOSABC+, konvensi enam huruf besar ditambah tanda plus yang didefinisikan ISO 32000-1 §9.6.4 untuk font subset. Awalan tersebut juga yang membuat panggilan ini idempoten: jalankan lintasan dua kali dan font yang sudah disubset akan dikenali dan dilewati, sehingga aman untuk dimasukkan ke dalam pekerjaan batch yang mungkin mengunjungi kembali file tersebut
var
Lib: TPDFlib;
Fonts: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('merged-report.pdf', '') = 1 then
begin
Fonts := Lib.SubsetEmbeddedFonts;
// Fonts = jumlah program FontFile2 yang ditulis ulang;
// 0 berarti tidak ada yang disematkan, atau semuanya sudah disubset
Lib.SaveToFile('merged-report-subset.pdf');
end;
finally
Lib.Free;
end;
end;
Dua detail implementasi patut diketahui karena menjelaskan batasan API. Pertama, lintasan menargetkan FontFile2, so mencakup program TrueType yang disematkan; font yang disematkan sebagai Type 1 atau CFF kosong dibiarkan tidak tersentuh daripada berisiko. Kedua, ini bergantung pada FontSub, yang membuat SubsetEmbeddedFonts hanya untuk Windows. Poin yang lebih halus dari implementasi: kelayakan font diputuskan dengan benar-benar menyelesaikan rantai referensi FontDescriptor → FontFile2, bukan dengan mempercayai heuristik bendera tersemat, karena font dalam dokumen yang dimuat tidak pernah melalui pembukuan sisi pembuatan yang menyetel bendera tersebut. Jika aliran yang diselesaikan ada, font tersebut adalah kandidat; jika tidak, ia akan dilewati tanpa kesalahan
Pertukaran yang jujur: font subset hanya berisi glif yang ada pada saat subsetting dilakukan. Jika perkakas hilir, atau kode Anda sendiri, kemudian menambahkan teks dalam font yang sama, karakter apa pun di luar subset tidak akan memiliki garis luar dan akan dirender sebagai glif yang hilang. Lakukan subset sebagai langkah perubahan konten terakhir, jangan pernah sebelum tahap pengeditan. Kewaspadaan yang sama berlaku jika Anda berencana menarik kembali font tersebut nanti untuk digunakan kembali; artikel tentang mengekstrak teks, gambar, dan font dengan PDFlibPas membahas apa yang dapat dan tidak dapat diberikan oleh program subset yang diekstrak kepada Anda
Bagaimana DownsampleImages memutuskan gambar mana yang akan diperkecil?
DownsampleImages(MaxDPI, Quality, Filter) hanya melakukan resample pada gambar yang dapat dipastikan terlalu besar (oversampled), menggunakan perkiraan DPI yang sengaja dibuat konservatif. Gambar XObject PDF menyimpan dimensi piksel tetapi tidak menyimpan resolusi fisik yang tepercaya, dan tag DPI apa pun dari gambar sumber jarang sekali bertahan dalam siklus muat-edit-simpan. Jadi lintasan memperkirakan SrcDPI = PixelWidth / 8.5, yang sebenarnya menanyakan: jika gambar ini membentang sepanjang lebar halaman Letter, berapakah resolusinya? Hanya gambar yang perkiraannya melebihi MaxDPI yang akan disentuh. Bias ini disengaja: gambar yang ditempatkan kecil di halaman memiliki DPI sebenarnya yang lebih tinggi daripada perkiraannya, sehingga lintasan cenderung kurang terpicu (under-triggers) daripada menurunkan kualitas aset kualitas cetak yang tidak dapat diukurnya
Quality dari 1 hingga 100 memilih kualitas pengodean ulang JPEG, sedangkan 0 menjaga keluaran tetap sebagai Flate gaya PNG lossless; Filter memilih kernel resampling, 0 untuk box average dan 1 untuk bilinear. Untuk dokumen kantor yang dipindai, DownsampleImages(150, 75, 1) adalah titik awal yang masuk akal; untuk apa pun yang mungkin dicetak ulang, tingkatkan MaxDPI ke 300 atau lewati lintasan ini sepenuhnya. Downsampling adalah satu-satunya langkah berbayar (lossy) dari ketiganya, jadi ini harus ditempatkan di balik pengaturan yang dapat dinonaktifkan oleh pengguna Anda
Mengonversi aliran LZW usang dengan NormalizeLZWStreams
NormalizeLZWStreams adalah keuntungan gratis: ia mendekompresi tanpa menghilangkan data (losslessly) setiap aliran LZWDecode stream dan mengompresinya kembali dengan FlateDecode, di tempat, mengembalikan jumlah aliran yang dikonversi. Ini menangani entri tunggal /Filter /LZWDecode dan LZW yang muncul di dalam larik rantai filter, di mana hanya tautan LZW yang diganti dan sisa rantai dipertahankan. Parameter prediktor (Predictor, Columns, Colors, BitsPerComponent) dibaca dari DecodeParms aliran dan diteruskan ke dekompresor, so data gambar yang dikodekan prediktor melakukan perjalanan bolak-balik dengan benar. Karena kedua filter adalah codec yang tepat bit-nya, bita yang didekodekan adalah identik sebelum dan sesudah; hanya kompresi wadah yang berubah, itulah sebabnya lintasan ini aman dijalankan tanpa syarat pada setiap file
Pada dokumen tanpa aliran LZW, panggilan tersebut cukup mengembalikan nilai 0 dan tidak menyentuh apa pun, yang secara eksplisit diuji oleh rangkaian regresi pustaka: file Flate saja yang baru dibuat harus melaporkan nol konversi. Jaminan no-op itu penting saat lintasan berada di alur kerja yang memproses ribuan file heterogen, beberapa dari tahun 2024 dan beberapa dari tahun 1998
Alur kerja optimalisasi ukuran lengkap di Delphi
Ketiga lintasan bergabung menjadi satu fungsi muat-optimalkan-simpan, dan urutannya tidak terlalu penting karena beroperasi pada jenis objek yang berbeda: font, gambar XObject, dan filter aliran. Menjalankan subsetting terlebih dahulu adalah pilihan yang rapi, karena merupakan lintasan dengan batasan urutan pengeditan
function OptimizePDF(const Src, Dst: string): Boolean;
var
Lib: TPDFlib;
Fonts, Images, Streams: Integer;
begin
Result := False;
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile(Src, '') <> 1 then
Exit;
Fonts := Lib.SubsetEmbeddedFonts; // TrueType FontFile2 -> subset
Images := Lib.DownsampleImages(150, 75, 1); // >150 DPI -> JPEG q75, bilinear
Streams := Lib.NormalizeLZWStreams; // LZWDecode -> FlateDecode
Result := Lib.SaveToFile(Dst) = 1;
// Catat Fonts/Images/Streams: tiga nol berarti file sudah ramping
finally
Lib.Free;
end;
end;
Verifikasi alur kerja seperti cara pustaka memverifikasi dirinya sendiri: perjalanan bolak-balik (round-trip). Pengujian regresi v3.130 membuat dokumen, menyimpannya, memuatnya kembali, menjalankan optimalisasi, menyimpan lagi, dan kemudian menegaskan tiga hal: keluaran lebih kecil, jumlah yang dikembalikan sesuai harapan, dan pemuatan ulang file yang dioptimalkan masih dapat diuraikan dan dirender. Mereproduksi perulangan buat-optimalkan-muat kembali terhadap sampel file produksi Anda sendiri, dan membandingkan teks yang diekstrak sebelum dan sesudah, adalah investasi satu jam yang menangkap kesalahan integrasi jauh sebelum pelanggan membuka faktur yang rusak
// Pemeriksaan bolak-balik: file yang dioptimalkan harus tetap dimuat dengan bersih
Lib := TPDFlib.Create;
try
Assert(Lib.LoadFromFile('merged-report-opt.pdf', '') = 1);
Assert(Lib.GetPageCount > 0);
finally
Lib.Free;
end;
Di mana alur kerja optimalisasi ini cocok dalam alur kerja penggabungan? Setelah penggabungan, bukan selama penggabungan. Menggabungkan terlebih dahulu dan mengoptimalkan hasil tunggal berarti setiap font yang disematkan disubset sekali terhadap gabungan semua karakter yang digunakan, bukan per file sumber. Jika throughput penggabungan adalah hambatan, PDFlibPas menawarkan jalur cepat tingkat bita yang menghindari penguraian objek penuh, yang dijelaskan dalam artikel tentang penggabungan PDF cepat dengan pergeseran referensi bita; dan untuk masukan yang terlalu besar untuk ditampung sepenuhnya di memori, penggabungan dan pemisahan akses langsung untuk PDF besar mencakup jalur pengaliran. Keduanya berpasangan secara alami dengan lintasan optimalisasi akhir pada keluaran yang digabungkan
Apa yang tidak akan dilakukan oleh ketiga lintasan tersebut
Trio optimalisasi losLab PDF Library sengaja mengecualikan apa pun yang mengubah semantik dokumen. SubsetEmbeddedFonts tidak menyatukan font duplikat di seluruh sumber yang digabungkan menjadi satu program, ia memperkecil masing-masing secara independen; deduping adalah transformasi yang berbeda dan lebih berisiko. DownsampleImages akan mengabaikan gambar yang perkiraan DPI konservatifnya tetap di bawah ambang batas bahkan ketika manusia dapat mengetahui bahwa ukurannya terlalu besar untuk bingkainya. And tidak ada lintasan yang menyentuh struktur dokumen, sehingga file yang membengkak oleh ribuan objek yatim (orphaned objects) memerlukan penyimpanan gaya penulisan ulang (rewrite-style) daripada lintasan tingkat aliran ini. Dalam batasan tersebut, kombinasi subsetting font, downsampling gambar, dan normalisasi LZW-ke-Flate menghilangkan tiga sumber klasik pembengkakan PDF dengan masing-masing satu panggilan API yang dapat diprediksi. Ketiga fungsi tersebut dikirimkan sebagai bagian dari losLab PDF Library untuk Delphi, C# dan VB.NET, bersama dengan API penggabungan, ekstraksi, dan perenderan yang dibahas di atas