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

Wkhtmltopdf I386 Laravel Package

h4cc/wkhtmltopdf-i386

Composer package that bundles the 32-bit Linux wkhtmltopdf binary, so PHP and Laravel apps can convert HTML to PDF without a system-wide install. Works well with Snappy-style wrappers for invoices, reports, and other printable documents. 32-bit only; check architecture compatibility before use.

Deep Wiki
Context7

Getting Started

This package contains no PHP code — it is a Composer-shipped binary (wkhtmltopdf 0.12.4, patched QtWebKit, Linux i386/glibc). Your day-to-day work is really "wire this binary into a wrapper and keep it runnable in your environment."

Minimal steps:

  1. Install the binary package and a wrapper:
    composer require h4cc/wkhtmltopdf-i386
    composer require barryvdh/laravel-snappy   # knplabs/knp-snappy + Laravel glue
    
  2. Verify the binary before touching Laravel — this isolates 90% of later confusion:
    vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386 --version
    # -> wkhtmltopdf 0.12.4 (with patched qt)
    echo '<h1>hello</h1>' > /tmp/t.html
    vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386 /tmp/t.html /tmp/t.pdf
    
    If step 2 fails, nothing in Laravel will fix it (see Gotchas — the i386/glibc constraint is the usual culprit).
  3. Point Snappy at the binary and publish its config:
    // config/snappy.php
    'pdf' => [
        'enabled' => true,
        'binary'  => env('WKHTMLTOPDF_BINARY', base_path('vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386')),
        'timeout' => 120,
        'options' => [
            'enable-local-file-access' => true,   // required on 0.12.4 for file:// and relative assets
            'encoding'                 => 'UTF-8',
        ],
        'env' => ['LANG' => 'C.UTF-8'],
    ],
    
  4. First real use case — invoice/report download from a Blade view:
    public function download(Invoice $invoice, PdfWrapper $pdf)
    {
        $pdf->setOption('page-size', 'A4');
        $pdf->setOption('margin-top', 15);
        $pdf->setOption('margin-bottom', 15);
    
        return response()->streamDownload(
            fn () => print $pdf->loadView('pdf.invoice', ['invoice' => $invoice])->output(),
            "invoice-{$invoice->number}.pdf",
            ['Content-Type' => 'application/pdf']
        );
    }
    

Where to look first: the bin/ directory of the package (that single path is the whole integration surface), then config/snappy.php for options. There is no source to read — the vendor package is a binary plus a composer.json.

Implementation Patterns

Resolve the path, don't hardcode it. Put the binary path in one config key or a small service provider so it can be overridden per environment (CI has no binary, prod does):

$this->app->singleton(PdfRenderer::class, fn () => tap(
    new SnappyPdf(app('files'), config('snappy.pdf.binary')),
    fn ($pdf) => $pdf->setTimeout(config('snappy.pdf.timeout'))
));

Then inject PdfRenderer everywhere instead of SnappyPDF — makes it swappable and mockable.

Guard for architecture at boot. Since this is an i386 build, add a cheap self-check so failures are loud and early rather than a cryptic sh: not found at request time:

// AppServiceProvider::boot()
if (app()->runningInConsole() === false && ! is_executable(config('snappy.pdf.binary'))) {
    Log::critical('wkhtmltopdf binary is missing or not executable', ['path' => config('snappy.pdf.binary')]);
}

Asset handling — pick one strategy deliberately.

  • URL mode: $pdf->loadView(...) with asset()/signed routes for CSS, JS, images. Works with local file access disabled, but requires the app to be reachable from the server itself and slows things down.
  • File mode: write the rendered Blade to a temp file, set --enable-local-file-access, and use absolute file:// paths for assets. Fastest and most deterministic; make sure you clean up temp files in a finally.

Headers and footers need real files (not strings) on 0.12.4:

$header = tempnam(sys_get_temp_dir(), 'hdr') . '.html';
file_put_contents($header, view('pdf.header', compact('invoice'))->render());
$pdf->setOption('header-html', $header);
$pdf->setOption('header-spacing', 5);
$pdf->setOption('margin-top', 25);

