dskripchenko/laravel-php-pdf
Laravel integration for the PHP-PDF library, making it easy to generate and work with PDF documents inside your Laravel applications. Provides a familiar service-provider setup and a clean, framework-friendly API for creating PDFs.
composer require dskripchenko/laravel-pdf (pulls dskripchenko/php-pdf — v1.5.0 requires ^1.18), then php artisan vendor:publish --tag=pdf-config and, if you bundle fonts, --tag=pdf-fonts.config/pdf.php): three sections matter day-to-day — fonts.families (registered TTF faces), fonts.provider (new in 1.5.0), and signing (certificate, key, and now signer).use Dskripchenko\LaravelPdf\Facades\Pdf;
Pdf::loadView('invoices.show', ['invoice' => $invoice])
->format('A4')
->save(storage_path("invoices/{$invoice->id}.pdf"));
Pdf::loadView('invoices.show', ['invoice' => $invoice])->sign();
.p12, skip the certificate/key config entirely and bind a signer (see below). Set PDF_SIGNER=App\Pdf\CryptoProSigner::class in .env and you're done — the package resolves it once and uses it for sign(), signed(), signatureConfig() and signDetached().Signer binding (the headline feature of 1.5.0). signing.signer accepts a container binding string or a class name. Bind an interface you control so tests and tenants can swap implementations without touching config:
// AppServiceProvider::register()
$this->app->singleton(SignerContract::class, fn () => new CryptoProSigner(
container: config('services.cryptopro.container'),
pin: config('services.cryptopro.pin'),
));
// config/pdf.php
'signing' => [
'signer' => App\Pdf\SignerContract::class, // or env('PHP_PDF_SIGNER')
],
Because the signer replaces the configured certificate and key, delete the certificate/key entries once signer is set — leaving both in place invites confusion about which one produced a given signature. This is also the sanctioned place for HSM/PKCS#11/KMS credentials: keep secrets in env or a vault read by the signer class, never in the config array.
Test doubles. Bind a deterministic Signer in TestCase::setUp() (or a Signer::fake()-style stub) so signing tests don't need a token plugged in. Snapshot the signature dictionary (/ByteRange, /SubFilter) rather than the raw bytes.
Font provider for portability. fonts.provider receives every family not listed in fonts.families, which makes it the right hook for aliasing Liberation fonts to the Arial / Times New Roman / Courier New names your Blade views already reference:
final class LiberationFontProvider implements FontProvider
{
public function getFont(string $family): ?Font
{
return match (strtolower($family)) {
'arial' => Font::fromFiles(resource_path('fonts/LiberationSans-Regular.ttf'), /* … */),
'times new roman' => Font::fromFiles(resource_path('fonts/LiberationSerif-Regular.ttf'), /* … */),
default => null,
};
}
}
This keeps views written on macOS/Windows rendering consistently inside a slim Linux container.
Detached signatures for archival flows. Pdf::signDetached() produces the CMS blob separately from the document — the right shape when a document lives in object storage and the signature in a database column, or when multiple parties countersign sequentially.
Queue long-running work. HSM round-trips and large documents belong in a job. Bind the signer as a singleton so a single token session is reused across documents in the worker rather than re-authenticating per PDF.
Pdf::signatureConfig() calls mid-request to swap backends — bind per-tenant signers as scoped bindings or a resolver that reads the current tenant, not by editing config at runtime.bold/italic/bold_italic fix means text in configured families that previously fell back to the regular face now genuinely renders bold. Any pixel/page-count snapshot tests, fixed-width table layouts, or measured column widths will shift. Regenerate fixtures and eyeball one invoice per template before shipping.regular is presumably mandatory; if you configure bold but forget bold_italic, italic bold text quietly renders in the regular face. Register all four faces per family and assert it in a test (Pdf::font() metadata checks beat visual diffs).signDetached() + CmsSigner needs php-pdf 1.20+. The TypeError is fixed, but only on that version; on 1.18/1.19 you get an explicit error rather than corrupt output. composer show dskripchenko/php-pdf before you blame your signer.signer and provider must be class strings or container keys — closures in config/pdf.php vanish under php artisan config:cache. Class strings also mean the class must be autoloadable from the config-cache context.Signer/CmsSigner blows up on the first sign(), not at boot. Add one smoke test that resolves config('pdf.signing.signer') and asserts the interface.pdfsig document.pdf for embedded signatures, and openssl cms -verify -in sig.p7s -content doc.pdf -inform DER -noverify for detached blobs. Verify the chain separately with -CAfile; -noverify only tells you the CMS parses.Signer/CmsSigner implementations, a custom FontProvider, and the raw fonts.families map. Those three cover nearly every "my environment differs" situation without forking the package.How can I help you explore Laravel packages today?