Externe PDF's samenvoegen of pagina's uit bestaande documenten toevoegen
In één oogopslag
Sectie met titel “In één oogopslag”Je hebt verschillende PDF-bestanden op schijf staan en hebt één PDF nodig. Dit recipe combineert bestaande documenten van begin tot eind met de samenvoeg-interface van Core, NextPDF\Document\PdfMerger. Je geeft ruwe PDF-bytereeksen door. De merger hernummert elk object om botsingen te voorkomen, bouwt één paginaboom en één kruisverwijzingstabel, en retourneert een NextPDF\Document\MergeResult die je naar schijf kunt schrijven of naar een client kunt streamen.
Dezelfde interface dekt de drie taken die je het vaakst nodig hebt:
- Voeg een geordende lijst PDF’s samen tot één document.
- Voeg een tweede PDF achter een basis-PDF toe.
- Plaats pagina’s vooraan door het nieuwe document als eerste in de invoervolgorde te zetten.
Het samenvoegen verloopt in-process, zonder headless browser of netwerkaanroep. Core moet geïnstalleerd zijn (composer require nextpdf/core:^3) en je hebt twee of meer leesbare PDF-bestanden nodig.
Installeren
Sectie met titel “Installeren”composer require nextpdf/core:^3Conceptueel overzicht
Sectie met titel “Conceptueel overzicht”Een PDF ordent pagina’s in een paginaboom met als wortel een /Pages-knooppunt en vindt elk indirect object via een kruisverwijzingstabel. Wanneer je twee brondocumenten combineert, overlappen hun objectnummers. Beide bestanden bevatten vrijwel altijd een object 1 0 obj, een /Catalog, en een /Pages-knooppunt. Als je alleen de bytes aan elkaar plakt, produceer je een beschadigd bestand omdat de verwijzingen niet langer wijzen naar de objecten die ze identificeren.
PdfMerger lost die overlap op. Het extraheert de pagina-objecten uit elke invoer, hernummert elk object naar één adresruimte, herschrijft de /Parent-verwijzing van elke pagina zodat die naar één samengevoegd /Pages-knooppunt wijst, en produceert één catalogus, één paginaboom en één trailer. De uitvoer is een structureel nieuw document, geen aan elkaar geplakte aaneenschakeling.
De volgorderegel is eenvoudig: pagina’s verschijnen in dezelfde volgorde als hun bronbestanden in de invoerlijst. Om achteraan toe te voegen, plaats je het basisdocument eerst. Om vooraan toe te voegen, plaats je het nieuwe document eerst. Er is geen aparte methode om vooraan toe te voegen, omdat de invoervolgorde de enige besturing is die je nodig hebt.
API-interface
Sectie met titel “API-interface”new NextPDF\Document\PdfMerger() biedt twee methoden.
merge(list<string> $pdfFiles, int $maxFiles = 100, int $maxTotalBytes = 200_000_000): MergeResultcombineert een geordende lijst ruwe PDF-bytereeksen. De twee begrenzende parameters beperken het aantal bestanden en de totale invoergrootte. Beide hebben veilige productiewaarden als standaard; scherp ze aan voor elke werklast.append(string $basePdf, string $appendPdf): MergeResultis een handige wrapper die precies twee documenten op volgorde samenvoegt. Het is gelijkwaardig aanmerge([$basePdf, $appendPdf]).
Beide methoden retourneren een NextPDF\Document\MergeResult, een readonly-object met $pdfData (de samengevoegde bytes), $totalPages, $sourceCount, $mergedSize, en de hulpmethode isValid(), die bevestigt dat de uitvoer begint met de %PDF-header.
Invoer bestaat uit ruwe bytereeksen, niet uit bestandspaden. Lees het bestand zelf in met file_get_contents(), of haal de bytes op uit objectopslag. Zo blijft de merger vrij van aannames over het bestandssysteem en kun je documenten samenvoegen die nooit op schijf belanden.
Als je één pagina uit een externe PDF wilt importeren als herbruikbaar Form XObject, bijvoorbeeld om een briefhoofdpagina achter gegenereerde inhoud te stempelen, gebruik dan het pakketoverstijgende importercontract NextPDF\Contracts\ImportedFormObjectInterface, geïmplementeerd door importers zoals nextpdf/artisan. Gebruik voor compositie van volledige documenten en volledige pagina’s de hier gedocumenteerde PdfMerger-interface.
Codevoorbeeld — Snelstart
Sectie met titel “Codevoorbeeld — Snelstart”Dit voorbeeld leest twee bestanden in en schrijft de samengevoegde uitvoer weg. Het laat foutafhandeling weg om de aanroepvorm te tonen; het productievoorbeeld hieronder voegt de volledige beveiligingen toe.
<?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);Codevoorbeeld — Productie
Sectie met titel “Codevoorbeeld — Productie”Dit zelfstandige programma bouwt twee kleine documenten in het geheugen, zodat het zonder extern bestand draait. Het voegt ze samen, valideert het resultaat en schrijft de uitvoer weg. Het vangt de twee uitzonderingen op die de samenvoeg-interface opwerpt en gooit elke uitzondering opnieuw met context in plaats van die te negeren. Vervang de invoer in het geheugen door je eigen file_get_contents()-leesbewerkingen of ophaalacties uit objectopslag, en koppel de uitvoer aan je response- of opslaglaag.
<?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)); }}Verwachte standaarduitvoer (het paginatotaal is de som van de paginatellingen van de bronnen, en de bytegrootte hangt af van de build):
Merged 3 source(s) into 4 page(s), <n> bytes.Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”- Invoer bestaat uit bytes, niet uit paden.
merge()neemt ruwe PDF-bytereeksen aan. Lees het bestand eerst in metfile_get_contents(). Als je een padstring doorgeeft, doorstaat de invoer de%PDF-headercontrole niet en wordtPageLayoutExceptionopgeworpen. - Volgorde is uitvoervolgorde. Pagina’s komen terecht in de volgorde waarin hun bronbestanden in de lijst staan. Er is geen methode om vooraan toe te voegen: plaats het nieuwe document eerst om het vooraan toe te voegen, of als laatste om het achteraan toe te voegen.
- Een lege lijst is een fout. Een lege
$pdfFileswerptPageLayoutExceptionop, geen leeg resultaat. Valideer de set voordat je de merger aanroept. - Elke invoer wordt vooraf gevalideerd. Elke vermelding mag niet leeg zijn en moet beginnen met
%PDF. De eerste mislukkende invoer werptPageLayoutExceptionop met de geschonden voorwaarde, en er wordt niets samengevoegd. - Grenzen leiden tot exceptions, niet tot afkappen. Het overschrijden van
maxFileswerpt op via de interne bronbewaking, en het overschrijden vanmaxTotalByteswerptWriterExceptionop. De merger laat nooit stilzwijgend bestanden vallen of kapt bytes af, dus stem beide grenzen af op je werklast. - Uitvoer is structureel nieuw, niet bytestabiel. Het samengevoegde document bevat een nieuwe catalogus, paginaboom en trailer. Twee uitvoeringen met dezelfde invoer zijn structureel gelijk, maar niet gegarandeerd byte-identiek. Daarom declareert dit recipe een
structural-reproduceerbaarheidsprofiel. - Annotaties op paginaniveau en gedeelde bronnen. Het samenvoegen stelt pagina-objecten samen tot één boom. Structuren op documentniveau die buiten de pagina-objecten in een bronbestand staan, worden niet meegenomen. Wanneer je één pagina als herbruikbare afbeelding met de bijbehorende bronnen wilt importeren, gebruik dan het pad via
ImportedFormObjectInterfacemet een importer zoalsnextpdf/artisan.
Prestaties
Sectie met titel “Prestaties”Het samenvoegen is lineair in het totale aantal pagina’s. Het parsen en hernummeren van objecten domineert het werk, niet de administratie van de merger zelf. Het piekgeheugen volgt het totale aantal invoerbytes, omdat elke bron als reeks in het geheugen wordt gehouden terwijl de uitvoer wordt samengesteld. De bewaking maxTotalBytes houdt die piek begrensd. Stel voor pijplijnen met hoog volume maxFiles en maxTotalBytes in op de kleinste waarden die je werklast nodig heeft, zodat een misvormde of te grote batch snel mislukt in plaats van het geheugen uit te putten. Een typische kleine samenvoeging blijft binnen een budget van 1500 ms kloktijd en 64 MB piekgeheugen.
Beveiligingsopmerkingen
Sectie met titel “Beveiligingsopmerkingen”Het samenvoegen verloopt in-process; er verlaten geen documentbytes de host en er wordt geen netwerkaanroep gedaan. Behandel elke externe PDF als niet-vertrouwde invoer:
- Houd de grenzen krap.
maxFilesenmaxTotalBytesvormen je eerste verdedigingslinie tegen denial-of-service-invoer. Stel ze voor elke interface die uploads accepteert in op je werkelijke plafond, niet op de royale standaardwaarden. - Valideer voordat je vertrouwt. Een geslaagde samenvoeging betekent dat de bytes zijn gecombineerd, niet dat de invoer veilig is. Laat niet-vertrouwde invoer eerst door de Core-inspector lopen. Zie Een PDF parsen en inspecteren voor een begrensde triagescan die versleuteling, handtekeningen en risicomarkeringen signaleert vóór zwaardere verwerking.
- Interpoleer gebruikersinvoer nooit in een pad. Dit recipe schrijft naar een vast pad of naar de side-channel van het cookbook. Leid uitvoerpaden af van door de server beheerde waarden, nooit van een aanvraagveld, om path traversal te voorkomen.
- Geen geheimen in het document. Neem geen inloggegevens, tokens of interne identificatiegegevens op in een samengevoegd document dat je aan een client retourneert.
Conformiteit
Sectie met titel “Conformiteit”Dit recipe doet zelf geen normatieve standaardclaim. Het stelt bestaande documenten samen via de samenvoeg-interface van Core en valideert het resultaat met de MergeResult::isValid()-headercontrole. Het paginaboommodel dat PdfMerger herbouwt, is de PDF 2.0-paginaboomstructuur die wordt beschreven in de referentie /modules/core/document/. Gebruik voor een structurele lezing van elk invoer- of uitvoerdocument, inclusief versie, paginatelling, versleuteling en handtekeningvlaggen, de Core-inspector die wordt gedocumenteerd in Een PDF parsen en inspecteren.
Zie ook
Sectie met titel “Zie ook”- Referentie van de Document-module — de volledige interface voor splitsen, samenvoegen en documentonderdelen.
- Een PDF parsen en inspecteren — triageer niet-vertrouwde invoer voordat je die samenvoegt.
- Uitzonderingsbewuste foutafhandeling — de NextPDF-uitzonderingshiërarchie achter
PageLayoutExceptionenWriterException. - Een document met meerdere pagina’s bouwen — stel de pagina’s samen die je vervolgens combineert.