I was working on a client project where we needed to process uploaded PDFs before sending them to an AI model. The documents were multi-page city directory scans, and the AI worked best when fed individual pages rather than entire documents. Simple enough, I just needed to split PDFs.
So I started looking for a Laravel package. I found a few options, but nothing that felt right.
The Problem with PDF in the Laravel Ecosystem
The Laravel ecosystem offers excellent packages for generating PDFs, such as Spatie's laravel-pdf. However, when it comes to manipulating existing PDFs (e.g. splitting, merging, extracting pages or adding watermarks), the story is different.
Some of this can be done with existing tools. For example, FPDI can combine pages, and you can wrap CLI tools like qpdf or Ghostscript to split documents. But that leaves you writing all the integration code yourself. There's no filesystem disk support, no service container integration, and no fluent API.
I wanted something with a facade, chainable methods, and proper Storage integration. Something that feels like writing Eloquent queries, not fighting a CLI tool.
Meet Collate
Collate is a Laravel package for PDF manipulation, powered by qpdf under the hood. Here's what my original problem looks like with it:
use Johind\Collate\Facades\Collate;
$pages = Collate::open($request->file('directory-scan'))
->split('ai-processing/scan-page-{page}.pdf');
// $pages โ Collection ['ai-processing/scan-page-1.pdf', ...]
// Now feed each page to your AI pipeline
That's it. The uploaded file is handled, pages are split, temp files are cleaned up automatically, and you get a collection of paths back.
But once I had the foundation in place, I kept going. I felt like this was missing in the ecosystem, and honestly it was just fun to build, so it grew into a full toolkit.
What Else Can You Do?
Merge multiple documents:
Collate::merge('cover.pdf', 'report.pdf', 'appendix.pdf')
->save('complete-report.pdf');
Watermark a document with an overlay:
Collate::open('proposal.pdf')
->overlay('branding/confidential-stamp.pdf')
->save('stamped-proposal.pdf');
Chain a whole workflow together. Add standard terms, set metadata, encrypt, and push to S3 in one go:
Collate::open($request->file('document'))
->addPages('legal/standard-terms.pdf')
->withMetadata(title: 'Client Report 2025')
->encrypt('client-password')
->toDisk('s3')
->save('reports/final.pdf');
Extract or remove specific pages:
Collate::open('document.pdf')
->onlyPages([1, 2, 3])
->save('first-three-pages.pdf');
Collate::open('document.pdf')
->removePages('5-10')
->save('trimmed.pdf');
Rotate pages, flatten forms, optimize for web:
Collate::open('scanned.pdf')
->rotate(90, range: '1-3')
->flatten()
->linearize()
->save('cleaned-up.pdf');
Read metadata and page counts without modifying anything:
$meta = Collate::inspect('document.pdf')->metadata();
$meta->title; // 'Quarterly Report'
$meta->author; // 'Taylor Otwell'
$count = Collate::inspect('document.pdf')->pageCount();
Everything chains.
Built for Laravel
A few things that make it feel native:
Storage disk integration. Read from S3, write to local, or the other way around. Collate handles the temp file dance for remote disks transparently:
Collate::fromDisk('s3')
->open('uploads/contract.pdf')
->toDisk('local')
->save('processed/contract.pdf');
It's Responsable. Return a PendingCollate directly from a controller and Laravel streams it to the browser. There's also download() and stream() if you need more control:
public function show()
{
return Collate::open('invoice.pdf');
}
Conditional operations via Laravel's Conditionable trait:
Collate::open('document.pdf')
->when($request->boolean('watermark'), fn ($pdf) => $pdf->overlay('watermark.pdf'))
->when($request->boolean('flatten'), fn ($pdf) => $pdf->flatten())
->save('output.pdf');
Macros for your own reusable operations:
PendingCollate::macro('stamp', function () {
return $this->overlay('assets/stamp.pdf');
});
Collate::open('contract.pdf')->stamp()->save('stamped.pdf');
Requirements
Collate requires PHP 8.4+, Laravel 11 or 12, and qpdf v11.0.0+ installed on your system. Installation is straightforward:
composer require johind/collate
php artisan collate:install
The install command publishes the config and verifies that qpdf is available.
Wrapping Up
I built Collate because I needed it for a real project and couldn't find something that felt at home in Laravel. What started as a PDF splitter turned into something I think the ecosystem was missing. The package ships with a full test suite, so you can trust it in production.
If you work with existing PDFs in Laravel, such as merging or splitting documents for processing, adding watermarks, encrypting or optimising them for the web, give it a try. The full list of operations can be found in the README.
I'm actively working on the package, so feedback and issues are very welcome.
Top comments (0)