Document: DParts, splitsen / samenvoegen en leveranciersuitbreidingen
In het kort
Sectie met titel “In het kort”De Document-module werkt met volledige Portable Document Format-bestanden (PDF), niet met pagina-inhoud. De module bouwt de Document Part-hiërarchie op waarmee gereguleerde workflows metadata koppelen. De module splitst een PDF in segmenten op basis van paginabereik, voegt meerdere PDF’s samen tot één bestand en registreert developer-extensies in de documentcatalogus.
Installeren
Sectie met titel “Installeren”composer require nextpdf/core:^3Conceptueel overzicht
Sectie met titel “Conceptueel overzicht”Deze module werkt boven het niveau van pagina-inhoud. Waar Graphics en Content operatoren uitzenden, werkt Document op structureel niveau: paginabomen, de documentcatalogus en de Document Part-boom.
Een Document Part (DPart) is een logische partitie van een PDF. ISO 32000-2 definieert een DPart-hiërarchie waarvan de knooppunten Document Part Metadata (DPM) bevatten. Een gereguleerde workflow, bijvoorbeeld voor farmaceutische, juridische of archiveringsprocessen, kan metadata koppelen aan een reeks pagina’s in plaats van aan het hele bestand — §14.12. DPart is een onveranderbaar readonly-knooppunt: een blad verwijst naar een aaneengesloten reeks pagina-indexen, en een tussenliggend knooppunt groepeert onderliggende DPart-knooppunten tot een boom. DPartRoot is de wortel van de boom die de Writer serialiseert. De /Start- en /End-vermeldingen van een bladknooppunt zijn indirecte verwijzingen naar pagina-objecten, geen numerieke pagina-indexen — §14.12. DPart::resolveWithPageObjects() lost die vermeldingen op aan de hand van een door de writer aangeleverde mapping van pagina-index→objectnummer en geeft de verwijzingsvorm /Start (en optioneel /End) terug. De methode gebruikt de integervorm alleen als fallback op testpaden waar de mapping niet beschikbaar is.
PdfMerger en PdfSplitter vormen het oppervlak voor documentsamenstelling. PdfMerger combineert pagina-objecten uit meerdere invoer-PDF’s, hernummert objecten om botsingen te voorkomen en herbouwt één paginaboom en cross-reference-tabel. De paginaboom die de class produceert, is een gebalanceerd Pages-knooppunt met Kids en Count, plus het overerfbare attribuutmodel dat PDF definieert voor paginaboomknooppunten — §7.7.3. PdfSplitter doet het omgekeerde: de class extraheert paginabereiken naar zelfstandige SplitDocument-objecten. PageRange is het waardeobject dat door beide classes wordt gebruikt. Het is 1-gebaseerd, valideert zijn grenzen en biedt contains(), count() en toArray().
VendorExtensionRegistry, ExtensionsDictionary en DeveloperExtensionEntry modelleren de developer-extensions-dictionary in de documentcatalogus. Een engine gebruikt die dictionary om een leveranciersuitbreidingsniveau te declareren dat verder gaat dan de basisspecificatie. Het register weigert conflicterende herregistratie van hetzelfde leveranciersprefix met VendorExtensionRegistryConflictException. CollectionDictionary en CollectionSort modelleren een catalogusvermelding voor een PDF-collection (portable collection of portfolio).
API-oppervlak
Sectie met titel “API-oppervlak”| Class | Belangrijkste methoden | Rol |
|---|---|---|
DPart | isLeaf(), hasMetadata(), resolveWithPageObjects(), write() | Onveranderbaar Document Part-knooppunt (@since 1.12.0) |
DPartRoot | isEmpty(), write() | Wortel van de DPart-boom die de Writer serialiseert (@since 1.12.0) |
PdfMerger | merge(array $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000), append() | Samenvoegen van meerdere PDF’s met objecthernummering (@since 1.9.0) |
PdfSplitter | split(), splitEvery(), extractPages() | Splitsing op paginabereik naar SplitDocument (@since 1.9.0) |
PageRange | contains(int $page), count(), toArray() | 1-gebaseerd waardeobject voor paginabereik |
MergeResult / SplitResult | isValid(), count(), document(), totalOutputSize() | Resultaatobjecten van samenstelling |
VendorExtensionRegistry | registratie van uitbreidingen | Register van developer-extensions (@since 2.2.0) |
ExtensionsDictionary | withEntry(), entries(), isEmpty(), toPdfDictionary() | Onveranderbare builder voor de extensions-dictionary (@since 2.0.0) |
CollectionDictionary | toPdfDictionary() | Catalogusvermelding voor portable collection (@since 2.0.0) |
Voer composer docs:generate-api-php -- --module=Document uit om de volledige PHPDoc-tabel te genereren.
Codevoorbeeld — snelstart
Sectie met titel “Codevoorbeeld — snelstart”Splits een PDF in documenten van één pagina en inspecteer vervolgens het resultaat.
<?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),);Codevoorbeeld — productie
Sectie met titel “Codevoorbeeld — productie”Voeg meerdere PDF’s samen binnen een expliciet invoerbudget en valideer vervolgens het resultaat voordat de gecombineerde uitvoer wordt weggeschreven.
<?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);Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”PdfMerger::merge()enPdfSplitter::split()dwingen invoergrenzen af viaResourceGuard. Invoer met te veel bestanden of te veel bytes werpt een exception in plaats van stilzwijgend af te kappen. KiesmaxFiles/maxTotalBytesbewust voor de werklast.- Een lege bestandenlijst of lege bereikenlijst werpt
PageLayoutException. Behandel deze als configuratiefouten, niet als lege resultaten. PageRangeis 1-gebaseerd en inclusief. Bij een blad-DPartbevat depages-lijst 0-gebaseerde pagina-indexen. De twee abstracties gebruiken verschillende indexbases. Converteer expliciet bij het wisselen tussen beide.DPartisreadonly. Construeer nieuwe knooppunten om een andere boom te bouwen, in plaats van een bestaand knooppunt te muteren.resolveWithPageObjects()geeft alleen de fallbackvorm met integerindex terug wanneer de pagina-objectmapping leeg is. Vertrouw niet op dat pad voor productie-uitvoer.VendorExtensionRegistrywerptVendorExtensionRegistryConflictExceptionbij een dubbel leveranciersprefix. Registreer elk prefix eenmaal.
Prestaties
Sectie met titel “Prestaties”Splitsen en samenvoegen schalen lineair met het aantal pagina’s en worden gedomineerd door parsing en objecthernummering, niet door de interne administratie van de module. De standaardreferentiewerklast past binnen een budget van 1500 ms wandkloktijd / 64 MB piek. Grote samenvoegingen worden vooral beperkt door het totale aantal invoerbytes. De maxTotalBytes-bewaking houdt het piekgeheugen begrensd. Het reproduceerbaarheidsprofiel is structural: een samengevoegde of gesplitste PDF krijgt een nieuwe trailer en /ID, zodat twee uitvoeringen structureel gelijk maar niet byte-identiek zijn.
Beveiligingsnotities
Sectie met titel “Beveiligingsnotities”PdfMerger::merge() en PdfSplitter::split() verwerken niet-vertrouwde PDF-bytes. Vóór het parsen leiden beide de invoer via ResourceGuard::assertSize() / assertCount(), die een denial of service door decompressie- of objectaantalsversterking begrenst. Stem de argumenten maxFiles, maxTotalBytes en maxBytes strak af op de deployment in plaats van te vertrouwen op de standaardwaarden. Behandel elke invoer-PDF als vijandig. Wanneer bronnen door de gebruiker worden aangeleverd, voer batchsamenstelling dan uit in een begrensde worker. Raadpleeg het dreigingsmodel van de engine in /modules/core/security/ voor de vertrouwensgrens.
Conformiteit
Sectie met titel “Conformiteit”De DPart-boom die deze module bouwt, volgt het Document Part-model in ISO 32000-2 §14.12, waarbij blad-/Start- en /End-vermeldingen volgens dezelfde clausule worden weggeschreven als indirecte verwijzingen naar pagina-objecten. Samengevoegde uitvoer gebruikt de structuur voor paginaboomknooppunten die is gedefinieerd in §7.7.3. Dit zijn implementatiefeiten die worden geproduceerd door src/Document/ en getest door tests/Unit/Document/ (DPartTest, DPartRootTest, DPartPageRefTest, DocumentPdfMergerDeepTest, DocumentPageRangeParseDeepTest). Ze vormen geen verklaring van end-to-end PDF 2.0-conformiteit. Volledige documentconformiteit wordt gevalideerd door de oracle- en golden-suites beschreven in /modules/core/conformance/.
Zie ook
Sectie met titel “Zie ook”- Core-module
- Writer-module — serialiseert de DPart-boom en paginaboom.
- Metadata-module — Extensible Metadata Platform (XMP) dat samengaat met DPM.
- Navigation-module
- Conformiteitsoverzicht
- Beveiligingsmodel van de engine