Lewati ke konten

Document: DParts, pemisahan / penggabungan, dan ekstensi vendor

Modul Document menangani berkas Portable Document Format (PDF) secara utuh, bukan konten halaman. Modul ini membangun hierarki Document Part yang digunakan oleh alur kerja teregulasi untuk melampirkan metadata. Modul ini memisahkan PDF menjadi segmen rentang halaman, menggabungkan beberapa PDF menjadi satu, dan mendaftarkan ekstensi pengembang di katalog dokumen.

Terminal window
composer require nextpdf/core:^3

Modul ini beroperasi di atas lapisan konten halaman. Jika Graphics dan Content menghasilkan operator, Document bekerja pada tingkat struktural: page tree, katalog dokumen, dan Document Part tree.

Sebuah Document Part (DPart) adalah partisi logis dalam sebuah PDF. ISO 32000-2 mendefinisikan hierarki DPart dengan node yang membawa Document Part Metadata (DPM). Alur kerja teregulasi, seperti farmasi, hukum, atau pengarsipan, dapat mengaitkan metadata dengan sub-rentang halaman, bukan dengan seluruh berkas — §14.12. DPart adalah node readonly yang tidak dapat diubah: node daun mereferensikan rentang indeks halaman yang bersambung, dan node perantara mengelompokkan node anak DPart menjadi sebuah tree. DPartRoot adalah akar tree yang diserialisasi oleh Writer. Entri /Start dan /End pada node daun merupakan referensi tidak langsung ke objek halaman, bukan bilangan bulat indeks halaman — §14.12. DPart::resolveWithPageObjects() menyelesaikan entri tersebut terhadap peta indeks-halaman→nomor-objek yang disediakan writer dan mengembalikan bentuk referensi /Start (dan /End opsional). Bentuk bilangan bulat hanya digunakan sebagai cadangan pada jalur pengujian ketika peta tersebut tidak tersedia.

PdfMerger dan PdfSplitter adalah antarmuka komposisi dokumen. PdfMerger menggabungkan objek halaman dari beberapa PDF masukan, menomori ulang objek untuk menghindari tabrakan, dan membangun ulang satu page tree serta satu tabel referensi-silang. Page tree yang dihasilkan adalah node Pages yang seimbang dengan Kids dan Count, ditambah model atribut yang dapat diwariskan yang didefinisikan PDF untuk node page-tree — §7.7.3. PdfSplitter melakukan kebalikannya: ia mengekstrak rentang halaman menjadi objek SplitDocument mandiri. PageRange adalah value object yang dikonsumsi oleh kedua kelas tersebut. Objek ini berbasis-1, memvalidasi batasnya, dan menjawab contains(), count(), dan toArray().

VendorExtensionRegistry, ExtensionsDictionary, dan DeveloperExtensionEntry memodelkan dictionary developer-extensions di katalog dokumen. Engine menggunakan dictionary tersebut untuk mendeklarasikan tingkat ekstensi vendor di luar spesifikasi dasar. Registry menolak pendaftaran ulang prefiks vendor yang sama ketika menimbulkan konflik, dengan VendorExtensionRegistryConflictException. CollectionDictionary dan CollectionSort memodelkan entri katalog collection PDF (portable collection atau portfolio).

KelasMetode utamaPeran
DPartisLeaf(), hasMetadata(), resolveWithPageObjects(), write()Node Document Part yang immutable (@since 1.12.0)
DPartRootisEmpty(), write()Akar DPart tree yang diserialisasi oleh Writer (@since 1.12.0)
PdfMergermerge(array $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000), append()Penggabungan multi-PDF dengan penomoran ulang objek (@since 1.9.0)
PdfSplittersplit(), splitEvery(), extractPages()Pemisahan rentang halaman menjadi SplitDocument (@since 1.9.0)
PageRangecontains(int $page), count(), toArray()Value object rentang halaman berbasis-1
MergeResult / SplitResultisValid(), count(), document(), totalOutputSize()Objek hasil komposisi
VendorExtensionRegistrypendaftaran ekstensiRegistry developer-extensions (@since 2.2.0)
ExtensionsDictionarywithEntry(), entries(), isEmpty(), toPdfDictionary()Builder extensions-dictionary yang tidak dapat diubah (@since 2.0.0)
CollectionDictionarytoPdfDictionary()Entri katalog portable-collection (@since 2.0.0)

Jalankan composer docs:generate-api-php -- --module=Document untuk menghasilkan tabel PHPDoc lengkap.

