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

Laravel Pdflib Laravel Package

contoweb/laravel-pdflib

A Laravel wrapper for PDFlib that brings the PDFlib API into your Laravel apps for generating and manipulating PDF documents. Create, edit, and render PDFs with a familiar facade-style interface and framework-friendly configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

contoweb/laravel-pdflib is a thin Laravel wrapper around PDFlib (PDFlib GmbH's commercial PDF engine), not an HTML-to-PDF renderer. If your mental model is "Blade template in, PDF out", stop now and reach for Browsershot/dompdf instead. This package is the right tool when you need precise, coordinate-level control: stamping data onto an existing PDF, generating print-ready documents with exact typography, embedding fonts, barcodes, or PDF/A output.

Minimum setup:

  1. composer require contoweb/laravel-pdflib
  2. Install the underlying PDFlib PHP extension for every PHP SAPI you run (FPM, CLI, queue workers) — the package cannot work without it.
  3. Publish the config: php artisan vendor:publish --tag=pdflib (verify the tag in the README; this package's config holds at minimum your license key and often font/template paths).
  4. Put your PDFlib license key in .env, never in the committed config.

Look first at, in order: the published config/pdflib.php, the facade's public surface (it exposes the wrapped PDFlib instance — that instance's method names are the upstream PDFlib API), then the PDFlib API reference itself. The package README will be short; the real documentation lives on PDFlib's site.

First use case that earns its keep: stamping dynamic data onto a designer-supplied PDF template (invoice, certificate, contract). Import the template page, place it with fit_pdi_page, then draw variable text on top. That is where PDFlib beats HTML renderers on fidelity, and it's the workflow most teams adopt first.

Implementation Patterns

Inject the wrapper, don't call the facade in domain code. Resolve the PDFlib wrapper through the container in a dedicated InvoicePdfGenerator / action class. Facades are convenient in a controller but make the generation logic untestable and hide the license/extension dependency from readers.

One document per instance, one instance per document. PDFlib objects carry significant internal state (open documents, loaded fonts, page buffers). Treat the instance as request-scoped, not as a long-lived singleton. If the package binds it as a singleton, override that binding to scoped in your own service provider — see gotchas below.

Template + overlay is the dominant pattern. Compose it as: open_pdi_document on the template → begin_page_ext → fit_pdi_page (place the template page) → load_font → measure with info_textline → show_textline/show_text at computed coordinates → end_page_ext → end_document. Measure before you place, or you will ship overlapping text.

Keep generation out of the request cycle. Generate in a queued job, write to Storage::disk('local') via storage_path(), and return a signed/expiring URL or attach the bytes to a mail. PDFlib is fast but synchronous and memory-hungry; a 200-page document in an FPM worker is a timeout waiting to happen.

Output routing. get_buffer() for in-memory responses (stream with response()->streamDownload() and set Content-Type: application/pdf), or write directly to a path for archival. Never echo PDFlib output — any stray whitespace from a Blade/config file corrupts the byte stream.

Config layering for multi-tenancy. If tenants bring their own license keys or brand fonts, resolve those from your own config/DB layer and pass them into the generator rather than relying on the single published config file.

Testing. You cannot meaningfully assert on PDF bytes. Assert on the inputs to the generator (the data object), and use a fake/smoke test that asserts a non-zero-length, %PDF- prefixed output. Keep a small golden-file test with a checksum only if the PDFlib version is pinned.

Gotchas and Tips

  • The extension-vs-package split is the #1 source of "works on my machine". The Composer package installs fine without the native extension, then fails at runtime. It works in tinker but not in the browser? Your CLI PHP has the extension and FPM doesn't. Same trap for queue workers running a different binary or container image.
  • Unlicensed = watermarked. Without a valid key PDFlib runs in demo mode and stamps output. If you see unexpected marks on generated PDFs, the key isn't being read — check config caching (php artisan config:clear after any .env change).
  • Return codes, not exceptions. The upstream PDFlib API signals failure with 0/false and an error string rather than throwing. Check get_errmsg() when a call fails; the wrapper may or may not convert these to exceptions — confirm before you rely on try/catch.
  • Coordinates are points, origin bottom-left. A4 is 595×842pt. Porting values from a CSS/HTML designer's mockup requires flipping the Y axis and converting px→pt (×0.75).
  • Fonts must be loaded explicitly with absolute paths, and UTF-8 text requires the unicode option on load_font (and a Unicode-capable font). Missing glyphs render as blanks or boxes, not errors.
  • Long-running processes leak document state. Under Octane, Horizon, or a Swoole worker, a singleton PDFlib instance retains documents between requests, producing merged or corrupted output. Bind it scoped or explicitly begin/end documents in a finally block.
  • Memory and buffers. Disable output buffering for large documents and raise memory_limit in the queue worker, not globally.
  • Licensing cost is a product decision, not an implementation detail. PDFlib is commercial per-server/per-CPU. Confirm the license covers your production topology before you build the feature.
  • v3.4.0 upgrade note: this release only adds Laravel 13 support — no API changes or deprecations were announced. Upgrading from 3.3.x is a low-risk composer update; just verify the illuminate/* constraint in composer.json matches your framework version and re-run your generation smoke test against the same PDFlib extension 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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel