Skip to content
getnextpdf.com

Pro edition

Merge

NextPDF\Pro\Merge\SmartMerger merges several PDFs into one, then applies Pro enhancements: a consolidated bookmark tree from per-input labels, content-hash page deduplication, and per-input page-range selection. Base document assembly runs through the Pro object-graph merge engine. It renumbers every input into one object space and writes a real cross-reference table.

This capability ships in NextPDF Pro (nextpdf/pro) and activates with a Pro-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.

The Merge classes are available whenever the Pro package is installed. No runtime capability flag gates this module.

Terminal window
composer require nextpdf/pro:^3

SmartMerger accepts a list of MergeInput value objects. Each input carries the source PDF bytes, an optional list of page ranges, and an optional label. Inputs with page ranges are reduced to the selected pages before merging. The combined document is produced by the Pro object-graph merge engine, which renumbers every input into one contiguous object space and emits a real cross-reference table; the Pro layer then adds the requested enhancements.

SmartMergeConfig controls the enhancements:

  • Bookmark consolidation inserts one outline entry per labeled input, pointing at the start of that input’s section. This follows the document catalog /Outlines model in ISO 32000-2:2020 §7.7.2.
  • Page deduplication removes byte-identical duplicate pages across inputs, compared by content hash.
  • Link rewriting scans for internal GoTo actions in the merged output.

SmartMergeResult reports the merged bytes plus statistics: total pages, source count, output size, bookmarks added, duplicates removed, links detected, and the ordered input labels.

Merging PDFs is not byte concatenation: each input carries its own object numbers, cross-reference table, and page tree, so a naive splice loads in no conforming reader. SmartMerger therefore delegates base assembly to the Pro object-graph engine (PdfSplitter::mergeDocuments()), which renumbers every input into one contiguous object space, rebuilds a single page tree, and emits a real cross-reference table with true byte offsets. The Pro enhancements — bookmark consolidation, deduplication, and link detection — then layer on that verified output rather than reimplementing assembly. Whole-document deduplication and detection-only link handling are deliberate scope boundaries that keep the merge deterministic and safe on untrusted input.

Design background: The anatomy of a PDF file.

  • Input. A non-empty list of MergeInput. An empty list raises InvalidArgumentException. Input count and per-input byte size are bounded by SmartMergeConfig (maxInputs, maxBytesPerInput).
  • Output. A SmartMergeResult. isValid() is true when the output begins with the %PDF header.
  • Bookmark consolidation adds one entry per input that has a non-empty label, when consolidateBookmarks is enabled.
  • Deduplication is opt-in (deduplicatePages, default off) and matches whole pages by content hash, not visually similar pages.
  • Link rewriting in the current release detects and counts internal GoTo actions; it does not perform full cross-document destination re-resolution. Treat linksRewritten as a detection count.
  • Determinism. For identical inputs and configuration the merged byte stream is stable, subject to the Pro merge engine’s documented determinism profile.
TypeKindKey members
NextPDF\Pro\Merge\SmartMergerfinal class__construct(?PdfMerger $coreMerger = null, ?PdfSplitter $splitter = null), merge(array $inputs, SmartMergeConfig $config = new SmartMergeConfig()): SmartMergeResult
NextPDF\Pro\Merge\MergeInputfinal readonly class__construct(string $pdfData, array $pageRanges = [], string $label = ''), hasPageRanges(): bool
NextPDF\Pro\Merge\SmartMergeConfigfinal readonly class__construct(bool $consolidateBookmarks = true, bool $deduplicatePages = false, bool $rewriteLinks = true, int $maxInputs = 100, int $maxBytesPerInput = 100_000_000), default(), basic()
NextPDF\Pro\Merge\SmartMergeResultfinal readonly classstring $pdfData, int $totalPages, int $sourceCount, int $bookmarksAdded, int $duplicatesRemoved, int $linksRewritten, array $inputLabels, isValid(): bool, hasOptimizations(): bool
<?php
declare(strict_types=1);
use NextPDF\Pro\Merge\MergeInput;
use NextPDF\Pro\Merge\SmartMerger;
$result = (new SmartMerger())->merge([
new MergeInput(file_get_contents('cover.pdf'), label: 'Cover'),
new MergeInput(file_get_contents('body.pdf'), label: 'Body'),
]);
echo $result->totalPages, " pages, ",
$result->bookmarksAdded, " bookmarks\n";
<?php
declare(strict_types=1);
use NextPDF\Pro\Merge\MergeInput;
use NextPDF\Pro\Merge\SmartMergeConfig;
use NextPDF\Pro\Merge\SmartMerger;
function assemblePacket(array $sections): string
{
$inputs = [];
foreach ($sections as $label => $bytes) {
$inputs[] = new MergeInput($bytes, label: (string) $label);
}
$config = new SmartMergeConfig(
consolidateBookmarks: true,
deduplicatePages: true,
rewriteLinks: false,
maxInputs: 50,
);
$result = (new SmartMerger())->merge($inputs, $config);
if (! $result->isValid()) {
throw new RuntimeException('merge produced invalid output');
}
return $result->pdfData;
}
  • A single input is valid and merges to a normalized copy of that document.
  • Deduplication compares whole-page byte content; pages that differ only by metadata or object numbering are not treated as duplicates.
  • Page-range selection on an input is applied before merge ordering.
  • linksRewritten is a detected-action count, not a guarantee that every cross-document link target was re-pointed.

Cost is dominated by the Pro merge engine and scales with total input bytes and page count. Deduplication adds one content hash per page. The performance_budget front-matter is the per-merge reference.

Input count and per-input size are bounded by SmartMergeConfig to limit resource exhaustion from hostile inputs. Merging does not execute embedded document scripts. See the Core security model for byte-stream parsing hardening.

ClaimSpec clauseStatus
Consolidated bookmarks via /OutlinesISO 32000-2:2020 §7.7.2Verified (unit suite)
Content-hash page deduplicationVerified (unit suite)
Full cross-document link re-resolutionNot supported (detection only)

For basic concatenation without Pro consolidation, the open-source Core NextPDF\Document\PdfMerger is the supported standalone path. SmartMerger does not delegate to it; the Pro merge runs on its own object-graph engine. See /modules/core/document/.

This module performs structural merging. It does not perform legal-hold assembly, redaction, or evidentiary chain-of-custody packaging; those are not provided here.

This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.