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

Htmltopdf Bundle Laravel Package

magelan/htmltopdf-bundle

Symfony bundle for converting HTML markup into PDF documents, wrapping an HTML-to-PDF library with service configuration so you can generate downloadable or streamed PDFs from templates and controllers with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before anything else: magelan/htmltopdf-bundle is a Symfony bundle — *Bundle suffix, DependencyInjection/Extension + Configuration tree, and autoconfiguration via Symfony's kernel. It has no ServiceProvider, no config/*.php publish tag, no facade, and no extra.laravel auto-discovery. It will not composer require into a Laravel app and "just work." With 0 stars and 0 dependents, treat it as a reference implementation of wiring, not a runtime dependency.

Minimal path to first PDF:

  1. Read three files in the repo before writing any code:

    • composer.json — the require block tells you the actual engine (likely spipu/html2pdf) and the PHP/Symfony constraints. If the constraints pin Symfony 3/4-era versions, they will conflict with a Laravel/latest-Symfony dependency graph.
    • DependencyInjection/Configuration.php — the full option surface (page size, orientation, margins, fonts, encoding).
    • DependencyInjection/MagelanHtmlToPdfExtension.php — which service ID is registered and how options map onto the engine. That trio is the entire useful surface. Everything else is kernel boilerplate.
  2. Install only the engine, not the bundle. In a Laravel app:

    composer require spipu/html2pdf
    
  3. First use case — a Blade view → HTML string → PDF download for an invoice, statement, or admin report:

    use Spipu\Html2Pdf\Html2Pdf;
    
    $html = view('pdf.invoice', ['invoice' => $invoice])->render();
    $pdf  = new Html2Pdf('P', 'A4', 'en');
    $pdf->writeHTML($html);
    $pdf->output("invoice-{$invoice->id}.pdf", 'D'); // D=download, I=inline, F=file
    
  4. Look first at the bundle's Configuration.php for the shape of defaults (margins, page size, fonts), then copy those defaults into your own config/pdf.php. You are porting configuration intent, not installing a package.

Implementation Patterns

Translate the Symfony wiring into Laravel idioms before porting logic:

Symfony bundle Laravel equivalent
Configuration.php tree config/pdf.php + config('pdf.*')
Service via services.xml Singleton binding in a ServiceProvider
%kernel.project_dir% base_path()
Response helper (download/inline) response()->streamDownload(...) / response()->file(...)
Bundle-autowired generator service app(PdfRenderer::class) behind an interface

Always adopt behind an interface. This is the single most important pattern given the package's maintenance signal:

// app/Contracts/PdfRenderer.php
interface PdfRenderer {
    public function render(string $html, array $options = []): string; // raw PDF bytes
}
// app/Services/Html2PdfRenderer.php
class Html2PdfRenderer implements PdfRenderer {
    public function render(string $html, array $options = []): string
    {
        $pdf = new \Spipu\Html2Pdf\Html2Pdf(
            $options['orientation'] ?? config('pdf.orientation', 'P'),
            $options['paper']       ?? config('pdf.paper', 'A4'),
            $options['lang']        ?? 'en',
        );
        $pdf->writeHTML($html);
        return $pdf->output('', 'S'); // S = return as string
    }
}

Bind it in AppServiceProvider::register():

$this->app->singleton(PdfRenderer::class, Html2PdfRenderer::class);

Separate rendering from responding. Controllers should not hold PDF bytes:

public function download(Invoice $invoice, PdfRenderer $renderer)
{
    $html = view('pdf.invoice', compact('invoice'))->render();
    $pdf  = $renderer->render($html);
    return response()->streamDownload(
        fn () => print($pdf),
        "invoice-{$invoice->id}.pdf",
        ['Content-Type' => 'application/pdf']
    );
}

For inline preview, use response($pdf, 200, ['Content-Type' => 'application/pdf']).

Queue anything nontrivial. Rendering is CPU- and memory-bound; a synchronous render inside an HTTP request will time out on image-heavy templates. Pattern: dispatch a GenerateInvoicePdf job, store to the local or S3 disk, then email/notify.

dispatch(new GenerateInvoicePdf($invoice->id));

Keep views engine-friendly. The bundle's underlying engine is an old wkhtmltopdf/HTML2PDF class, so:

  • Use table-based layouts and inline styles.
  • Avoid flexbox, grid, calc(), and modern CSS variables.
  • Preload all fonts and images as absolute URLs or embed them base64 — relative paths break when the renderer doesn't share a filesystem cwd.
  • Set charset in the layout <meta> if you handle non-Latin text.

Test with a golden file. In a PdfRendererTest, assert the returned bytes start with %PDF- and contain the invoice number, so a refactor swapping the renderer still passes.

Gotchas and Tips

  • Do not install the bundle into Laravel. composer require magelan/htmltopdf-bundle will pull Symfony kernel pieces you don't need and may fail resolution against current Laravel. Extract the class, install the engine.

  • Verify the actual engine before quoting effort. The assessment docs flag that "whether it shells out to a binary (wkhtmltopdf-style) or drives a headless browser… determines the entire operational model." If it's wkhtmltopdf, you inherit archived software with poor modern-CSS support. If it wraps spipu/html2pdf (a pure-PHP lib), you get no shell/binary dependency — but limited CSS fidelity and slower large-document performance. Confirm which, in composer.json.

  • Fonts are the #1 silent failure. The engine looks up system fonts — if you render non-Latin scripts, ship the fonts into your Docker image (fonts-noto-cjk etc.) or characters render as tofu. Same for any custom font referenced in CSS.

  • The output() second argument is a footgun. 'D' downloads (sends headers), 'S' returns a string, 'F' writes a file, 'I' inlines. In Laravel you almost always want 'S' and let response() handle transport — this decouples rendering from HTTP.

  • Wrap output in try/catch for the engine's exception type. Html2Pdf throws Html2PdfException, not a generic \Exception. Map to a domain-level PdfGenerationException so the controller layer stays renderer-agnostic.

  • Timeout and memory ceilings from day one. Set max_execution_time and memory_limit for the worker/queue, and cap output size. A 50 MB PDF from a runaway template is a real failure mode.

  • The package is unowned and unmaintained. With 0 stars, 0 dependents, no issue history, no security review. Combined with HTML→PDF being a classic SSRF/local-file-inclusion surface (remote asset loading, file:// refs, embedded JS), never pass user-supplied HTML to it without sanitizing. Your own templates only.

  • The abstraction is the point. Because PdfRenderer is one interface with one implementation, swapping to spatie/browsershot or a Gotenberg client later is a one-file change (Html2PdfRenderer → BrowsershotRenderer), not a refactor. Do this from the first commit — the whole value of "borrowing wiring ideas from a 0-star Symfony bundle" is preserved only if the exit is cheap.

  • Extension point that actually matters: the options array in your PdfRenderer::render() signature. Model it on the bundle's Configuration.php keys (orientation, paper, margins, lang, default-CSS) so porting a config from the Symfony side is mechanical.

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