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 Generator Bundle Laravel Package

forci/pdf-generator-bundle

A Symfony bundle that integrates PDF generation into your application, providing configuration and services to render HTML or templates into downloadable PDF documents with streamlined setup and dependency injection.

View on GitHub
Deep Wiki
Context7

Getting Started

Start by confirming the framework mismatch before writing any code. This is a Symfony bundle (forci/pdf-generator-bundle), not a Laravel package: there is no extra.laravel.providers, no facade, no config/pdf.php to publish. If your app is Laravel, the "getting started" step is really reading the bundle source — the *Bundle class, the DependencyInjection/*Extension, and the service definitions — and deciding whether to port the ~100–300 line wrapper into an App\Services\Pdf class or skip it entirely. If you are on Symfony, register the bundle in config/bundles.php, add its YAML config under config/packages/, and inject the renderer service into a controller or a dedicated exporter service.

Where to look first:

  1. The DI extension and service definition — this tells you exactly what the underlying engine is (Dompdf, mPDF, wkhtmltopdf, headless Chrome, hosted API) and what options are exposed. This is the single most important file.
  2. The public render method signature — usually render(string $html, array $options), renderTemplate(string $template, array $context, array $options), or similar. That signature is the seam you will build against.
  3. The bundle's config tree — page size, orientation, margins, and output mode defaults live here; copy them into your PdfOptions value object if porting.
  4. The Composer constraints — check symfony/* and PHP version floors before assuming it will install alongside a modern stack.

First use case to target: an admin-only, non-customer-facing export — a back-office report, a downloadable statement for internal review, a shipping label preview. Keep it out of customer-facing invoice generation until you have golden-file tests against real templates. A minimal first cut looks like:

// Symfony: inject the bundle's service
public function __construct(private PdfGeneratorInterface $pdf) {}

public function export(Report $report): Response
{
    $html = $this->twig->render('report/pdf.html.twig', ['report' => $report]);
    return $this->pdf->renderResponse($html, ['format' => 'A4']);
}
// Laravel: port the seam first, bind a maintained renderer behind it
interface PdfGenerator {
    public function render(string $html, PdfOptions $options): PdfDocument;
}

Bind barryvdh/laravel-dompdf behind that interface for simple documents, or spatie/browsershot for fidelity. The day-to-day ergonomics you want — injectable service, template → PDF, stream/save/response — come from the interface you define, not from this bundle.

Implementation Patterns

Service-injection pattern (Symfony native). The bundle's whole value is that controllers stay library-agnostic. Inject the PDF service, render a Twig template, pass options, return the result. Keep controllers thin — no new Dompdf() scattered around. If you have multiple document types (invoice, statement, label), introduce a per-document exporter class that wraps the service and owns the template path + option defaults, so callers do $invoiceExporter->download($invoice) rather than juggling HTML and config.

Port-the-wrapper pattern (Laravel). Treat the bundle as a reference implementation. Copy its option-mapping logic (page size, orientation, margins, output mode) into a PdfOptions DTO and an adapter class. The wrapper is small enough that the port is days, not weeks — but you now own it permanently, so put it behind an interface so the backend is swappable later.

Template-to-PDF pattern. The dominant real-world flow is: render a Blade/Twig template to an HTML string → pass to the PDF service → return a Response (stream) or write to disk (save) for async delivery. Keep the template and the PDF call in the same exporter so the option set travels with the template. Anything with page headers/footers usually needs renderer-specific options (CSS @page rules, or explicit header/footer HTML), so centralise those defaults in the exporter rather than at each call site.

Output-mode branching. Stream-vs-save-vs-return is where day-to-day usage actually varies. A single download() for small synchronous exports, a queued job for large or batched generation that writes to storage and emails/links, and an in-memory return for cases where you then attach the PDF to a mail. Model this as three thin methods on the exporter rather than three call sites each choosing options.

Queued generation pattern. PDF rendering is CPU- and memory-bound and will block a request worker. For anything non-trivial, dispatch a job, render, store to disk, notify. This is a workflow decision you make regardless of which renderer you pick; if you port from this bundle, keep the "render HTML → bytes" step pure and synchronous inside the job, so it is testable without the queue.

Golden-file testing pattern. Pick 5–10 representative templates — one dense table, one with a logo and custom font, one multi-page with headers/footers, one with page-break constraints. Assert against stable outputs (page count, extracted text, or a checksum on a known-good renderer version). This is the pattern that catches silent regressions when you upgrade the underlying engine.

Gotchas and Tips

The license is a hard stop until reviewed. NOASSERTION means GitHub could not detect a recognised license file — so either there is no license, a malformed one, or a bespoke one. Absent an explicit grant, default copyright applies. Do not ship it in anything you distribute without legal sign-off. If you vendor the code, keep the attribution and treat the license question as unresolved, not resolved.

Symfony-only conventions. No Laravel service provider, no config publishing, no Blade integration. The -bundle suffix is the give-away. Adapting it means hand-porting the DI wiring — and the wrapper is small enough that the port costs roughly what evaluating compatibility costs.

Pin the underlying engine explicitly. The bundle hides which renderer it uses. That matters enormously for fidelity (CSS grid, web fonts, page-break control) and for operations (binary invocation vs pure PHP vs external service). Read the source and pin the engine version in your own Composer constraints so an upstream constraint bump doesn't silently change rendering.

Transitive Symfony pins can collide. Laravel already pulls symfony/* components. A bundle of unknown vintage may floor those to versions your app cannot satisfy, producing an unsatisfiable dependency graph. Run composer why-not and composer audit before committing to a port.

No upstream means no upstream fixes. Zero dependents, one star, no evidence of CI or a version-support matrix. You are the maintainer of record the moment you adopt it. Budget a named owner and treat it as an internal module, not a dependency. That owner answers every question by reading the source — there is no Stack Overflow corpus, no issue backlog with signal.

Debugging is source-reading, not searching. When output looks wrong — font substitution, margins ignored, page breaks mid-table — the answer is in the bundle's option mapping and the renderer's docs, not in community threads. Log the resolved option set on every render during integration; most "the PDF looks wrong" reports are an option that got dropped in translation between template, wrapper, and engine.

Extension point: the seam is yours. The only durable extension point is the interface you wrap around whatever you adopt. Define PdfGenerator with a narrow signature (render(string $html, PdfOptions $options): PdfDocument) before you integrate anything. That single decision makes the backend reversible — Dompdf to Browsershot to Gotenberg — and is the one thing worth building even if you ultimately skip this package.

Do not put rendering on the request path for real volume. Memory blow-ups and timeouts show up under load, not in dev. Shadow-run in staging against realistic document sizes and page counts before trusting it in production, and default to the queued path for anything customer-facing.

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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment