Menghitung jumlah halaman dalam sebuah arsip hasil pindaian berukuran 1,4 GB seharusnya murah. Panggil LoadFromFile pada file itu dan operasi tersebut berhenti menjadi murah: HotPDF mem-parsing data cross-reference dan membangun sebuah objek dalam memori untuk setiap satu dari ratusan ribu objek tidak langsung milik dokumen tersebut, dan sebuah worker 32-bit menabrak batas ruang alamat 2 GB di suatu titik di tengah proses parsing itu. Operasi yang sebenarnya Anda inginkan, jumlah halaman, tidak pernah membutuhkan objek-objek itu sama sekali. Ia hanya butuh page tree dan tidak lebih. Celah itu, antara apa yang diminta sebuah pekerjaan dan apa yang diberikan sebuah full load, adalah alasan utuh mengapa Direct File API ini ada
Direct File API memberi Delphi dan C++Builder akses pada level file terhadap sebuah PDF: jumlah halaman, penyalinan, dekripsi, penambahan incremental, semuanya membaca dari disk hanya apa yang benar-benar dibutuhkan alih-alih merekonstruksi seluruh model dokumen di RAM. Keahliannya terletak pada mencocokkan setiap pekerjaan dengan tingkat paling ringan yang bisa menjawabnya. Cocokkan dengan benar dan sebuah service akan mempertahankan memori yang datar di berbagai ukuran input. Cocokkan dengan keliru dan file berukuran terlalu besar yang pertama akan menumbangkan worker tersebut
Berapa biaya yang harus dibayar sebuah full load
LoadFromFile bukanlah musuh. Ia memang layak menggunakan memorinya: begitu tree-nya berada di RAM, Anda mendapat akses acak ke setiap halaman dan setiap objek, dan itu persis yang dibutuhkan oleh InsertPagesFromDocument, MovePage, dan serialisasi ulang lewat SaveLoadedDocument. Tidak ada jalan pintas untuk restrukturisasi yang sesungguhnya; Anda harus memegang seluruh dokumen untuk bisa menyusunnya ulang
Masalahnya dimulai ketika ukuran input bukan sesuatu yang bisa Anda kendalikan. Upload dari pelanggan, hasil scanner, dan arsip dari satu dekade lalu tidak peduli dengan apa pun yang diasumsikan oleh test corpus Anda. Muat setiap input tanpa syarat dan batas memori Anda ditentukan oleh satu file terbesar yang pernah dikirim siapa pun. Waktu parsing mengikuti jumlah objek, dan memori resident mengendap pada beberapa kali lipat ukuran file setelah struktur objek dan stream yang sudah didekode dihitung, sehingga satu gigabyte di disk bisa berarti beberapa gigabyte resident
Melakukan kompilasi ulang untuk 64-bit memang mengangkat batas ruang alamat, namun tagihannya tetap utuh. Worker tetap membakar detik-detik CPU dan beberapa kali lipat ukuran file di RAM hanya untuk menjawab sebuah pertanyaan yang sebenarnya bisa dijawab struktur file itu sendiri dalam hitungan milidetik. Di bawah konkurensi, hitungannya berubah bermusuhan: empat full load berjalan sekaligus berbagi satu anggaran memori, dan throughput anjlok persis ketika antrean sedang paling dalam dan Anda paling tidak mampu menanggungnya
Membaca file melalui handle
Tingkat read-only membuka file sebagai sebuah handle, menjawab pertanyaan struktural tentangnya, lalu menutupnya. Tidak ada object tree, tidak ada rendering halaman, tidak ada memori yang bertumbuh seiring ukuran input
var
Pdf: THotPDF;
Handle, PageCount: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Handle := Pdf.DAOpenFileReadOnly('archive-2026-06.pdf', '');
if Handle > 0 then
try
PageCount := Pdf.DAGetPageCount(Handle);
RouteByPageCount('archive-2026-06.pdf', PageCount);
finally
Pdf.DACloseFile(Handle);
end;
finally
Pdf.Free;
end;
end;
Ada tiga kebiasaan yang menjaga tingkat ini tetap jujur. Pertama, periksa nilai return-nya. Handle yang tidak positif berarti proses open gagal, dan memicu DAGetPageCount pada handle yang mati adalah jenis bug yang tetap tersembunyi sampai suatu hari pelanggan mengirim file yang cacat. Kedua, pasangkan setiap open yang berhasil dengan DACloseFile di dalam blok finally; sebuah service yang membocorkan handle tidak akan crash, ia hanya membusuk perlahan, yang justru lebih buruk. Ketiga, hormati apa yang sebenarnya dilakukan parameter password. DAOpenFileReadOnly menerima satu parameter password, namun untuk input terenkripsi ia diam-diam turun ke full parse untuk membaca jumlah halamannya, sehingga jaminan memori datar itu pun menguap. Arahkan file terproteksi melalui DecryptFile terlebih dahulu dan sisa pipeline tetap murah
Probe yang sama juga berfungsi ganda sebagai gerbang triase. File bisa saja muncul dengan label yang salah, setengah ter-upload, atau berganti nama dari format lain sama sekali, dan sebuah pemeriksaan DAOpenFileReadOnly menolak semua itu di pintu depan dalam hitungan milidetik, dengan error yang tertaut langsung pada file yang bermasalah. Alternatifnya adalah membiarkan file sampah menerobos jauh ke dalam queue worker dan meledak di sana, di mana mengurai input mana yang menjadi penyebabnya bisa menghabiskan waktu satu sore penuh
Menyalin, mendekripsi, dan mengenkripsi seluruh file
Tingkat kedua memindahkan dan mentransformasi seluruh file tanpa pernah mengekspos bagian internalnya. Inilah pemanggilan-pemanggilan yang paling diandalkan oleh pipeline intake
// Salinan struktural: validate-and-move tanpa mem-parsing object tree
Status := Pdf.DACopyFile('incoming\statement.pdf', 'verified\statement.pdf');
LogDirectFileStatus('copy', Status);
// Dekripsi sambil menyalin: jalur Direct File menuju input yang terproteksi
Status := Pdf.DecryptFile('incoming\protected.pdf',
'verified\plain.pdf', 'batch-password');
LogDirectFileStatus('decrypt-copy', Status);
// Enkripsi sambil menyalin: melindungi output tanpa full load
Status := Pdf.EncryptFile('verified\statement.pdf',
'outbound\statement.pdf', 'owner-secret', '', aes256, [prPrint]);
LogDirectFileStatus('encrypt-copy', Status);
Setiap pemanggilan punya tempatnya sendiri. DACopyFile adalah salinan tervalidasi dari direktori karantina ke penyimpanan terkelola: ia membuka dan mengindeks struktur PDF sambil berjalan, sehingga input yang terpotong atau bukan PDF gagal tepat di sini, bukan tiga tahap kemudian di hilir. DecryptFile menulis salinan yang telah didekripsi melalui jalur penulisan-ulang AES-256 langsung yang melewati object tree kapan pun input mengizinkannya, rekan untuk file besar dari alur dekripsi load-and-resave yang dibahas di artikel enkripsi AES-256. EncryptFile menjalankan gerakan yang sama secara terbalik, menerapkan proteksi password selama proses penyalinan pada level file dengan parameter key-type dan izin yang sama seperti yang sudah digunakan jalur in-memory
Menambahkan perubahan alih-alih menulis ulang
Incremental update, yang didefinisikan dalam ISO 32000-1 §7.5.6, adalah tingkat ketiga. Byte asli tetap berada di tempatnya di disk, dan objek baru atau yang dimodifikasi ditambahkan setelahnya, diikuti oleh sebuah bagian cross-reference baru yang merangkai kembali ke bagian asli. Untuk sebuah arsip 900 MB yang hanya butuh satu halaman ditambahkan, biaya penulisannya adalah delta-nya saja, bukan seluruh file
// Menambahkan halaman audit ke arsip besar tanpa menulis ulang
Pdf.BeginIncrementalUpdate('archive-2026-06.pdf');
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Processed by intake service 2026-06-11');
Pdf.SaveIncrementalUpdate('archive-2026-06-stamped.pdf'); // byte asli + delta
Ada dua poin disiplin yang penting di sini. BeginIncrementalUpdate harus menunjuk ke file asli, karena data cross-reference yang ditambahkan merangkai kembali ke byte offset di dalamnya. Dan modelnya memang dirancang append-only: setiap incremental save membesarkan file, tidak pernah mengecilkannya. Sebuah dokumen yang dicap setiap malam akan membengkak tanpa batas hingga sebuah serialisasi ulang berkala, memuatnya lalu menulisnya kembali lewat SaveLoadedDocument, memadatkannya kembali. Sifat append-only yang sama inilah yang menjadikan incremental update satu-satunya cara aman untuk menyentuh dokumen yang telah ditandatangani secara digital, sebuah batasan yang dibahas di artikel digital signature dan PAdES. Mesin cross-reference yang mendasarinya mendapat pembahasannya sendiri di artikel object stream dan incremental update
Ada sebuah jebakan dalam save append-only yang lolos dari kebanyakan review. Byte asli tetap berada di dalam file, tetap terbaca bagi siapa pun yang mau melihatnya. Sebuah incremental update yang "menggantikan" sebuah halaman tidak menghapus halaman lama itu; ia hanya menggantikannya pada revisi saat ini, sementara revisi sebelumnya tetap ada di sana, sepenuhnya dapat dipulihkan. Jadi incremental update adalah alat yang salah untuk membuang konten sensitif. Untuk benar-benar membuang riwayat yang tidak boleh dilihat penerima, Anda membutuhkan serialisasi ulang penuh: LoadFromFile diikuti oleh SaveLoadedDocument, yang hanya menulis keadaan saat ini dan meninggalkan revisi-revisi yang terkubur di belakang
Mencocokkan tingkat dengan operasinya
Logika pemilihannya cukup singkat untuk diingat di kepala, dan lebih baik mengkodekannya sebagai sebuah keputusan routing yang eksplisit di bagian atas pipeline daripada membiarkan setiap pekerjaan berimprovisasi jalurnya sendiri. Operasi yang Anda butuhkan menentukan tingkatnya:
- Menghitung, memeriksa, atau mengklasifikasikan membuka sebuah handle:
DAOpenFileReadOnly,DAGetPageCount,DACloseFile - Memindahkan, mendekripsi, atau mengenkripsi seluruh file tetap berada pada level file dengan
DACopyFile,DecryptFile, atauEncryptFile - Merestrukturisasi halaman atau menggabungkan dokumen membutuhkan full load:
LoadFromFile, laluInsertPagesFromDocumentatauMovePage, laluSaveLoadedDocument - Menambahkan delta kecil ke file yang sangat besar atau yang sudah ditandatangani memanggil
BeginIncrementalUpdatelalu menyimpannya
Pipeline campuran sebaiknya menempatkan sebuah ambang ukuran di depan jalur full-load. Kirim apa pun yang melewati beberapa ratus megabyte melalui tingkat Direct File, dan cadangkan full load hanya untuk restrukturisasi yang sesungguhnya pada worker 64-bit dengan anggaran memori yang nyata. Ambang ini mengubah crash out-of-memory menjadi sebuah keputusan routing yang bisa Anda lihat dan atur
Tingkat apa pun yang menangani sebuah pekerjaan, tulis outputnya ke sebuah nama sementara dan ganti nama ke lokasi akhirnya hanya setelah hasilnya tervalidasi. Sebuah file yang setengah tertulis di bawah nama akhirnya akan terlihat persis seperti file yang baik bagi tahap berikutnya dalam pipeline, dan pemanggilan Direct File membuat pemeriksaan ini murah: memastikan sebuah output cukup dengan satu baris probe handle
Direct File API ini tersedia sebagai bagian dari HotPDF Delphi Component untuk Delphi dan C++Builder. Halaman produk menautkan referensi fungsi secara menyeluruh, termasuk pemanggilan incremental-update yang ditunjukkan di sini