A modern PHP package to combine multiple PDF files with page-level granularity. Built on TCPDF and FPDI for reliable rendering. Supports Laravel auto-discovery (10.x, 11.x, 12.x) and standalone usage.
- PHP
^8.2 - Composer
Under the hood the package depends on tecnickcom/tcpdf and setasign/fpdi — both are pulled in automatically.
composer require tudorr89/pdfcombinerIf you use Laravel ≥ 5.5 the package registers itself via auto-discovery. No manual setup required.
Publish the config (optional):
php artisan vendor:publish --tag=pdfcombine-configrequire 'vendor/autoload.php';
use PdfCombine\PDFCombiner;
$combiner = new PDFCombiner;$combiner = new \PdfCombine\PDFCombiner();
$combiner
->addFile('/path/to/report.pdf')
->addFile('/path/to/appendix.pdf')
->save('/path/to/combined.pdf');$combiner
->addFile('/path/to/large.pdf', [1, 5, 7]) // pages 1, 5, 7
->addFile('/path/to/summary.pdf') // all pages
->save('/path/to/result.pdf');$combiner
->addFile('/path/to/doc.pdf')
->addRaw($pdfBinaryString, 'attachment.pdf')
->download('final-report.pdf');$content = $combiner
->addFile('/path/to/doc.pdf')
->blob();
// or
$content = $combiner->stream();use PdfCombine\Contracts\PDFCombinerInterface;
class ReportController
{
public function combine(PDFCombinerInterface $combiner)
{
$combiner
->addFile(storage_path('reports/january.pdf'))
->addFile(storage_path('reports/february.pdf'))
->save(storage_path('reports/q1.pdf'));
return response()->download(storage_path('reports/q1.pdf'));
}
}use PDFCombine;
PDFCombine::addFile('/path/to/a.pdf')
->addFile('/path/to/b.pdf')
->download('combined.pdf');Add a PDF file from disk.
$path— absolute or relative path to a.pdffile$pages— optional array of page numbers to include (1-indexed). Empty array = all pages.
$combiner->addFile('/path/to/doc.pdf'); // all pages
$combiner->addFile('/path/to/doc.pdf', [1, 3]); // pages 1 and 3 only
$combiner->addFile('/path/to/doc.pdf', range(5, 10)); // pages 5 through 10Add multiple files at once. Accepts plain paths or [path, pages] tuples.
$combiner->addFiles([
'/path/to/cover.pdf',
['/path/to/body.pdf', [1, 2, 3]],
'/path/to/back.pdf',
]);Add PDF content from a raw binary string (e.g. from an HTTP response, database BLOB, or upload stream).
$rawPdf = file_get_contents('https://example.com/remote.pdf');
$combiner->addRaw($rawPdf, 'remote.pdf');Combine all added files and write the result to $outputPath. Returns true on success.
$combiner->addFile('a.pdf')->addFile('b.pdf')->save('combined.pdf');Send the combined PDF to the browser as a download (sends Content-Disposition: attachment headers + body). Does not call exit — middleware and terminating callbacks still run.
$combiner->download('quarterly-report.pdf');In Laravel, prefer streaming the body yourself so you stay on the full response pipeline:
return response()->streamDownload( fn () => print($combiner->stream()), 'quarterly-report.pdf', ['Content-Type' => 'application/pdf'], );
Return the combined PDF as a raw binary string.
$pdf = $combiner->stream();
// store in S3, send via email attachment, etc.Alias for stream().
Return the total number of pages that would be included in the output (honours page-range filters).
$combiner
->addFile('doc.pdf', [2, 4])
->addFile('summary.pdf');
echo $combiner->getPageCount(); // 2 + all pages of summary.pdfReturn the number of files currently queued.
Clear all queued files. Preserves orientation / unit / paper size so the same configured instance can be reused.
$combiner->reset();Restore orientation / unit / paper size to their defaults (P / mm / A4). Does not touch the file queue.
$combiner->resetConfig();use PdfCombine\Contracts\PDFCombinerInterface;
$combiner->withOrientation(PDFCombinerInterface::ORIENTATION_PORTRAIT); // 'P'
$combiner->withOrientation(PDFCombinerInterface::ORIENTATION_LANDSCAPE); // 'L'$combiner->withUnit(PDFCombinerInterface::UNIT_MM);
$combiner->withUnit(PDFCombinerInterface::UNIT_CM);
$combiner->withUnit(PDFCombinerInterface::UNIT_IN);
$combiner->withUnit(PDFCombinerInterface::UNIT_PT);// Named constants
$combiner->withPaperSize(PDFCombinerInterface::SIZE_A4);
$combiner->withPaperSize(PDFCombinerInterface::SIZE_A5);
$combiner->withPaperSize(PDFCombinerInterface::SIZE_A6);
$combiner->withPaperSize(PDFCombinerInterface::SIZE_LETTER);
$combiner->withPaperSize(PDFCombinerInterface::SIZE_LEGAL);
// Or any TCPDF-compatible format string
$combiner->withPaperSize('A3');
$combiner->withPaperSize('Legal');
// Or custom [width, height] array (in the unit defined above)
$combiner->withPaperSize([210, 297]); // A4 in mmreturn [
'orientation' => env('PDFCOMBINE_ORIENTATION', 'P'),
'unit' => env('PDFCOMBINE_UNIT', 'mm'),
'paper_size' => env('PDFCOMBINE_PAPER_SIZE', 'A4'),
];Environment variables override the defaults:
PDFCOMBINE_ORIENTATION=L
PDFCOMBINE_UNIT=in
PDFCOMBINE_PAPER_SIZE=LetterAll setter and adder methods return $this, so you can chain calls:
$content = $combiner
->withOrientation(PDFCombinerInterface::ORIENTATION_LANDSCAPE)
->withPaperSize(PDFCombinerInterface::SIZE_A3)
->addFile('cover.pdf')
->addFile('body.pdf', range(2, 10))
->addFile('back.pdf')
->stream();The package throws typed exceptions from PdfCombine\Exceptions\PDFCombineException:
| Method | Exception message |
|---|---|
| File not found | PDF file not found at path: ... |
| Invalid / unreadable PDF | The file at '...' is not a valid PDF or could not be read. |
| No files added | No PDF files have been added to combine. |
| Save failed | Failed to save the combined PDF to: ... |
| Page out of range | Requested page X is out of range. The document has Y pages. |
use PdfCombine\Exceptions\PDFCombineException;
try {
$combiner->addFile('/missing.pdf')->save('/out.pdf');
} catch (PDFCombineException $e) {
logger()->error('PDF combine failed: ' . $e->getMessage());
}See the releases page on GitHub.
MIT — see the LICENSE file for details.