utafitilabs / pdf-scribe-bundle
HTML-to-PDF for Symfony with headless Chrome/Chromium: modern CSS rendering, @page control, one service to inject.
Package info
github.com/utafitilabs/pdf-scribe-bundle
Type:symfony-bundle
pkg:composer/utafitilabs/pdf-scribe-bundle
Requires
- php: >=8.4
- symfony/config: ^7.3 || ^8.0
- symfony/dependency-injection: ^7.3 || ^8.0
- symfony/http-kernel: ^7.3 || ^8.0
- symfony/process: ^7.3 || ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.2
- symfony/framework-bundle: ^7.3 || ^8.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-24 15:26:12 UTC
README
This bundle turns HTML or a URL into a PDF with headless Chrome/Chromium — the
same rendering engine your templates were written for, so modern CSS, web fonts and
@page rules print exactly as they display. One injectable PdfGeneratorInterface,
bytes back or a file on disk, sensible defaults. Enable the bundle, point it at a
browser, and go.
Part of the utafiti tools.
Why
PHP-native PDF libraries render a subset of CSS and fall over on flexbox, grid or
@font-face; wiring a browser by hand means a process call, temp files, timeouts and
cleanup in every project. This bundle handles all of it: render a Twig template as you
already do, hand the HTML over, get a PDF.
Install
Applications using Symfony Flex:
composer require utafitilabs/pdf-scribe-bundle
Applications without Symfony Flex — after requiring the package, enable the bundle:
// config/bundles.php return [ // ... UtafitiLabs\PdfScribe\PdfScribeBundle::class => ['all' => true], ];
That one line registers the PdfGeneratorService and aliases it to
PdfGeneratorInterface, with a 120 s timeout and background printing on. Nothing
else to configure — unless your browser lives somewhere other than the default macOS
path, in which case name it:
# config/packages/pdf_scribe.yaml (optional — create it only to override) pdf_scribe: binary_path: '%env(PDF_SCRIBE_BINARY)%' timeout: 120 options: print-background: true no-pdf-header-footer: true
# .env # macOS PDF_SCRIBE_BINARY="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" # Linux PDF_SCRIBE_BINARY=/usr/bin/chromium-browser
A taste
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use UtafitiLabs\PdfScribe\Contract\PdfGeneratorInterface; final class InvoiceController extends AbstractController { public function download(Invoice $invoice, PdfGeneratorInterface $pdf): Response { $html = $this->renderView('invoice/pdf.html.twig', ['invoice' => $invoice]); return new Response($pdf->fromHtml($html), 200, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'attachment; filename="invoice.pdf"', ]); } }
$pdf->fromUrl('https://example.test/report/2026', ['landscape' => true]); // bytes $pdf->saveFromHtml($html, '/var/reports/summary.pdf'); // file
Methods: fromHtml, fromUrl (return the PDF bytes), saveFromHtml, saveFromUrl
(write to a path). Per-call options override the configured defaults:
print-background (bool), no-pdf-header-footer (bool), landscape (bool),
scale (float, 0.1–2.0), virtual-time-budget (ms, lets page scripts finish
before printing). Page size and margins are CSS, in your template:
@page { size: A4; margin: 15mm; } @media print { body { print-color-adjust: exact; } }
Every failure — missing or non-executable binary, non-zero exit, no output file —
raises PdfGenerationException with the reason in the message.
Documentation
Read the documentation at docs/index.md.
Requirements
- PHP 8.4+ · Symfony 7.3+ / 8
- A Chrome or Chromium binary on the host (
google-chrome,chromium,chromium-browser, or the macOS app bundle); the bundle shells out to it with--headless.
Contributing
See CONTRIBUTING.md. CI enforces the standard (php-cs-fixer, PHPStan max, PHPUnit driving a stand-in browser binary, lowest→newest dependency matrix) on every pull request.
Credits
License
MIT License (MIT): see the LICENSE file for more details.