Queue anything bigger than an invoice. Rendering a 40-page report inline will stall a PHP-FPM worker for seconds to tens of seconds:

final class GenerateReportPdf implements ShouldQueue
{
    public int $timeout = 180;
    public function handle(PdfRenderer $pdf): void
    {
        Storage::disk('s3')->put($this->path, $pdf->loadView('pdf.report', $this->data)->output());
        $this->user->notify(new ReportReady($this->path));
    }
}

Bump the job $timeout above the Snappy timeout; the default 60s job timeout kills long renders mid-flight.

Testing. Never shell out in feature tests. Bind a fake PdfRenderer that returns a canned minimal PDF (%PDF-1.4 ...), assert on the view data and response headers. Keep exactly one @group binary smoke test that runs in CI against the real binary to catch environment drift.

Docker. Install the package during the composer stage of a glibc base image (php:8.x-fpm, not Alpine) and copy vendor/ forward; then chmod +x in the same layer (see below). If you must use Alpine, run wkhtmltopdf from a sidecar container or prebuilt static binary instead of this package — the i386 ELF won't load.

Gotchas and Tips

  • i386 only. On an x86_64 host you need 32-bit libs (libc6:i386, lib32z1, usually ia32-libs on older images). On ARM (Apple Silicon, Graviton, M1 CI runners) it flatly will not run. If your fleet is amd64 or ARM, you want h4cc/wkhtmltopdf-amd64 or a maintained image — not this package.
  • musl ≠ glibc. Alpine will error with something like not found even though the file exists, because the dynamic loader path /lib/ld-linux.so.2 is missing. Don't chase "file doesn't exist" messages on Alpine; it's a libc mismatch.
  • Executable bit gets lost. Some deploy tooling (artifact zips, CI caching, COPY from a --no-install stage) strips the +x flag, producing Permission denied. Fix deterministically:
    "scripts": { "post-install-cmd": "chmod +x vendor/h4cc/wkhtmltopdf-i386/bin/wkhtmltopdf-i386" }
    
  • --enable-local-file-access is mandatory on 0.12.4 for file:// URLs and relative assets. Without it you get pages of unstyled HTML with no error and exit code 0. This is the single most common "my CSS is missing" cause.
  • Fonts. Minimal Docker images ship almost nothing; PDFs render with tofu boxes. Install fontconfig, fonts-liberation, fonts-dejavu, and any brand font, then fc-cache -f. Set LANG=C.UTF-8 via the env array or non-ASCII text comes out as ????.
  • Scaling looks wrong. CSS pixels vs PDF points mismatch: add 'dpi' => 96 and 'disable-smart-shrinking' => true when your layout is designed for screen rendering. Expect to iterate once on margins after enabling this.
  • Silent failures. Snappy swallows stderr. When debugging, reproduce with the raw command, drop --quiet, and read stderr — "Exit with code 1 due to network error: ContentNotFoundError" tells you exactly which asset 404'd.
  • Timeouts. Symfony Process throws ProcessTimedOutException; the default Snappy timeout is 60s. Raise both the config timeout and the queue job timeout together, or you'll get half-written PDFs.
  • JS-heavy pages. Prefer server-rendered HTML; if you must, use --javascript-delay (ms) rather than --window-status. Complex charts often need --no-stop-slow-scripts and can still segfault on large DOMs — render chart images server-side instead.
  • Stale and effectively abandoned. Last release was January 2018 (bundles 0.12.4); upstream is at 0.12.6.1 with security and font fixes. Treat this as a pinned, unpatched dependency: no CVE fixes will arrive. Plan the migration now — either a newer static build of wkhtmltopdf, a maintained Docker image, or an HTTP PDF service behind your PdfRenderer interface.
  • Never commit the binary or copy vendor/ into the repo; keep vendor/ out of the webroot and out of any path the app can serve.
  • Extension points worth adding early: a PdfRenderer contract (swap Snappy → sidecar → cloud without touching call sites), an artisan pdf:check command that runs --version and renders one fixture, and a WKHTMLTOPDF_BINARY env override so dev machines without the i386 binary can point at a local build.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin