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.
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:
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:
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:
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:
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:
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:
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#
- Importing — reading a single file or byte stream in as a
Document - Modifying Imported Pages — adding content to a page after it comes out of a merge
- Opening Encrypted PDFs — the
$passwordsarray, for sources that need one - Saving & Output — writing a
Documentto a file, the step every pattern here relies on