PDF 1.5 memperkenalkan dua struktur penyimpanan yang tidak bisa diekspresikan oleh format file sebelumnya: object stream dan cross-reference stream. Object stream adalah satu kontainer terkompresi Flate, ditandai /Type /ObjStm, yang menampung banyak objek tidak langsung berukuran kecil yang dikemas berurutan alih-alih tersebar di seluruh badan file. Cross-reference stream adalah tabel pencarian file yang ditulis ulang sebagai binary terkompresi dengan field lebar variabel, menggantikan tabel ASCII lebar tetap yang menutup setiap PDF hingga versi 1.4. Keduanya berjalan beriringan. Begitu objek dilipat ke dalam sebuah stream, tabel teks lama tidak bisa lagi mengalamatinya, sehingga xref biner harus hadir bersamanya
Bandingkan dengan layout klasik dan biaya yang dihilangkannya jadi mudah terlihat. Dalam sebuah file PDF 1.4, setiap objek tidak langsung berada tanpa kompresi di belakang header obj-nya sendiri, dan tabel di bagian akhir menghabiskan tepat 20 byte ASCII per entry, kompresi dilarang. Sebuah dokumen dengan 200.000 objek membawa sekitar 4 MB data cross-reference sebelum satu glyph pun digambar, dengan seluruh badan dictionary tanpa kompresi menumpuk di atasnya. PDF 1.5 menyerang kedua angka itu sekaligus: dictionary dilipat ke dalam kontainer Flate, dan tabel 4 MB itu menyusut menjadi beberapa ratus kilobyte binary. ISO 32000-1 mendefinisikan kedua struktur ini dalam §7.5.7 dan §7.5.8
Di mana penghematan ini sebenarnya berdampak
Object stream hanya menyentuh objek non-stream, sehingga ia mengompresi struktur, bukan piksel. Konten halaman sudah terkompresi Flate bahkan sebelum 1.5, dan data gambar membawa codec-nya sendiri, itulah sebabnya sebuah brosur padat gambar hampir tidak bergerak sama sekali. File yang menyusut drastis adalah yang padat struktur: AcroForm dengan ribuan dictionary field, outline tree yang dalam, elemen struktur tagged-PDF. Objek-objek itu kecil, banyak jumlahnya, dan hampir identik satu sama lain, dan pengulangan itulah yang dieksploitasi Flate begitu semuanya berada dalam satu buffer tunggal alih-alih tersebar di seluruh badan file dengan header terselip di antaranya
Mudah untuk meremehkan berapa banyak bagian sebuah file lama yang sebenarnya adalah overhead. Sebuah arsip form yang telah menyerap edit bertahun-tahun bisa menghabiskan lebih dari setengah byte-nya untuk header dictionary, padding xref, dan revisi yang tidak akan pernah dilihat siapa pun. Dua fitur di sini mengembalikan dua yang pertama dari itu. Yang ketiga, revisi yang terakumulasi, hanya bisa diatasi lewat pemadatan (compaction), begitu file tidak lagi perlu mengingat riwayatnya sendiri
Di HotPDF, Anda mengaktifkan keduanya melalui sepasang property, dan bagaimana keduanya saling bergantung lebih penting daripada urutan Anda menulisnya:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'catalog-2026.pdf';
Pdf.UseXRefStream := True; // xref biner, prasyarat untuk ObjStm
Pdf.UseObjectStreams := True; // mengemas objek ke dalam /Type /ObjStm
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Compressed structure demo');
Pdf.EndDoc; // menghasilkan kontainer XRefStm + ObjStm
finally
Pdf.Free;
end;
end;
UseObjectStreams membutuhkan UseXRefStream diatur ke True. Sebuah objek terkompresi dijangkau melalui entry xref type-2, yang mencatat nomor object-stream ditambah sebuah indeks, dan baris teks 20-byte klasik tidak punya tempat untuk menyimpan pasangan itu. Jadi UseObjectStreams sendirian tidak melakukan apa pun yang terlihat; kedua flag, diatur sebelum BeginDoc, adalah konfigurasi yang benar-benar berfungsi. Atur setelah BeginDoc dan HotPDF sudah terlanjur berkomitmen pada layout lama
Mengapa keduanya default mati
HotPDF membiarkan kedua property tersebut False secara default, dan alasannya muncul dalam integrasi dengan kode lama di hilir. Sebuah reader yang hanya memahami PDF 1.4 tidak akan mengumumkan bahwa ia tidak bisa menangani objek terkompresi. Ia bertemu sebuah xref stream, tidak menemukan satu pun kata kunci trailer yang diharapkannya, dan melaporkan tabel cross-reference yang rusak atau langsung menolak membuka file itu. Jika output Anda mengalir ke gateway fax yang sudah tua, printer hardware yang menjalankan interpreter tertanam, atau sebuah parser yang ditulis seseorang berdasarkan spek 1.4 satu dekade lalu, biarkan kedua flag itu mati untuk saluran tersebut dan terima file yang lebih besar. Untuk penyimpanan arsip dan pengiriman web, di mana setiap viewer arus utama sudah membaca PDF 1.5 selama dua puluh tahun, mengaktifkan keduanya adalah kompresi yang Anda dapatkan hampir tanpa biaya
Ada satu efek orde kedua yang layak diberitahukan pada tim support Anda. Begitu dictionary dikemas ke dalam object stream, membandingkan dua file yang dihasilkan byte demi byte tidak lagi berarti apa-apa, karena mengubah satu field saja bisa me-Flate ulang seluruh kontainer dan mengacak semua yang ada setelahnya. Diff file semacam itu berdasarkan konten objeknya, bukan dengan perbandingan biner
Incremental update dan byte offset yang dilindunginya
Sebuah digital signature mencakup sebuah /ByteRange eksplisit: dua rentang dari file fisik, diberikan sebagai byte offset absolut, tempat digest CMS itu diambil. Tulis ulang file itu, bahkan menjadi sesuatu yang terlihat identik di layar, dan seluruh offset itu berpindah. Digest-nya berhenti cocok dan signature terbaca rusak. Itulah persis masalah yang dipecahkan ISO 32000-1 §7.5.6 dengan incremental update. Objek baru dan yang berubah ditambahkan setelah %%EOF yang sudah ada, lalu sebuah bagian cross-reference baru ditulis dengan entry /Prev-nya menunjuk kembali ke yang sebelumnya. Byte asli tidak pernah terganggu, sehingga revisi yang ditandatangani tetap dapat diverifikasi dan Acrobat bisa menampilkan setiap revisi yang ditandatangani secara terpisah di panel signature
HotPDF mengekspos ini melalui entry point-nya sendiri:
Pdf.BeginIncrementalUpdate('contract-signed.pdf');
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Addendum recorded 2026-06-11');
Pdf.SaveIncrementalUpdate('contract-updated.pdf'); // hanya menambahkan delta-nya saja
Ada dua hal yang sering membuat orang tersandung. BeginIncrementalUpdate harus menerima nama file asli, karena bagian xref yang ditambahkan mencatat offset yang hanya bermakna terhadap byte asli yang persis itu; arahkan ke salinan yang diganti nama atau disimpan ulang dan offset-nya menggambarkan file yang sudah tidak ada lagi. Dan penyimpanannya memang append-only secara konstruksi, sehingga outputnya selalu lebih besar daripada inputnya. Pertumbuhan itu bukan pemborosan yang perlu dipangkas. Itu adalah sifat yang sama yang membuat revisi yang sudah ditandatangani sebelumnya tetap utuh
Memodifikasi file yang dimuat melalui LoadFromFile
Developer yang pertama kali mengenal HotPDF lewat generation API-nya cenderung menabrak tembok tertentu. BeginDoc membuka sebuah dokumen yang benar-benar baru, yang merupakan alat yang salah ketika maksud Anda adalah mengubah dokumen yang sudah ada. Mengedit file yang sudah ada justru berjalan melalui pemanggilan loaded-document:
PageCount := Pdf.LoadFromFile('base.pdf');
Pdf.InsertPagesFromDocument(OtherDoc, '1-3', 5); // halaman 1-3 setelah halaman 5
Pdf.MovePage(2, 5);
Pdf.SaveLoadedDocument('modified.pdf');
Campurkan keduanya dan gejalanya adalah sebuah file output yang berisi konten baru Anda dan tidak ada sama sekali dari konten aslinya, karena BeginDoc dengan senang hati membangun dokumen baru di samping dokumen yang Anda kira sedang Anda edit. Baca LoadFromFile bersama SaveLoadedDocument sebagai satu kosakata dan BeginDoc bersama EndDoc sebagai kosakata lain. Sebuah rutin yang menggunakan keduanya terhadap file yang sama hampir selalu keliru
Kapan harus memadatkan file yang sudah ditambahi
Penyimpanan append-only membawa biaya yang perlahan menumpuk. Sebuah job malam hari yang mencap satu baris status pada PDF yang sama menghasilkan 365 revisi dalam setahun, dan setiap revisi menyeret sebuah bagian xref baru di belakangnya. Ketika riwayat itu sudah tidak lagi berguna, dan tidak ada signature dalam file yang perlu dipertahankan, Anda bisa meratakan semuanya dengan melakukan serialisasi ulang lewat jalur loaded-document:
Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');
Penyimpanan ulang ini adalah penulisan ulang penuh. Ia sengaja membuang revisi-revisi sebelumnya dan merusak signature apa pun yang masih ada dalam file, jadi tempatkan ini di belakang gerbang kebijakan yang sama seperti yang Anda terapkan pada langkah destruktif lainnya. Satu aturan produksi yang terbukti bertahan: padatkan ketika jumlah revisi melewati sebuah ambang, atau ketika overhead yang ditambahkan tumbuh melebihi porsi tertentu dari file dasarnya, dan jangan pernah memadatkan dokumen yang panel signature-nya berisi sesuatu
Memeriksa output sebelum dikirim
Memverifikasi sepasang fitur ini terasa menyegarkan karena begitu konkret. Buka hasilnya di Adobe Acrobat dan pastikan tiga hal: document properties melaporkan PDF 1.5 atau lebih baru begitu object stream diaktifkan; panel signature masih memvalidasi setiap revisi yang sebelumnya ditandatangani setelah sebuah incremental update; dan jumlah halaman serta bookmark selamat melewati siklus load, modify, dan save. Untuk output arsip, jalankan juga file itu lewat veraPDF, karena xref terkompresi justru adalah jenis struktur yang diteliti lebih dalam oleh validator ketat dibanding viewer yang lebih permisif. Jika pekerjaan Anda juga melibatkan input yang sangat besar, metode inspeksi dalam panduan kami tentang Direct File API untuk workflow PDF besar cocok secara alami dengan incremental saving, dan mekanisme signature di balik byte range di atas dibahas secara mendalam di artikel digital signature dan PAdES HotPDF
Kedua fitur ini tersedia sebagai bagian dari HotPDF Delphi Component untuk Delphi dan C++Builder, berdampingan dengan API generation, form, enkripsi, dan signing yang dibahas di tempat lain pada blog ini. Halaman produk menautkan referensi API lengkap jika Anda ingin mencocokkan pemanggilan di atas dengan pipeline dokumen Anda sendiri