Pop PDF
Import

Merging

Pdf::merge() reads several complete PDF files and hands back a single Document holding every page from every one of them — no external tool, only the same reader importFromFile() uses, run once per source.

Merging Files#

merge($files, $document = new Document(), $passwords = []) takes an array of paths and returns their combined pages as one Document. Every page of every source comes along — merge() has no $pages argument the way importFromFile() does. So, either unwanted pages should be pulled out before merge() is called, or pulled out via the $document->deletePage() method after the merge() call.

PHP
use Pop\Pdf\Pdf;

$combined = Pdf::merge([__DIR__ . '/report.pdf', __DIR__ . '/gallery.pdf']);
$combined->getNumberOfPages(); // 5 pages total — 3 from report.pdf, 2 from gallery.pdf

Merging needs at least two sources — a single file has nothing to combine it with, and merge() raises Pop\Pdf\Build\Exception rather than handing back a copy of the one file given:

PHP
use Pop\Pdf\Pdf;
use Pop\Pdf\Build\Exception;

try {
    Pdf::merge([__DIR__ . '/report.pdf']);
} catch (Exception $exception) {
    echo $exception->getMessage();
    // Error: Merging requires at least 2 source PDF documents.
}

A path in the array that does not exist raises the same exception type, naming the missing file directly, so a batch job merging a list of paths can catch one exception type for both problems.

$files isn't limited to two entries — every source in the array contributes its pages, in order:

PHP
use Pop\Pdf\Pdf;

$three = Pdf::merge([__DIR__ . '/cover.pdf', __DIR__ . '/report.pdf', __DIR__ . '/gallery.pdf']);
$three->getNumberOfPages(); // 6 — 1 + 3 + 2

Page Order

Pages come out in source order: every page of the first file in $files, in its own original order, then every page of the second, and so on.

Merging Raw Data#

mergeRawData($data, $document = new Document(), $passwords = []) is merge() for sources already held as bytes rather than files on disk — an array of raw PDF strings in place of an array of paths:

PHP
use Pop\Pdf\Pdf;

$combined = Pdf::mergeRawData([
    file_get_contents(__DIR__ . '/report.pdf'),
    file_get_contents(__DIR__ . '/gallery.pdf'),
]);

$combined->getNumberOfPages(); // 5

The same two-source minimum applies, raising the identical exception when only one data string is given. Reach for mergeRawData() when the documents being combined arrive as bytes already in memory — several uploads handled in the same request, or PDFs pulled back from storage — so nothing has to touch the filesystem before the merge does its work.

Merging Into an Existing Document#

merge() and mergeRawData() both take a Document as a second argument. Pass one and the merged pages are appended to it rather than to a document created fresh — the same instance comes back, still carrying whatever fonts, metadata and pages it already had:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Font;
use Pop\Pdf\Pdf;

$document = new Document();
$document->addFont(Font::ARIAL);

$combined = Pdf::merge([__DIR__ . '/cover.pdf', __DIR__ . '/report.pdf'], document: $document);

$combined === $document;        // true — the same instance, not a copy
$combined->getAvailableFonts(); // ['Arial'] — a merge with no starter comes back empty
$combined->getNumberOfPages();  // 4

A starter document's own pages lead the result, whatever order things happened in — the merged sources follow behind them:

PHP
use Pop\Pdf\Document;
use Pop\Pdf\Document\Font;
use Pop\Pdf\Document\Page;
use Pop\Pdf\Document\Page\Text;
use Pop\Pdf\Pdf;

$starter = new Document(new Page(Page::LETTER));
$starter->addFont(Font::HELVETICA_BOLD);
$starter->getPage(1)->addText(new Text('Cover Sheet', size: 28), 'Helvetica-Bold', x: 72, y: 700);

$combined = Pdf::merge([__DIR__ . '/report.pdf', __DIR__ . '/gallery.pdf'], document: $starter);
$combined->getNumberOfPages(); // 6 — the starter's own page, then report.pdf's 3, then gallery.pdf's 2

The two-source minimum counts the files being merged, not the starter, so a starter document plus a single file raises the same exception a lone file does.

A merge also chains onto a previous one by writing the result out and passing that path alongside the next batch of files. Nothing about merge() distinguishes a source that started life as a plain document, an import, or the result of an earlier merge — a valid PDF file is a valid source either way:

PHP
use Pop\Pdf\Pdf;

$combined = Pdf::merge([__DIR__ . '/cover.pdf', __DIR__ . '/report.pdf']);
Pdf::writeToFile($combined, filename: __DIR__ . '/combined.pdf');

$again = Pdf::merge([__DIR__ . '/combined.pdf', __DIR__ . '/gallery.pdf']);
$again->getNumberOfPages(); // 6 — the earlier 4-page merge plus gallery.pdf's 2

Reading a document in for a merge never modifies the source file either, the same as importFromFile()cover.pdf, report.pdf and gallery.pdf are each read fresh every time they appear in a $files array, however many merges reference them.

See Also#