Search by

utafitilabs / pdf-scribe-bundle

eemjema

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

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-09-24 09:18 UTC

This package is auto-updated.

Last update: 2026-09-24 15:26:12 UTC


README

CI Latest Version Total Downloads License

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.