Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pdf Bundle Laravel Package

fbeen/pdf-bundle

A Symfony bundle that simplifies PDF generation from HTML or Twig templates, providing service integration and configuration to add PDF export functionality to Symfony applications with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

fbeen/pdf-bundle is a Symfony bundle, not a Laravel package. It is a republish of the long-standing psliwa/pdf-bundle, patched so it installs against Symfony 3.3, and it wraps the psliwa/php-pdf rendering library. Before writing code, calibrate expectations: there is no service provider, no config publish, no facade, no queue integration. If you are evaluating this from a Laravel codebase, your first decision is whether you need the bundle at all — normally you don't, and you should consume the underlying library directly (see Implementation Patterns). If you are in a Symfony 3.3 app, the setup is:

  1. Require it:
composer require fbeen/pdf-bundle
  1. Register the bundle in app/AppKernel.php (Symfony 3.3 predates Flex auto-registration):
$bundles = [
    // ...
    new Fbeen\PdfBundle\FbeenPdfBundle(),
    // or new Psliwa\PdfBundle\PsliwaPdfBundle() if the fork kept the original namespace
];
  1. Configure the engine. The default is the library's internal renderer; wkhtmltopdf is the alternative:
# app/config/config.yml
psliwa_pdf:
    engine: pdf        # or: wkhtmltopdf

The config root may have been renamed to fbeen_pdf in the fork — verify before you trust the snippet.

  1. Look here first, in this order:
    • vendor/fbeen/pdf-bundle/DependencyInjection/Configuration.php — the authoritative config tree.
    • vendor/fbeen/pdf-bundle/Resources/config/*.xml — the real service IDs.
    • bin/console debug:container psliwa.pdf — confirm the facade ID before injecting anything.

First use case — render markup and hand back a file from a controller:

public function invoiceAction()
{
    $facade = $this->get('psliwa.pdf');
    $pdf = $facade->getPdf('<pdf><page format="A4">Hello world</page></pdf>');
    $pdf->save('/tmp/invoice.pdf');

    return $this->file('/tmp/invoice.pdf', 'invoice.pdf');
}

The markup is XML-ish, not HTML: <pdf>, <page>, <dynamic-page>, <text>, <img>, <line>, plus style attributes. Get a single page rendering before you touch fonts, templates, headers or footers — that is where 80% of the setup pain lives.

Implementation Patterns

Build markup from a Twig template, never string concatenation. Keep documents in app/Resources/views/pdf/*.xml.twig and render them into the facade:

$markup = $this->renderView('pdf/invoice.xml.twig', ['order' => $order]);
$pdf = $this->get('psliwa.pdf')->getPdf($markup);

This is the single biggest maintainability win — the markup language is verbose and attribute-heavy, and inline PHP string building becomes unreadable fast.

Stream vs. save. For synchronous downloads, write to a temp file and return BinaryFileResponse (or $this->file()), then unlink() in a finally block or a response callback. For anything above trivial size, generate on the queue instead: a job produces the PDF, stores it on disk/S3, and the controller just redirects to a signed URL. PDF rendering is CPU- and memory-heavy enough that synchronous generation is a latency bug waiting to happen.

Multi-page documents. Use fixed <page> elements for covers and letterhead, and <dynamic-page> when content flows and needs automatic page breaks. Page headers/footers are attributes of the page node, not of the content, so keep them in the layout template and only vary the body.

Shared styling. If the fork exposes a stylesheets/fonts entry in config, put shared header/footer/font declarations there rather than repeating attributes in every template. The library understands a CSS-like subset (font, size, color, margin, padding, border) — not real CSS.

The wkhtmltopdf engine is a different programming model. You stop writing XML and start writing HTML + real CSS:

psliwa_pdf:
    engine: wkhtmltopdf
    wkhtmltopdf:
        binary: /usr/local/bin/wkhtmltopdf

Then render HTML exactly as you would a web page and call the facade's HTML/URL method (getPdfFromHtml() / getPdfFromUrl() — check the facade class in vendor/, the names move between versions). Reach for this whenever a designer hands you a layout; the internal engine will not do flexbox, floats or complex tables.

Caching. Treat a PDF as a derived artifact keyed by a hash of the data that produced it. cache()->remember('pdf:invoice:'.$order->id.':'.$order->updated_at, ...) around the generation step pays for itself immediately.

In Laravel, skip the bundle. Wire the underlying library in a service provider instead of pulling symfony/framework-bundle in just to satisfy a bundle:

// AppServiceProvider::register()
$this->app->singleton('pdf.facade', function () {
    // return the library's facade/document builder
});

Then inject it like any other service. You get the same rendering with none of the Symfony DI overhead.

Gotchas and Tips

  • This is legacy software with no community. Symfony 3.3 hit EOL in January 2020, and the fork has zero stars and zero dependents. There will be no security patches, no Symfony 4/5 support, and no Stack Overflow answers specific to the fork. Its composer.json almost certainly pins symfony/* to 3.3.*, which is a hard blocker for any framework upgrade — plan the exit before you plan the feature.

  • It is not a Laravel package. No ServiceProvider, no config/pdf.php, no publishable assets. Integration in Laravel means either hand-registering a Symfony bundle into a hand-built container (not worth it) or using the library underneath directly. The bundle adds only Symfony DI glue.

  • XML, not HTML. Unclosed tags, unescaped & and stray < in user data will throw parse errors that rarely name the offending line. Escape all interpolated content ({{ value|e }} in Twig is not enough for attribute context — check the output), and log the full markup on any exception in development.

  • UTF-8 and non-Latin text. Default fonts have no glyph coverage beyond Latin-1; you get blank boxes or hard exceptions. Register a Unicode TTF (DejaVu is the usual choice) and set font-type explicitly. Test with a real accented/Japanese/Arabic string before shipping.

  • Stale caches after font or config changes. The library caches parsed stylesheets and font metrics. After adding a font or editing shared styles, clear var/cache/ before concluding the change didn't work.

  • wkhtmltopdf is its own liability. It is unmaintained, needs a patched Qt build, and on headless servers fails with unhelpful errors if the binary is missing or not executable. External images/CSS need --enable-local-file-access on newer builds, and remote resources need explicit timeouts. Pin the binary version in your deployment image.

  • Memory and time. Large dynamic-page documents can exhaust the default memory_limit and PHP max_execution_time. Raise both per-job, or chunk the document.

  • Debugging workflow. Add a dev-only route that renders a sample document with ?debug=1, and use bin/console debug:container to confirm the service ID before injecting. Most "it doesn't work" reports here are a wrong service ID or a config key that silently isn't being read.

  • Extension points. If you stay on the internal engine, custom tags are possible by registering node builders with the parser, and custom fonts/stylesheets via config. That is the ceiling of extensibility — there is no plugin ecosystem.

  • Alternatives are cheap migrations. Because the input is markup, moving to dompdf/dompdf, mpdf/mpdf, knplabs/knp-snappy-bundle + wkhtmltopdf, or spatie/browsershot (headless Chrome, best CSS fidelity) is mostly a template rewrite. For a Laravel app, barryvdh/laravel-dompdf or Browsershot will be a better day-to-day experience than a Symfony 3.3 bundle on life support.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons