The dockerless PDF generator for Symfony. Just Chrome. No containers, no wrapping, no kidding.
I wanted to use sensiolabs/gotenberg-bundle -- it's a great and complete Symfony bundle for PDF generation. But it relies on Gotenberg, which requires a running Docker container.
In my case, working in on-premise environments (banking, insurance, regulated industries), Docker is simply not available on production servers. Security policies and infrastructure constraints prevent running containers.
Yet these same machines almost always have a browser installed, or can easily add one. Google Chrome and Chromium are well-maintained, widely trusted, and available on virtually every Linux distribution through standard package managers.
So I built ChromePdfBundle: the same clean builder-based API, but driving Chrome/Chromium directly via the Chrome DevTools Protocol -- no Docker, no external service, no extra infrastructure.
| Docker-based solutions | ChromePdfBundle |
|---|---|
| Require Docker + a running container | Requires only a Chrome/Chromium binary |
| HTTP calls to an external service | Direct communication via CDP |
| Extra infrastructure to maintain | Uses a browser already on the system |
| Not usable in Docker-free environments | Works everywhere Chrome runs |
composer require daif/chrome-pdf-bundle
This installs the bundle along with chrome-php/chrome, the PHP library used to communicate with Chrome via the DevTools Protocol.
If not using Symfony Flex, manually register the bundle:
// config/bundles.php
return [
// ...
Daif\ChromePdfBundle\DaifChromePdfBundle::class => ['all' => true],
];
Create a minimal configuration:
# config/packages/daif_chrome_pdf.yaml
daif_chrome_pdf:
assets_directory: '%kernel.project_dir%/assets'
The bundle will automatically detect Chrome/Chromium on your system. You can also specify the binary path explicitly:
daif_chrome_pdf:
chrome_binary: '/usr/bin/google-chrome'
namespace App\Controller;
use Daif\ChromePdfBundle\ChromePdfInterface;
use Symfony\Component\HttpFoundation\Response;
class InvoiceController
{
public function generateInvoice(ChromePdfInterface $chromePdf): Response
{
return $chromePdf->html()
->content('invoice.html.twig', [
'invoice' => $invoice,
])
->generate()
->stream()
;
}
}
use Daif\ChromePdfBundle\ChromePdfInterface;
class ReportController
{
public function generateReport(ChromePdfInterface $chromePdf): Response
{
return $chromePdf->url()
->url('https://example.com/report')
->generate()
->stream()
;
}
}
use Daif\ChromePdfBundle\ChromePdfInterface;
class DocController
{
public function generateDoc(ChromePdfInterface $chromePdf): Response
{
return $chromePdf->markdown()
->wrapper('wrapper.html.twig')
->files('content.md')
->generate()
->stream()
;
}
}
use Daif\ChromePdfBundle\ChromeScreenshotInterface;
class ScreenshotController
{
public function capture(ChromeScreenshotInterface $chromeScreenshot): Response
{
return $chromeScreenshot->html()
->content('page.html.twig')
->generate()
->stream()
;
}
}
If a template needs to link to a static asset (image, CSS, font), use the {{ chrome_pdf_asset() }} Twig function:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>My PDF</title>
</head>
<body>
<img src="{{ chrome_pdf_asset('img/logo.png') }}" alt="Logo"/>
</body>
</html>
MIT License (MIT): see the License File for more details.
How can I help you explore Laravel packages today?