Menggabung atau memecah PDF dua gigabyte dengan cara yang sudah jelas menelan dua hal sekaligus: waktu jam dinding dan ruang alamat. Cara yang sudah jelas itu adalah memuat tiap masukan, mengerjakan tugasnya, menulis keluarannya. Pemuatanlah yang jebol. Arsip pindaian yang naik dari 300 ke 600 DPI melipatduakan resolusi liniernya dan kira-kira melipatempatkan ukurannya di disk, sehingga pekerjaan perakitan yang sepanjang tahun santai menangani berkas 400 MB mulai megap-megap begitu satu masukan melewati satu gigabyte, sering kali sekadar saat menghitung halaman. Tugasnya sendiri tidak pernah bertambah sulit. Buka, hitung, pilih rentang, sambung, hanya itu isinya. Pemuatan pohon penuh sekadar berhenti menjadi default yang waras pada ukuran sebesar itu. PDF Library for Delphi, pustaka PDF milik losLab untuk Delphi dan C++Builder, menjawabnya dengan lapisan Direct Access: sekeluarga fungsi berawalan DA yang ditopang pembaca streaming yang menyusuri tabel cross-reference di tempat alih-alih membangun seluruh dokumen di memori
Ke mana memori pergi pada pemuatan penuh
Memuat PDF "secara normal" berarti mengurai xref, meresolusi setiap objek tak langsung menjadi pohon di memori, mendekode object stream, dan merangkai pohon halaman, font, serta anotasi menjadi objek yang bisa Anda manipulasi. Untuk alur kerja penyuntingan itu pertukaran yang tepat. Untuk pekerjaan gabung, pecah, dan pemeriksaan, sebagian besarnya sia-sia. Arsip pindaian 30.000 halaman bisa memuat jutaan objek tak langsung, dan pekerjaan pemecahan hanya perlu membaca beberapa ratus di antaranya: simpul halaman dalam rentang yang diminta, ditambah apa pun yang dirujuk simpul itu
Lapisan Direct Access membalik modelnya. DAOpenFile dan DAOpenFileReadOnly mengurai trailer dan xref, hanya beberapa kilobyte di ekor berkas, lalu mengembalikan sebuah file handle. Objek diambil secara malas ketika sebuah pemanggilan membutuhkannya. Konsekuensi praktisnya, membuka berkas berukuran multi-gigabyte memakan waktu kira-kira sama dengan membuka berkas kecil, dan memori mengikuti apa yang Anda sentuh, bukan apa yang dikandung berkasnya
Menyelidiki berkas raksasa tanpa memuatnya
Pola di bawah ini berasal dari benchmark berkas besar milik pustaka ini sendiri: buka read-only, ajukan pertanyaan, tutup. Tidak pernah ada pohon dokumen yang terbentuk
var
Lib: TPDFlib;
Handle, Pages: Integer;
begin
Lib := TPDFlib.Create;
try
Handle := Lib.DAOpenFileReadOnly('archive-2025.pdf', '');
if Handle = 0 then
raise Exception.Create('Direct access open failed');
Pages := Lib.DAGetPageCount(Handle);
Writeln('pages : ', Pages);
Writeln('title : ', Lib.DAGetInformation(Handle, 'Title'));
Lib.DACloseFile(Handle);
finally
Lib.Free;
end;
end;
Mode read-only layak dipilih kapan pun bisa: ia membuat tahap penerimaan tetap berjalan sementara proses lain memegang berkasnya, dan ia mendokumentasikan niat. Tahap penyelidikan yang tanpa sengaja memanggil fungsi pengubah akan gagal seketika alih-alih merusak arsipnya
PageRef adalah handle objek, bukan nomor halaman
Kesalahan paling umum dengan API DA adalah menyodorkan nomor halaman di tempat sebuah fungsi mengharapkan PageRef. Hampir setiap pemanggilan DA per halaman menerima handle referensi ke objek halaman, bukan nomor halaman: DAExtractPageText, DARenderPageToFile, DARotatePage, dan DACapturePage semuanya mengharapkan sebuah ref. Anda memperolehnya dengan menerjemahkan nomor yang dipakai manusia lewat DAFindPage:
PageRef := Lib.DAFindPage(Handle, 250); // nomor halaman -> handle objek
if PageRef <> 0 then
begin
Text := Lib.DAExtractPageText(Handle, PageRef, 0);
Lib.DARenderPageToFile(Handle, PageRef, 5, 150, 'page250.png');
end;
Menyodorkan angka mentah 250 justru tidak menimbulkan kesalahan. Ia mengalamati objek apa pun yang kebetulan duduk di balik nilai handle itu, yang pada hari baik gagal secara kasatmata dan pada hari buruk mengekstrak teks dari halaman yang salah ke dalam dokumen yang dilihat pelanggan. Jika Anda membungkus lapisan DA dalam kode layanan Anda sendiri, buatlah penerjemahan itu mustahil dilewati: terima nomor halaman di batas antarmuka, panggil DAFindPage seketika, dan hanya oper ref di bagian dalam
Menggabungkan ratusan berkas dengan daftar bernama
Untuk dua berkas, MergeFiles(First, Second, Output) sudah cukup. Perakitan batch lebih baik diskalakan lewat daftar berkas: daftarkan masukan di bawah sebuah nama daftar, lalu gabungkan daftar itu dalam satu tahap
Lib.AddToFileList('Statements', 'jan.pdf');
Lib.AddToFileList('Statements', 'feb.pdf');
Lib.AddToFileList('Statements', 'mar.pdf');
Lib.MergeFileList('Statements', 'q1-statements.pdf');
// Verifikasi hasilnya dengan cara murah: direct access lagi
Handle := Lib.DAOpenFileReadOnly('q1-statements.pdf', '');
Writeln('merged pages: ', Lib.DAGetPageCount(Handle));
Lib.DACloseFile(Handle);
Keluarga merge punya tiga varian, dan bedanya bukan kecepatan semata. MergeFileListFast melewatkan pelestarian pohon struktur; MergeFileListStrict memberlakukan mode ketat; versi tanpa akhiran adalah default yang berimbang. Aturan operasional yang mengalir darinya: jika ada masukan berupa Tagged PDF yang struktur aksesibilitasnya wajib bertahan, dengan apa pun yang diproduksi untuk PDF/UA sebagai contoh paling jelas, gunakan varian default atau Strict, sebab Fast diam-diam membuang pohon strukturnya. Untuk arsip pindaian biasa tanpa penandaan, Fast adalah kinerja gratis. Putuskan per pipeline, bukan per suasana hati pengembang, dan catat varian yang dipakai di log pekerjaan
Memecah tanpa memuat: ekstraksi rentang
Pemecahan mengikuti filosofi tanpa-muat yang sama. ExtractFilePages(InputFileName, Password, OutputFileName, RangeList) menarik rentang halaman langsung dari berkas ke berkas, dengan daftar rentang seperti '1-500', '501-1000', atau pilihan yang dipisahkan koma, dan sumbernya tidak pernah menjelma menjadi pohon dokumen. Ketika sebuah dokumen sudah dimuat karena alasan lain, ExtractPageRanges menghasilkan dokumen baru di memori dari dokumen saat ini, dan CopyPageRanges menarik rentang dari dokumen lain yang sudah dimuat berdasarkan ID-nya. Untuk memecah aliran cetak terkonsolidasi menjadi satu berkas per laporan, bentuk berkas-ke-berkas itulah yang mencegah masukan 4 GB pernah mengembang ke RAM
Berkas yang berdusta tentang geometrinya
Pipeline berkas besar bertemu berkas rusak dengan laju yang tak pernah dilihat pipeline berkas kecil, semata karena masukannya melewati lebih banyak sistem. Dua bentuk kegagalan pantas ditangani secara eksplisit
Pertama, header yang bergeser. Gateway surel dan spooler cetak kadang menambahkan byte di depan sebuah PDF, sehingga penanda %PDF tidak lagi duduk di offset 0 dan setiap offset xref di berkas itu meleset sebesar jumlah yang sama. Pembaca streaming mendeteksi hal ini dan memaparkannya (DAShiftedHeader di tingkat datar, ShiftedHeader pada TSmartPDFReader), lalu mengompensasinya saat pembacaan. Aritmetika offset rakitan sendiri biasanya tidak melakukannya, dan itulah sebabnya "jalan di semua berkas yang kami hasilkan, gagal di berkas dari pelanggan X" menjadi gejala klasiknya
Kedua, tabel cross-reference yang rusak. DACopyFile(InputFileName, OutputFileName, PageCount) mengalirkan seluruh berkas ke salinan baru sambil membangun ulang xref-nya, dan mengembalikan jumlah halaman sebagai hasil sampingan. Menjalankannya sebagai tahap normalisasi di depan konsumen hilir yang rewel mengubah satu kelas kegagalan parsing yang datang sesekali menjadi satu langkah perbaikan yang dapat diramalkan. Dan ketika suntingan Anda sendiri perlu disimpan, DAAppendFile menuliskannya sebagai incremental update, menambahkan revisi baru alih-alih menulis ulang bergigabyte-gigabyte, sehingga biaya penyimpanan sebanding dengan perubahannya, bukan dengan berkasnya
Detail pengiriman: linearisasi dan komposisi
Dua kemampuan bertetangga melengkapi pipeline berkas besar. Ketika keluaran yang sudah dirakit disajikan lewat HTTP untuk ditonton di peramban, LinearizeFile menata ulangnya untuk streaming byte-range sehingga halaman pertama tampil sebelum sisa paket 500 MB selesai diunduh. Jalankan sebagai tahap terakhir, setelah semua penggabungan, sebab modifikasi apa pun sesudahnya akan melinearisasi ulang berkas itu menjadi tidak linier. Dan ketika paket memerlukan komposisi alih-alih penyambungan biasa, misalnya lembar sampul yang distempel di belakang setiap laporan atau dua halaman sumber yang diimposisi ke satu lembar keluaran, DACapturePage mengubah halaman mana pun menjadi templat yang dapat dipakai ulang dan DADrawCapturedPage menempatkannya pada halaman tujuan di persegi panjang sembarang, tetap tanpa pemuatan dokumen penuh atas sumber multi-gigabyte itu
Batasan dan apa yang tetap read-only
Formatnya sendiri kehabisan ruang jauh sebelum Direct Access melakukannya. Offset bertipe Int64 di sepanjang lapisan DA, jadi plafon yang sebenarnya adalah disk yang tersedia dan field offset xref 10 digit pada tabel cross-reference klasik (non-stream). Arsip pindaian multi-gigabyte biasa saja dalam praktik, dan memori tetap terbatas berapa pun ukuran berkasnya karena objek baru dibaca ketika ada pemanggilan yang memintanya
Dua pertanyaan cukup sering muncul sehingga layak dijawab langsung. Penggabungan lewat jalur default membawa serta struktur dokumen, jadi bookmark dan tautan bertahan; varian Fast-lah yang menukar pohon struktur demi kecepatan, dan itulah seluruh alasan untuk mencadangkannya bagi masukan tanpa penandaan. Kebiasaan yang aman adalah membuka keluaran gabungan, menyusuri outline-nya, dan memeriksa acak beberapa tautan internal sebelum mengirimkannya. Soal penyuntingan: ada jalan tengah yang berguna antara penyelidikan read-only dan pemuatan penuh. Operasi tingkat halaman bekerja langsung pada handle-nya, di antaranya DARotatePage, DAMovePage, dan DAHidePage, berikut pembacaan field formulir, dan DAAppendFile mengekalkan suntingan itu sebagai revisi inkremental. Penyuntingan tingkat konten, apa pun yang menulis ulang operator marking di dalam sebuah halaman, tetap menjadi milik lapisan dokumen penuh
Artikel terkait
Jika keluaran gabungan Anda harus tetap aksesibel, latar belakang pohon strukturnya dibahas di artikel aksesibilitas Tagged PDF, yang menjelaskan persis apa yang akan dibuang varian merge Fast. Untuk menarik konten keluar dari rentang yang Anda pecah, lihat panduan ekstraksi teks, gambar, dan font
Daftar fungsi Direct Access selengkapnya disertakan bersama pustaka; edisi dan unduhan uji coba ada di halaman produk PDF Library for Delphi