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

Php Pdf Laravel Package

psliwa/php-pdf

Pure-PHP library for generating PDF documents. Build pages with text, images, shapes, colors, and barcodes; supports fonts, transformations, and simple drawing APIs. Useful for reports, invoices, and dynamic PDFs without external binaries.

View on GitHub
Deep Wiki
Context7

Getting Started

Gate on the environment before you write a line of code. This is a Composer install plus a native extension plus a PHP compatibility question, and any of the three can kill the spike in an hour.

# 1. Does it even install against your PHP?
composer require psliwa/php-pdf --dry-run
# 2. Is Imagick present in the runtime that will actually render?
php -m | grep -i imagick
php -r 'var_dump(extension_loaded("imagick") ? Imagick::getVersion() : null);'

If Composer resolves, immediately pin the exact installed version in composer.json and commit composer.lock. Do not use a caret constraint — upstream is dormant and a stray composer update is a production incident.

Where to look first, in order:

  1. examples/ in the vendor directory — these are the real documentation. There is no Laravel-facing docs, no service provider, no config publishing.
  2. The XML document definition (document.xml in examples) and the accompanying stylesheet. That pairing is the whole mental model: structure in XML, presentation in a CSS-like file.
  3. Psliwa\Pdf\Builder\PdfBuilder — the programmatic builder, if you prefer to construct pages in PHP instead of XML.
  4. PHPPdf\Core\FacadeBuilder — the thin entry point most examples use for file-to-file rendering.

Minimal first render (file-to-file, the fastest path to "it works"):

use PHPPdf\Core\FacadeBuilder;

$facade = FacadeBuilder::create()->build();

$facade->createPdf(
    resource_path('pdf/invoice.xml'),
    storage_path("app/pdf/invoices/{$invoice->id}.pdf"),
    resource_path('pdf/invoice.css'),
    'utf-8'
);

Start with a hardcoded two-line invoice template. Confirm the PDF opens in a viewer, then confirm it opens in your actual target consumer (email clients, accounting exports, print shops) — visual acceptance is the real test, not file_exists().

First real use case: the invoice or receipt. It is the document with the highest business value, the lowest layout complexity, and the clearest success criteria. Resist starting with the 200-line transaction statement; get one page right before you test auto-pagination.

Do not trust the repository description. It advertises reading and parsing existing PDFs and text extraction. The library is overwhelmingly a generator with an XML/CSS layout DSL. If you need parsing or text extraction, reach for smalot/pdfparser and keep this package focused on creation.

Implementation Patterns

Wrap it behind a contract from day one. The library is framework-agnostic, unmaintained, and you will likely replace it. One interface, one adapter, zero direct PHPPdf\* references in controllers or models.

// app/Contracts/PdfRenderer.php
namespace App\Contracts;

interface PdfRenderer
{
    /** @param array<string, mixed> $data */
    public function render(string $template, array $data): string; // returns PDF bytes
}
// app/Services/PhpPdfRenderer.php
namespace App\Services;

use App\Contracts\PdfRenderer;
use PHPPdf\Core\FacadeBuilder;
use Illuminate\Support\Facades\Storage;

class PhpPdfRenderer implements PdfRenderer
{
    public function render(string $template, array $data): string
    {
        $xml  = view("pdf.{$template}.xml", $data)->render();
        $css  = Storage::disk('pdf')->get("styles/{$template}.css");

        $tmpXml = tempnam(sys_get_temp_dir(), 'pdf_xml_');
        $tmpOut = tempnam(sys_get_temp_dir(), 'pdf_out_');

        try {
            file_put_contents($tmpXml, $xml);

            FacadeBuilder::create()
                ->build()
                ->createPdf($tmpXml, $tmpOut, /* stylesheet path or contents */);

            return file_get_contents($tmpOut);
        } finally {
            @unlink($tmpXml);
            @unlink($tmpOut);
        }
    }
}

Bind it in a service provider: $this->app->bind(PdfRenderer::class, PhpPdfRenderer::class);.

Keep templates in Blade, not as static XML. You get escaping, loops, and partials for free — and it solves the number-one correctness bug (unescaped & and < in customer names breaking the XML parser).

{{-- resources/views/pdf/invoice/xml.blade.php --}}
<document>
  <page>
    <text font-size="18" font-weight="bold">{{ $invoice->number }}</text>
    <text>Billed to: {{ $invoice->customer->name }}</text>
    @foreach ($invoice->lines as $line)
      <text>{{ $line->description }}</text>
      <text align="right">{{ number_format($line->amount, 2) }}</text>
    @endforeach
  </page>
