kitpages/pdf-bundle
A Symfony bundle from Kitpages that simplifies generating PDFs from your application, wrapping a PDF library behind bundle configuration and services so controllers can produce downloadable documents with minimal setup.
This is a thin Symfony wrapper around the wkhtmltopdf binary (HTML → PDF via a headless WebKit). The bundle itself is small; almost all your day-to-day behavior comes from configuring the binary correctly and driving the service it registers.
Minimal steps:
wkhtmltopdf is the real dependency. On Debian/Ubuntu install the patched-qt build plus font and X libs (libxrender1, libxext6, libfontconfig1, xfonts-75dpi). Verify with wkhtmltopdf --version as the same user PHP runs as.composer require kitpages/pdf-bundle). Given the age of the repo, expect to pin it and possibly add it manually to app/AppKernel.php (Symfony ≤3) or config/bundles.php (Symfony 4+). Check the composer constraints against your framework version before committing.config.yml / config/packages/kitpages_pdf.yml. The bundle exposes config for the binary location and default CLI options; open DependencyInjection/Configuration.php in the repo to see the exact keys — this is the file to read first, not the README.Resources/config/services.yml. Inject that service into your controller/action rather than instantiating anything yourself.Response with Content-Type: application/pdf. Start with a streamed inline response for iteration, then switch to Content-Disposition: attachment for the real feature.If you are on a modern Symfony (5/6/7), budget time for compatibility work or pick knplabs/knp-snappy (same underlying binary, actively maintained) instead. The concepts below transfer directly.
Controller action → PDF response. The canonical workflow is: build data → renderView() a Twig template → pass the HTML string to the PDF service → wrap the returned bytes in a Symfony Response with the PDF content type and a filename. Keep the template free of layout chrome (no nav/sidebar) and put print-specific CSS in it.
Configure options per call, not globally. Global config is good for defaults (margins, page size, --quiet), but per-document options — orientation, header/footer HTML, --dpi, TOC — belong at the call site so different documents don't fight over shared config. Treat the option array as part of the document definition.
Template-driven, not string-concatenated. Always render through Twig with absolute URLs for assets. wkhtmltopdf resolves relative URLs against the input file/URL and will silently drop images and CSS otherwise. The cleanest pattern is to pass absolute asset URLs (absolute_url(asset(...))) and --enable-local-file-access only when you genuinely need local files.
Two input modes, two use cases.
Stream vs. store. Return inline (Content-Disposition: inline) for previews and attachments for downloads. For anything generated more than once (monthly statements, batch labels), generate once and persist to storage, then serve the stored file — regeneration on every request is the most common performance mistake with wkhtmltopdf.
Batch generation. For N documents, generate sequentially or via a queue worker, not in the web request. wkhtmltopdf is CPU- and memory-hungry; a worker with a timeout and a retry policy is the right home for bulk jobs. Reuse the service instance across the batch.
Testing. Wrap the bundle service behind your own small PdfRenderer interface. In tests, bind a fake implementation that returns a canned PDF or plain bytes. This keeps your test suite from depending on a system binary being installed on CI.
Integration tips. Put PDF CSS in a separate stylesheet loaded only in the PDF template — screen styles rarely translate. Use fixed page-break rules (page-break-before, page-break-inside: avoid) in that stylesheet rather than trying to control pagination from PHP.
Compatibility is the biggest risk. Two stars and zero dependents means this is effectively unmaintained. The bundle targets the Symfony 2/3 era; service definitions, bundle registration, and config tree may not load on Symfony 4+. Read composer.json constraints before doing anything else, and treat any modern-Symfony usage as a fork-and-patch exercise.
Binary path and user mismatch. PHP-FPM often runs as www-data while you tested as your shell user. The binary may be on your PATH but not the web user's, and font caches are per-user. Always set an absolute binary path in config and verify with the same user.
Silent rendering failures. wkhtmltopdf famously exits 0 while producing a blank or partial PDF. Symptoms: missing images, unstyled output, blank pages. Causes are almost always URL resolution, JS timing, or missing network access. Debug by running the exact command line manually from the shell — log the command the bundle builds and replay it. That single habit resolves most issues.
JavaScript and modern CSS. The bundled WebKit is ancient: no flexbox/grid support in older builds, limited ES6, no fetch/Promise in some versions. If your template depends on client-side rendering, pass --javascript-delay (milliseconds) and expect flakiness. Prefer server-rendered, table-based layout CSS for PDFs.
Timeouts and memory. Large documents or slow external assets exceed default timeouts. Set --load-error-handling ignore and a generous --javascript-delay where appropriate, and raise PHP's max_execution_time and the CLI timeout. In queue workers, enforce a hard kill so a hung binary doesn't wedge the worker.
Security. URL mode is an SSRF vector if the URL is user-influenced; it also fetches from inside your network with whatever privileges the host has. Never pass raw user input as the input URL — allowlist, or render HTML from data instead. Sanitize any HTML you feed the binary if it includes user content.
Encoding. Pass UTF-8 explicitly and include <meta charset="utf-8"> in the template. Non-Latin scripts additionally require the right fonts installed on the host; missing glyphs render as boxes with no error.
Extension points. The bundle is thin, so the useful extension points are outside it: your own PdfRenderer wrapper (for testing and swapping backends), a Twig namespace for PDF-only templates, and a small config layer that maps document types to option sets. When (not if) you outgrow this bundle, knplabs/knp-snappy is a drop-in conceptual replacement for the same binary, and dompdf or a headless-Chrome driver are alternatives if you need modern CSS.
How can I help you explore Laravel packages today?