Pisahkan sebuah PDF menjadi dokumen satu halaman, lalu periksa hasilnya.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Document\PageRange;
use NextPDF\Document\PdfSplitter;
$splitter = new PdfSplitter();
$result = $splitter->splitEvery(file_get_contents('/srv/in/report.pdf'), 1);
foreach (range(0, $result->count() - 1) as $index) {
$segment = $result->document($index);
file_put_contents("/srv/out/page-{$index}.pdf", $segment->pdfData);
}
$singlePage = $splitter->extractPages(
file_get_contents('/srv/in/report.pdf'),
new PageRange(2, 4),
);

Gabungkan beberapa PDF dengan anggaran masukan yang eksplisit, lalu validasi hasilnya sebelum menulis keluaran gabungan.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Document\PdfMerger;
use NextPDF\Exception\PageLayoutException;
/** @var list<string> $sources Raw PDF byte strings to combine. */
$sources = array_map(
static fn (string $path): string => file_get_contents($path),
glob('/srv/batch/*.pdf') ?: [],
);
$merger = new PdfMerger();
try {
// Bound the merge: at most 50 files, 100 MB total.
$merged = $merger->merge($sources, maxFiles: 50, maxTotalBytes: 100_000_000);
} catch (PageLayoutException $e) {
throw new \RuntimeException('Merge rejected: empty or invalid input set.', previous: $e);
}
if (!$merged->isValid()) {
throw new \RuntimeException('Merged document failed structural validation.');
}
file_put_contents('/srv/out/combined.pdf', $merged->pdfData);
  • PdfMerger::merge() dan PdfSplitter::split() memberlakukan batasan masukan melalui ResourceGuard. Jika masukan berisi terlalu banyak berkas atau terlalu banyak byte, metode ini memunculkan exception alih-alih memangkasnya secara diam-diam. Tetapkan maxFiles / maxTotalBytes dengan sengaja sesuai beban kerja Anda.
  • Daftar berkas kosong atau daftar rentang kosong memunculkan PageLayoutException. Perlakukan ini sebagai kesalahan konfigurasi, bukan hasil kosong.
  • PageRange berbasis-1 dan inklusif. Sebuah DPart daun memiliki daftar pages berupa indeks halaman berbasis-0. Kedua abstraksi tersebut menggunakan basis indeks yang berbeda. Konversikan secara eksplisit ketika Anda berpindah di antara keduanya.
  • DPart bersifat readonly. Untuk membangun tree yang berbeda, buat node baru alih-alih mengubah node yang sudah ada. resolveWithPageObjects() mengembalikan bentuk cadangan indeks bilangan bulat hanya ketika peta page-object kosong. Jangan mengandalkan jalur tersebut pada keluaran produksi.
  • VendorExtensionRegistry memunculkan VendorExtensionRegistryConflictException untuk prefiks vendor yang duplikat. Daftarkan setiap prefiks satu kali saja.

Pemisahan dan penggabungan berskala linear terhadap jumlah halaman dan didominasi oleh penguraian serta penomoran ulang objek, bukan oleh pencatatan internal modul itu sendiri. Beban kerja referensi standar masuk dalam anggaran 1500 ms wall / 64 MB puncak. Penggabungan berukuran besar terutama dibatasi oleh total byte masukan. Guard maxTotalBytes menjaga memori puncak tetap terbatas. Profil reproduktivitasnya adalah structural: PDF yang digabung atau dipisah membawa trailer dan /ID yang baru, sehingga dua kali eksekusi setara secara struktural tetapi tidak identik secara byte.

PdfMerger::merge() dan PdfSplitter::split() mengonsumsi byte PDF yang tidak tepercaya. Sebelum penguraian, keduanya melewatkan masukan melalui ResourceGuard::assertSize() / assertCount(), yang membatasi denial of service akibat amplifikasi dekompresi atau jumlah objek. Tetapkan argumen maxFiles, maxTotalBytes, dan maxBytes secara ketat untuk deployment, alih-alih mengandalkan nilai standar. Perlakukan setiap PDF masukan sebagai berbahaya. Ketika sumber berasal dari pengguna, jalankan komposisi batch di worker yang terbatas. Lihat model ancaman engine di /modules/core/security/ untuk batas kepercayaan.

DPart tree yang dibangun modul ini mengikuti model Document Part dalam ISO 32000-2 §14.12, dengan entri /Start dan /End daun yang dipancarkan sebagai referensi tidak langsung ke objek halaman di bawah klausa yang sama. Keluaran gabungan menggunakan struktur node page-tree yang didefinisikan dalam §7.7.3. Hal ini merupakan fakta implementasi yang dihasilkan oleh src/Document/ dan diuji oleh tests/Unit/Document/ (DPartTest, DPartRootTest, DPartPageRefTest, DocumentPdfMergerDeepTest, DocumentPageRangeParseDeepTest). Ini bukan pernyataan kesesuaian PDF 2.0 secara menyeluruh. Kesesuaian dokumen secara penuh divalidasi oleh oracle dan golden suite yang dijelaskan dalam /modules/core/conformance/.