dskripchenko / laravel-php-pdf
Laravel bridge for dskripchenko/php-pdf — the MIT PDF toolkit. Pdf facade, config-driven fonts and page setup, and a response()->pdf() macro. GPL-free alternative to mpdf/FPDI stacks.
Requires
- php: ^8.2
- dskripchenko/php-pdf: ^1.18
- illuminate/http: ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^11.0 || ^12.0 || ^13.0
Requires (Dev)
- dskripchenko/php-pdf-fonts-liberation: ^1.1
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-18 16:04:32 UTC
README
Laravel bridge for
dskripchenko/php-pdf— the pure-PHP, MIT-licensed PDF toolkit (generate, read, merge). No GPL friction, faster than mpdf/dompdf, and conformance-validated on every push.
Installation
composer require dskripchenko/laravel-php-pdf
The service provider and the Pdf facade register automatically
(package discovery). Optionally publish the config:
php artisan vendor:publish --tag=php-pdf-config
Usage
HTML → PDF
use Dskripchenko\LaravelPhpPdf\Facades\Pdf; // bytes $bytes = Pdf::fromHtml('<h1>Invoice #1234</h1>')->bytes(); // file Pdf::fromHtml(view('invoices.show', $data)->render())->save(storage_path('invoice.pdf')); // HTTP responses Route::get('/invoice', fn () => Pdf::fromHtml($html)->inline('invoice.pdf')); Route::get('/invoice/download', fn () => Pdf::fromHtml($html)->download('invoice.pdf')); // Streamed responses — rendered straight into the output buffer, never // held in memory as one string. For very large documents. Route::get('/report', fn () => Pdf::fromHtml($html)->stream('report.pdf')); Route::get('/report/download', fn () => Pdf::fromHtml($html)->streamDownload('report.pdf'));
The HTML may carry <style> blocks — the cascade, selectors, @media print
— with php-pdf 1.19 or later. Page size, orientation and margins come from
config/php-pdf.php: an @page rule in the HTML is not applied by
Pdf::fromHtml(). To let @page decide, parse the HTML with php-pdf and
render the result: Pdf::render(\Dskripchenko\PhpPdf\Document::fromHtml($html))
(the configured fonts apply, the default metadata does not).
response()->pdf()
The macro accepts a PendingPdf, a php-pdf Document (either layer), a
filled form (Pdf::fillForm()), or raw bytes — the mpdf Output('', 'D') habit, the Laravel way:
return response()->pdf(Pdf::fromHtml($html), 'invoice.pdf'); // inline return response()->pdf(Pdf::fromHtml($html), 'invoice.pdf', inline: false); // download
Programmatic documents
$document = Pdf::builder() ->heading(1, 'Quarterly report') ->paragraph('Q1 revenue exceeded the forecast by 12%.') ->build(); return Pdf::render($document)->download('report.pdf');
Everything from the underlying toolkit is reachable — charts, barcodes, AcroForm fields, PDF/A, encryption, digital signatures, and reading/merging existing PDFs. See the php-pdf documentation.
Filling existing forms
Pdf::fillForm() opens a PDF form produced elsewhere — a file path or the
PDF bytes — and returns php-pdf's ExistingFormFiller. The configured
fonts.default is used for values the form's own fonts cannot show, so
Cyrillic or CJK values work without extra code:
$form = Pdf::fillForm(storage_path('templates/application.pdf')) ->setValues([ 'full_name' => 'Иван Петров', 'agree' => true, ]) ->stampImage(0, storage_path('signature.png'), x: 400, y: 700, width: 120, height: 40) ->flatten(); return response()->pdf($form, 'application.pdf', inline: false);
fields() lists the template's fields with their types and accepted
options; see filling an existing form
for the value rules and what flatten() does.
Digital signatures
Configure the key once (php-pdf.signing, below), then:
// Sign a generated document. A SignatureField in it is signed with its // stamp filled from the certificate; otherwise the signature is invisible. return Pdf::fromHtml($html)->signed(['reason' => 'Approved'])->download('contract.pdf'); // Sign an existing PDF — or one signed before; earlier signatures stay valid. $signed = Pdf::sign(storage_path('contract.pdf'), ['reason' => 'Countersigned']); // Certify for form filling, lock fields, sign a named field… $signed = Pdf::sign(storage_path('contract.pdf'), ['certification' => 2, 'lock' => ['amount'], 'field' => 'approver']); // …or drive php-pdf's PdfSigner directly: visible fields, fields for later // signers, a key in the user's browser (CryptoPro browser plug-in). // (ExternalSigner is Dskripchenko\PhpPdf\Pdf\Signature\External\ExternalSigner.) $prepared = Pdf::signer($pdf)->prepare(Pdf::signatureConfig(['signer' => new ExternalSigner($userCert)])); // Fill a signed form without breaking the signature. $filled = Pdf::fillForm($signed)->setValue('amount', '100')->toBytes(incremental: true); // Long-term validation: DSS (B-LT) plus a document timestamp (B-LTA). $archived = Pdf::ltv($signed); // Check signatures against the configured trust anchors. $report = Pdf::validate($archived); $report->allValid(); // A detached .p7s (CAdES) beside a file, and its check. $p7s = Pdf::signDetached(storage_path('invoice.xml')); Pdf::verifyDetached(storage_path('invoice.xml'), $p7s)->isValid();
Signatures are PAdES (ETSI.CAdES.detached) by default; RSA, ECDSA and
GOST R 34.10-2012 keys work (GOST needs OpenSSL's gost engine loaded into
PHP). Timestamp authorities, OCSP responders and CRLs are reached through
Laravel's HTTP client, so proxies apply and Http::fake() works in tests.
The full feature set is in the signing guide.
Keys outside PHP: CryptoPro, HSM, cloud KMS
Pdf::sign(), ->signed() and Pdf::signatureConfig() take any php-pdf
signer as 'signer'; the configured key is then not needed (the configured
timestamp authority, format and reason still apply). Ready-made signers:
use Dskripchenko\PhpPdfCryptoPro\CryptoProCadesSigner; // dskripchenko/php-pdf-cryptopro use Dskripchenko\PhpPdfPkcs11\Pkcs11Signer; // dskripchenko/php-pdf-pkcs11 use Dskripchenko\PhpPdfCloudKms\Aws\AwsKmsSigner; // dskripchenko/php-pdf-cloud-kms // CryptoPro CSP on the server — GOST R 34.10-2012. $signed = Pdf::sign(storage_path('contract.pdf'), ['signer' => CryptoProCadesSigner::fromThumbprint($thumbprint, pin: $pin)]); // An HSM, a smart card or a token through PKCS#11. $signed = Pdf::sign(storage_path('contract.pdf'), ['signer' => Pkcs11Signer::open($module, pin: $pin, keyLabel: 'pdf')]); // AWS KMS (Google Cloud KMS and Azure Key Vault likewise). return Pdf::fromHtml($html) ->signed(['signer' => AwsKmsSigner::create('alias/pdf-signing', $certificatePem, region: 'eu-central-1')]) ->download('contract.pdf');
To sign with it everywhere, bind it in the container and name the binding
in php-pdf.signing.signer — it then replaces the configured certificate
and key, and is resolved once:
// AppServiceProvider::register() $this->app->singleton('pdf.signer', fn () => Pkcs11Signer::open(config('services.hsm.module'), pin: config('services.hsm.pin'), keyLabel: 'pdf')); // config/php-pdf.php → 'signing' => ['signer' => 'pdf.signer', …] (or PHP_PDF_SIGNER=pdf.signer) $signed = Pdf::sign(storage_path('contract.pdf'));
Pdf::signDetached() takes 'signer' and the configured signer too. A
CryptoPro signer (a CmsSigner, it returns a whole CMS) needs php-pdf 1.20
or later there.
When those URLs come from users or from uploaded certificates, turn on
signing.network.block_private_networks: every address a host resolves to
must then be public (no loopback, private, link-local or metadata ranges,
unless listed in allowed_cidrs), redirects are checked the same way, the
connection goes to the address that was checked, and responses are capped
at max_response_bytes.
Configuration
config/php-pdf.php controls page defaults, fonts, default metadata,
signing and validation (an excerpt):
'paper' => env('PHP_PDF_PAPER', 'a4'), // a3..a6, letter, legal, tabloid, executive 'orientation' => env('PHP_PDF_ORIENTATION', 'portrait'), 'margins' => ['top' => null, 'right' => null, 'bottom' => null, 'left' => null], // pt 'fonts' => [ // Embedded TTFs (required for Cyrillic, Greek, Arabic, CJK — the // base-14 defaults cover Latin only). Fonts are subset automatically. 'default' => storage_path('fonts/DejaVuSans.ttf'), 'bold' => storage_path('fonts/DejaVuSans-Bold.ttf'), // Named families for CSS font-family / RunStyle(fontFamily: ...): // A face that is not configured falls back to the regular one. 'families' => [ 'mono' => ['regular' => storage_path('fonts/DejaVuSansMono.ttf'), 'bold' => storage_path('fonts/DejaVuSansMono-Bold.ttf')], ], // A FontProvider (container binding or class) for any other family. 'provider' => null, ], 'metadata' => ['Author' => 'ACME Corp.'], // default /Info entries 'signing' => [ // A Signer / CmsSigner bound in the container (CryptoPro, PKCS#11, cloud KMS)… 'signer' => env('PHP_PDF_SIGNER'), // …or a PEM certificate + key (file paths or PEM text)… 'certificate' => env('PHP_PDF_SIGN_CERT'), 'key' => env('PHP_PDF_SIGN_KEY'), 'passphrase' => env('PHP_PDF_SIGN_PASSPHRASE'), // …or a PKCS#12 container. 'pkcs12' => env('PHP_PDF_SIGN_P12'), 'pkcs12_password' => env('PHP_PDF_SIGN_P12_PASSWORD'), 'chain' => [], // intermediate certificates 'format' => env('PHP_PDF_SIGN_FORMAT', 'pades'), // or 'pkcs7' (adbe.pkcs7.detached) 'reason' => null, 'location' => null, 'contact' => null, 'signer_name' => null, 'timestamp' => [ 'url' => env('PHP_PDF_TSA_URL'), 'username' => env('PHP_PDF_TSA_USERNAME'), 'password' => env('PHP_PDF_TSA_PASSWORD'), 'policy' => null, 'headers' => [], 'timeout' => 15, ], 'network' => [ 'block_private_networks' => env('PHP_PDF_SIGN_BLOCK_PRIVATE', false), 'allowed_cidrs' => [], 'max_response_bytes' => 20 * 1024 * 1024, 'max_redirects' => 3, ], ], 'validation' => [ 'trust' => [], // root certificates for Pdf::validate() 'system_trust' => false, // add OpenSSL's system roots 'fetch_revocation' => false, ],
No fonts at hand? dskripchenko/php-pdf-fonts-liberation
ships Liberation Sans, Serif and Mono (Latin, Cyrillic, Greek, Hebrew),
metric-compatible with Arial, Times New Roman and Courier New. Name its
provider in fonts.provider — it answers for the families not listed in
families, font-family: Arial included — and point the default font at
it:
'fonts' => [ 'provider' => \Dskripchenko\PhpPdfFontsLiberation\LiberationFontProvider::class, 'default' => base_path('vendor/dskripchenko/php-pdf-fonts-liberation/assets/fonts/LiberationSans-Regular.ttf'), 'bold' => base_path('vendor/dskripchenko/php-pdf-fonts-liberation/assets/fonts/LiberationSans-Bold.ttf'), 'italic' => base_path('vendor/dskripchenko/php-pdf-fonts-liberation/assets/fonts/LiberationSans-Italic.ttf'), 'bold_italic' => base_path('vendor/dskripchenko/php-pdf-fonts-liberation/assets/fonts/LiberationSans-BoldItalic.ttf'), ],
Migrating from laravel-mpdf / barryvdh wrappers
The underlying toolkit ships compat facades and guides for mpdf and FPDI call sites.
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13
dskripchenko/php-pdf1.18+ (1.19+ for<style>CSS in HTML)- Extensions:
mbstring,zlib,dom(plusopensslfor encryption and signatures)
Testing
composer test
The php-pdf family
| Package | What it adds |
|---|---|
| php-pdf | The core: HTML with CSS or a builder to PDF, read, merge, fill forms, sign and validate. |
| laravel-php-pdf (this package) | Laravel bridge: a Blade view straight to PDF, response()->pdf(), signing from config. |
| php-pdf-fonts-liberation | Liberation Sans, Serif and Mono, metric-compatible with Arial, Times New Roman and Courier New (OFL, kept out of the MIT core). |
| php-pdf-cryptopro | Signs with a key in CryptoPro CSP on the server: GOST R 34.10-2012, Russian qualified signatures. |
| php-pdf-pkcs11 | Signs with a key on an HSM, a smart card or a USB token, through PKCS#11. |
| php-pdf-cloud-kms | Signs with a key in AWS KMS, Google Cloud KMS or Azure Key Vault. |
License
MIT. The underlying dskripchenko/php-pdf is MIT as well — no GPL
obligations anywhere in the stack.