</document>

Render out-of-band, always. Never build a PDF in the request cycle beyond a trivial page. Push to a queued job with explicit limits:

class GenerateInvoicePdf implements ShouldQueue
{
    public int $timeout = 120;
    public int $tries = 3;

    public function handle(PdfRenderer $renderer): void
    {
        ini_set('memory_limit', '512M');

        $bytes = $renderer->render('invoice', ['invoice' => $this->invoice]);

        // atomic write: temp then move, never stream directly to the final disk path
        Storage::disk('invoices')->put(
            "{$this->invoice->id}.pdf",
            $bytes
        );
    }
}

Use a dedicated queue (->onQueue('pdf')) so a memory-hungry render cannot starve general workers.

Model long documents explicitly. The library's differentiator is flowing multi-page output with repeated headers and footers, but you must give it structure: a page node with auto-height, breakable content, and a footer template with a page-number placeholder. Test with a 200-row table on day one — that is the fixture that exposes pagination bugs.

Add a second adapter immediately — MpdfRenderer or BrowsershotRenderer implementing the same contract. It costs an hour, it gives you an A/B benchmark, and it means the exit ramp exists before you are committed.

Golden-file tests in CI. Visual DSLs regress silently. Render each supported template from fixed fixtures and either snapshot a hash or rasterise page 1 with Imagick and compare against a committed PNG with a small pixel tolerance. Text-based assertions alone will miss the failure modes that matter here.

Resolve fonts and images explicitly. Route every asset path through Storage::disk(...)->path() or a configured absolute path in config/pdf.php. Never rely on the library's default working-directory assumptions; they differ between CLI, FPM, and queue workers.

Gotchas and Tips

  • Imagick is mandatory and fails fatally. A missing extension produces a Class 'Imagick' not found fatal, not a catchable exception. Add a boot-time health check (a health endpoint or a queued VerifyPdfRenderer job) so you learn about it at deploy, not at 2 a.m.
  • PHP 8 compatibility is the first real question. Release cadence is old and the dependency tree carries Zend Framework-era packages. Expect deprecation warnings that escalate to fatals after a PHP minor bump. Pin the runtime image, not just the Composer lock.
  • Fork and vendor rather than depend on Packagist. Add a repositories entry pointing at your VCS fork so patches ship on your schedule. Budget an ongoing owner — an unowned fork is worse than no dependency.
  • XML escaping will bite you. Customer names with &, <, ", or CJK characters corrupt the document or the layout. Blade's {{ }} handles the escaping; free-form string concatenation does not.
  • The DSL is idiosyncratic and thinly documented. Expect to learn style attribute names by experimentation. Keep a resources/pdf/cookbook.md with working snippets — this knowledge is non-transferable and leaves with the developer.
  • Unicode, RTL, and complex scripts are unverified. Test with the actual strings your customers produce (accented names, CJK, emoji in descriptions) before promising coverage. Assume Latin-1-era typography until proven otherwise; mPDF is the safer answer for international documents.
  • Memory is the scaling ceiling. Whole-document-in-memory rendering means hundreds of MB for image-heavy statements. Enforce a hard cap on line-item count and image weight before dispatch, and reject oversized requests explicitly instead of letting a worker OOM.
  • Concurrency and temp files. Imagick uses a shared temp directory; set a unique Imagick::setRegistry('temporary-path', ...) or ensure per-worker scratch space. Write output to a unique temp file then rename — partial PDFs from timed-out jobs are worse than failures.
  • Barcode generation drags in Zend dependencies. If you need barcodes, verify that path early; it is the most likely place for an abandoned-package conflict.
  • Debug by bisecting the document. When a render fails, cut the XML down to one node and re-add until it breaks. Log the generated XML on failure — an unlogged Exception from a queue worker with a truncated stack trace is the usual debugging experience.
  • Extension points worth knowing: custom node types and stylesheet attributes are the intended way to add domain-specific markup (e.g. a <total> node). Prefer extending the node system over post-processing the generated PDF.
  • Set a decommission trigger in writing. "If upstream sees no commits for 12 months, or we patch more than N times a year, migrate to mPDF/Browsershot." Decide that while the spike is still fresh, not after the second emergency upgrade.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource