Gabungkan PDF eksternal atau tambahkan halaman dari dokumen yang ada
Sekilas pandang
Bagian berjudul “Sekilas pandang”Anda memiliki beberapa berkas PDF di disk dan ingin menghasilkan satu PDF. Resep ini menggabungkan dokumen yang sudah ada secara berurutan, dari awal hingga akhir, melalui permukaan penggabungan Core, NextPDF\Document\PdfMerger. Anda menyediakan string byte PDF mentah. Penggabung menomori ulang setiap objek untuk menghindari benturan, membangun satu page tree dan satu cross-reference table, lalu mengembalikan NextPDF\Document\MergeResult yang dapat Anda tulis ke disk atau alirkan ke klien.
Permukaan yang sama menangani tiga tugas yang paling sering Anda butuhkan:
- Gabungkan daftar PDF berurutan menjadi satu dokumen.
- Tambahkan PDF kedua setelah PDF dasar.
- Sisipkan di awal halaman dengan menempatkan dokumen baru sebagai masukan pertama.
Penggabungan berjalan in-process, tanpa peramban headless atau panggilan jaringan. Anda memerlukan Core yang sudah terpasang (composer require nextpdf/core:^3) serta dua berkas PDF atau lebih yang dapat dibaca.
Pemasangan
Bagian berjudul “Pemasangan”composer require nextpdf/core:^3Tinjauan konseptual
Bagian berjudul “Tinjauan konseptual”PDF menata halaman dalam page tree yang akarnya adalah node /Pages, dan menemukan setiap objek tidak langsung melalui cross-reference table. Ketika Anda menggabungkan dua dokumen sumber, nomor objeknya saling tumpang tindih. Kedua berkas hampir selalu memuat objek 1 0 obj, /Catalog, dan node /Pages. Jika Anda hanya menggabungkan byte secara berurutan, hasilnya adalah berkas rusak karena referensinya tidak lagi menunjuk ke objek yang semestinya.
PdfMerger menangani tumpang tindih tersebut. Ia mengekstraksi objek halaman dari setiap masukan, menomori ulang setiap objek ke dalam satu ruang alamat, menulis ulang referensi /Parent setiap halaman agar menunjuk ke satu node /Pages gabungan, lalu menghasilkan satu catalog, satu page tree, dan satu trailer. Keluarannya adalah dokumen dengan struktur baru, bukan sekadar rangkaian berkas yang ditempelkan.
Aturan pengurutannya sederhana: halaman muncul dalam urutan yang sama dengan berkas sumbernya di daftar masukan. Untuk menambahkan di akhir, tempatkan dokumen dasar terlebih dahulu. Untuk menyisipkan di awal, tempatkan dokumen baru terlebih dahulu. Tidak ada metode terpisah untuk menyisipkan di awal karena urutan masukan adalah satu-satunya kendali yang Anda perlukan.
Permukaan API
Bagian berjudul “Permukaan API”new NextPDF\Document\PdfMerger() menyediakan dua metode.
merge(list<string> $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000): MergeResultmenggabungkan daftar string byte PDF mentah yang berurutan. Kedua parameter batas tersebut membatasi jumlah berkas dan ukuran total masukan. Nilai default keduanya aman untuk produksi; perketat keduanya sesuai setiap beban kerja.append(string $basePdf, string $appendPdf): MergeResultadalah pembungkus praktis yang menggabungkan persis dua dokumen secara berurutan. Ini setara denganmerge([$basePdf, $appendPdf]).
Kedua metode mengembalikan NextPDF\Document\MergeResult, objek readonly yang membawa $pdfData (byte gabungan), $totalPages, $sourceCount, $mergedSize, dan helper isValid() yang memastikan keluaran diawali dengan header %PDF.
Masukan berupa string byte mentah, bukan jalur berkas. Baca sendiri berkasnya dengan file_get_contents(), atau ambil byte tersebut dari object storage. Hal ini menjaga penggabung bebas dari asumsi sistem berkas dan memungkinkan Anda menggabungkan dokumen yang tidak pernah menyentuh disk.
Jika Anda perlu mengimpor satu halaman dari PDF eksternal sebagai Form XObject yang dapat dipakai ulang, misalnya untuk meletakkan halaman kop surat di belakang konten yang dihasilkan, gunakan kontrak importer lintas paket NextPDF\Contracts\ImportedFormObjectInterface, yang diimplementasikan oleh importer seperti nextpdf/artisan. Untuk komposisi seluruh dokumen dan seluruh halaman, gunakan permukaan PdfMerger yang didokumentasikan di sini.
Contoh kode — Mulai cepat
Bagian berjudul “Contoh kode — Mulai cepat”Contoh ini membaca dua berkas dan menulis hasil gabungannya. Contoh ini menghilangkan penanganan kesalahan untuk menunjukkan bentuk pemanggilannya; contoh produksi di bawah menambahkan pengamanan lengkap.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Document\PdfMerger;
$merger = new PdfMerger();
$result = $merger->merge([ file_get_contents(__DIR__ . '/cover.pdf'), file_get_contents(__DIR__ . '/body.pdf'), file_get_contents(__DIR__ . '/appendix.pdf'),]);
file_put_contents(__DIR__ . '/combined.pdf', $result->pdfData);
printf("Merged %d source(s) into %d page(s).\n", $result->sourceCount, $result->totalPages);Contoh kode — Produksi
Bagian berjudul “Contoh kode — Produksi”Program mandiri ini membangun dua dokumen kecil di memori, sehingga dapat berjalan tanpa berkas eksternal. Program ini menggabungkan keduanya, memvalidasi hasilnya, dan menulis keluarannya. Program ini menangkap dua exception yang dilemparkan oleh permukaan penggabungan dan melempar ulang masing-masing dengan konteks, alih-alih menelannya begitu saja. Ganti masukan dalam memori dengan pembacaan file_get_contents() Anda sendiri atau pengambilan dari object storage, lalu hubungkan keluarannya ke lapisan respons atau penyimpanan Anda.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Document\MergeResult;use NextPDF\Document\PdfMerger;use NextPDF\Exception\PageLayoutException;use NextPDF\Exception\WriterException;
/** * Build a tiny labelled PDF so the program is self-contained. * * In your own code, replace calls to this helper with reads of the external * PDFs you want to combine, for example file_get_contents($path). */function buildSample(string $label, int $pages): string{ $doc = Document::createStandalone(); $doc->setTitle($label);
for ($page = 1; $page <= $pages; $page++) { $doc->addPage(); $doc->setFont('helvetica', '', 12); $doc->cell(0, 10, sprintf('%s - page %d', $label, $page), newLine: true); }
return $doc->getPdfData();}
// Validate the input set before touching the merger. An empty set is a// configuration error, not an empty success./** @var list<string> $sources Raw PDF byte strings, in output order. */$sources = [ buildSample('Cover', 1), // first in the list -> first in the output (prepend position) buildSample('Body', 2), buildSample('Appendix', 1), // last in the list -> appended after the body];
if ($sources === []) { throw new RuntimeException('No source PDFs supplied to merge.');}
$merger = new PdfMerger();
try { // Bound the merge deliberately: at most 50 files, 100 MB total input. $result = $merger->merge($sources, maxFiles: 50, maxTotalBytes: 100_000_000);} catch (PageLayoutException $e) { // Raised when the list is empty or an input does not begin with %PDF. throw new RuntimeException( sprintf('Merge rejected an input: %s', $e->getConstraint()), previous: $e, );} catch (WriterException $e) { // Raised when the total input size exceeds the configured byte cap. throw new RuntimeException( sprintf('Merge exceeded its size budget at stage "%s".', $e->getWriterState()), previous: $e, );}
if (!$result->isValid()) { throw new RuntimeException('Merged output failed its structural header check.');}
emitResult($result);
/** * Write the merged document to the cookbook side-channel, or to a default file. */function emitResult(MergeResult $result): void{ printf( "Merged %d source(s) into %d page(s), %d bytes.\n", $result->sourceCount, $result->totalPages, $result->mergedSize, );
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT'); $path = $out !== false && $out !== '' ? $out : __DIR__ . '/combined.pdf';
if (file_put_contents($path, $result->pdfData) === false) { throw new RuntimeException(sprintf('Could not write merged PDF to "%s".', $path)); }}Keluaran standar yang diharapkan (total halaman adalah jumlah halaman dari semua sumber, dan ukuran byte bergantung pada hasil build):
Merged 3 source(s) into 4 page(s), <n> bytes.Kasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”- Masukan berupa byte, bukan jalur.
merge()menerima string PDF mentah. Baca berkasnya denganfile_get_contents()terlebih dahulu. Jika Anda memberikan string jalur, masukan akan gagal pada pemeriksaan header%PDFdan melemparkanPageLayoutException. - Urutan adalah urutan keluaran. Halaman ditempatkan sesuai urutan kemunculan berkas sumbernya dalam daftar. Tidak ada metode terpisah untuk menyisipkan di awal: tempatkan dokumen baru di awal untuk menyisipkan di depan, atau di akhir untuk menambahkan di belakang.
- Daftar kosong adalah sebuah kesalahan.
$pdfFilesyang kosong melemparkanPageLayoutException, bukan hasil kosong. Validasi himpunannya sebelum Anda memanggil penggabung. - Setiap masukan divalidasi di awal. Setiap entri harus tidak kosong dan diawali dengan
%PDF. Masukan pertama yang gagal akan melemparkanPageLayoutExceptionbeserta batasan yang dilanggar, dan tidak ada yang digabungkan. - Batas melemparkan, bukan memotong. Melampaui
maxFilesmelemparkan melalui penjaga sumber daya internal, dan melampauimaxTotalBytesmelemparkanWriterException. Penggabung tidak pernah diam-diam membuang berkas atau memangkas byte, jadi setel kedua batas tersebut sesuai beban kerja Anda. - Keluaran baru secara struktural, bukan stabil per-byte. Dokumen gabungan membawa catalog, page tree, dan trailer yang baru. Dua kali eksekusi atas masukan yang sama setara secara struktural, tetapi tidak dijamin identik per-byte. Itulah sebabnya resep ini mendeklarasikan profil reproduksibilitas
structural. - Anotasi tingkat halaman dan sumber daya bersama. Penggabungan menyusun objek halaman ke dalam satu pohon. Struktur tingkat dokumen yang berada di luar objek halaman dalam berkas sumber tidak ikut terbawa. Jika Anda perlu mengimpor satu halaman sebagai grafik yang dapat dipakai ulang beserta sumber dayanya, gunakan jalur
ImportedFormObjectInterfacemelalui importer sepertinextpdf/artisan.
Kinerja
Bagian berjudul “Kinerja”Penggabungan bersifat linear terhadap jumlah total halaman. Pekerjaannya didominasi oleh penguraian dan penomoran ulang objek, bukan oleh pembukuan internal penggabung. Puncak memori mengikuti total byte masukan karena setiap sumber disimpan di memori sebagai string selama keluaran dirakit. Penjaga maxTotalBytes memastikan puncak tersebut tetap terbatas. Untuk alur kerja bervolume tinggi, atur maxFiles dan maxTotalBytes ke nilai terkecil yang dibutuhkan beban kerja Anda, agar batch yang cacat atau terlalu besar gagal dengan cepat alih-alih menghabiskan memori. Penggabungan kecil yang umum berada dalam anggaran 1500 ms waktu dinding dan puncak 64 MB.
Catatan keamanan
Bagian berjudul “Catatan keamanan”Penggabungan berjalan in-process; tidak ada byte dokumen yang meninggalkan host, dan tidak ada panggilan jaringan yang dilakukan. Perlakukan setiap PDF eksternal sebagai masukan yang tidak tepercaya:
- Jaga batas tetap ketat.
maxFilesdanmaxTotalBytesadalah garis pertahanan pertama Anda terhadap masukan denial-of-service. Untuk permukaan apa pun yang menerima unggahan, atur keduanya ke batas atas sesungguhnya Anda, bukan ke nilai standar yang longgar. - Validasi sebelum Anda memercayai. Penggabungan yang berhasil berarti byte-nya telah disatukan, bukan berarti masukannya aman. Jalankan masukan yang tidak tepercaya melalui Core inspector terlebih dahulu. Lihat Mengurai dan memeriksa PDF untuk pemindaian triase terbatas yang menandai enkripsi, tanda tangan, dan penanda risiko sebelum pemrosesan yang lebih berat.
- Jangan pernah menyisipkan masukan pengguna ke dalam jalur. Resep ini menulis ke jalur tetap atau ke saluran samping cookbook. Turunkan jalur keluaran dari nilai yang dikendalikan server, jangan pernah dari field permintaan, untuk menghindari path traversal.
- Tidak ada rahasia di dalam dokumen. Jangan menyematkan kredensial, token, atau pengidentifikasi internal dalam dokumen gabungan yang Anda kembalikan kepada klien.
Kesesuaian
Bagian berjudul “Kesesuaian”Resep ini tidak membuat klaim standar normatif tersendiri. Resep ini menyusun dokumen yang sudah ada melalui permukaan penggabungan Core dan memvalidasi hasilnya dengan pemeriksaan header MergeResult::isValid(). Model page tree yang dibangun ulang oleh PdfMerger adalah struktur page tree PDF 2.0 yang dijelaskan dalam referensi /modules/core/document/. Untuk pembacaan struktural terhadap dokumen masukan atau keluaran apa pun, termasuk versi, jumlah halaman, enkripsi, dan flag tanda tangan, gunakan Core inspector yang didokumentasikan di Mengurai dan memeriksa PDF.
Lihat juga
Bagian berjudul “Lihat juga”- Referensi modul Document — permukaan lengkap untuk pemisahan, penggabungan, dan bagian dokumen.
- Mengurai dan memeriksa PDF — lakukan triase masukan yang tidak tepercaya sebelum Anda menggabungkannya.
- Penanganan kesalahan berbasis exception — hierarki exception NextPDF di balik
PageLayoutExceptiondanWriterException. - Membangun dokumen multi-halaman — susun halaman yang kemudian Anda gabungkan.