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

dskripchenko/php-pdf

A PHP package for generating PDF documents, with support for Laravel applications. Build invoices, reports, and other printable documents programmatically through a straightforward API for creating and customizing PDF files.

Deep Wiki
Context7

Getting Started

Install and verify the runtime.

composer require dskripchenko/php-pdf

Pure PHP means no ImageMagick, Ghostscript, or external PDF binaries at runtime, but check composer.json for required extensions — typically mbstring, zlib, gd (images/charts), and openssl (PKCS#7 signing). Enable openssl in production if you plan to sign documents.

Where to look first, in order:

  1. README.md — feature matrix and the canonical "hello world".
  2. examples/ — the fastest way to learn the real API; the package ships runnable scripts for HTML, barcode, chart, merge, form, and signing flows.
  3. tests/ — fixtures show expected inputs/outputs, edge cases, and how PDF/A and PDF/X modes are asserted.
  4. src/ — look for the Document/Pdf entry point, then Page, Html, Barcode, Chart, Form, Signature, and Merge namespaces. Extension interfaces (font resolver, image loader, signer) usually live near these.

Minimal first document:

use Dskripchenko\PhpPdf\Document;

$pdf = Document::make('A4');           // or [595.28, 841.89] in points
$page = $pdf->addPage();
$page->text(40, 60, 'Invoice #1001', ['size' => 16]);
$pdf->save(storage_path('app/invoices/1001.pdf'));

First real use case: an HTML invoice. Render a Blade view to a string, feed it to the HTML importer, and write the result to Laravel's storage. This is the highest-leverage feature because it replaces mpdf/TCPDF for the 80% case (invoices, reports, certificates) without adding a dependency.

$html = view('pdf.invoice', ['invoice' => $invoice])->render();

$pdf = Document::make('A4');
$pdf->addPageFromHtml($html, [
    'basePath' => public_path(),       // resolves relative <img> and CSS
    'margin'   => [40, 40, 40, 40],
]);

Storage::disk('local')->put("invoices/{$invoice->id}.pdf", $pdf->output());

Start with one page, one font, one image. Once that renders correctly, layer in headers/footers, barcodes, and page breaks.

Implementation Patterns

Wrap generation in a single service, not in controllers. Keep the PDF package behind an application-level interface (InvoicePdfGenerator, ReportPdfGenerator) so a future swap to mpdf or a headless-Chrome sidecar is a one-class change. The package is young (11 stars), so isolate the blast radius.

final class InvoicePdf
{
    public function __construct(private Invoice $invoice) {}

    public function render(): string
    {
        $pdf = Document::make('A4');
        $pdf->addPageFromHtml(
            view('pdf.invoice', ['invoice' => $this->invoice])->render(),
            ['basePath' => public_path()]
        );
        return $pdf->output(); // raw bytes
    }
}

HTML/CSS templates: treat it as a print layout engine, not a browser. Use a single-column flow, tables for tabular data, explicit @page sizes/margins, and absolute positioning only for headers/footers. Inline critical CSS or embed it in a <style> block; external stylesheets are fragile. Keep font families explicit and reference fonts you have embedded.

Headers, footers, and page numbering. Most pure-PHP engines expose a page callback or a post-render hook. The reliable pattern is to render the body first, count pages, then stamp footers in a second pass (or use the package's page event if provided). Avoid relying on CSS position: fixed for repeating headers unless the HTML importer explicitly documents support.

Barcodes for operational documents. Generate Code128/QR/EAN directly into the page rather than as pre-rendered PNGs — vector output stays crisp and avoids GD. Size by X-dimension and quiet zone, not by pixel width.

$page->barcode('CODE128', $invoice->number, [
    'x' => 40, 'y' => 760, 'width' => 180, 'height' => 40,
]);

Charts for reports. Build charts from arrays (labels/series) and embed as vector drawing. For complex dashboards, generate the chart server-side as SVG/PNG and embed the image; the built-in chart API is best for simple bar/line/pie.

Fonts and branding. Register TTF files once (a service provider or a bootstrap in config/pdf.php) and reuse them. Embed and subset fonts for archival output. If you need a corporate font, confirm the license permits embedding and subsetting.

AcroForm workflows. Use forms when downstream systems must fill fields (government forms, onboarding packets). Fill values, then choose:

  • flatten() for a final, immutable PDF; or
  • leave fields editable and set NeedAppearances/appearance streams if viewers must render values.

Always verify field names from the source PDF (pdftk dump_data_fields or qpdf --json) before filling.

Signing workflow. Signing must be the last write. Load the finished PDF, attach the PKCS#7 signature, optionally add a timestamp, and then persist without further mutation. Keep the certificate and private key outside the repo, load the passphrase from secrets, and log the certificate serial and signing time for auditability.

$pdf = Document::load($unsignedPath);
$pdf->sign([
    'certificate' => config('pdf.cert'),
    'private_key' => config('pdf.key'),
    'password'    => config('pdf.key_password'),
    'reason'      => 'Invoice approval',
    'location'    => 'Berlin',
    'timestamp'   => config('pdf.tsa_url'),
]);
$pdf->save($signedPath);

Merge, split, and stamp. Merging is a resource-level operation: concatenate pages, then deduplicate fonts/images if the API exposes it. For batch workflows (statements, claims), merge per-customer documents, stamp a cover page, and stream the result. Read existing PDFs to extract page counts or text before merging so you can build a table of contents.

PDF/A vs PDF/X. Choose the profile at document creation, not after:

  • PDF/A (archival): embed all fonts, no encryption, no transparency (flatten), XMP metadata, output intent. Validate with veraPDF in CI.
  • PDF/X (print): CMYK color, trim/bleed boxes, output intent, no RGB-only assets. Validate with a preflight tool.

Laravel integration tips.

  • Put paths, fonts, certificate locations, and default page size in config/pdf.php; publish and cache it.
  • Generate in a queued job for anything over a few pages; write to Storage, then notify or expose a signed URL.
  • For downloads, return response()->streamDownload(fn () => print $bytes, 'invoice.pdf', ['Content-Type' => 'application/pdf']) or serve the stored file; don't hold large byte strings in the session.
  • Bind a singleton font/asset registry in a service provider to avoid re-registering fonts per request.
  • Use Storage::disk('local')->path(...) for temp files so paths are absolute and consistent.

Testing.

  • Assert text extraction (pdftotext -layout) rather than binary hashes.
  • Run qpdf --check on every generated fixture.
  • Run veraPDF for PDF/A fixtures; compare page counts against source.
  • For visual regressions, render pages to PNG with mutool draw and diff.
  • Freeze document metadata (creation date, document ID) if you need deterministic output; otherwise tests will flake on timestamps.

Gotchas and Tips

The HTML importer is a subset, not Chromium. No JavaScript, limited selectors, and unreliable flex/grid support. CSS that works in a browser may silently collapse. Debug by dumping the exact HTML string and rendering it standalone; simplify to tables and block layout when output is wrong.

Coordinate origin is bottom-left in PDF space. Many wrappers expose top-left helpers, but not all methods agree. If content appears mirrored or off-page, check whether the Y coordinate is being flipped. Keep a small helper (mm(), pt()) and use one unit convention everywhere.

Unicode needs the right font. Standard PDF fonts (Helvetica, Times) cover Latin-1 only. For Cyrillic, CJK, emoji, or currency symbols, embed a TTF with those glyphs. Missing glyphs often render as blanks rather than throwing — test with real customer data.

OTF/CFF fonts and variable fonts may not be supported. Prefer static TTF. If embedding fails, convert with fonttools (fonttools varLib.instancer or otf2ttf) before shipping.

Images: downscale before embedding. A 4000px photo decoded into memory can blow the PHP memory limit. Resize to target DPI (150–300) first, prefer JPEG passthrough, and set memory_limit/max_execution_time for large jobs. Remote image URLs are often disabled or unreliable — download to a temp path and pass an absolute path.

Temp files and concurrency. Pure-PHP PDF writers often use a temp directory. Ensure it is writable and unique per request (or per queued job); shared filenames cause race conditions under load. Clean up in a finally.

Signing breaks on any subsequent save. A signature covers a byte range; modifying the document afterward invalidates it. Sign last, copy the signed bytes to storage, and never re-open-and-save. If you need post-sign metadata, use an incremental update that the signer explicitly supports.

Certificate and TSA details matter. Use PEM certificate + private key with openssl_pkcs7_sign under the hood (or the package’s signer). Test with a real timestamp authority; some TSAs require SHA-256 and reject SHA-1. Check certificate chain inclusion and revocation settings for your compliance regime.

PDF/A validation failures are usually one of four things: unembedded fonts, transparency, missing XMP metadata, or encryption. Fix them at generation time — post-processing with external tools defeats the "dependency-free" advantage.

Merge loses more than pages. Outlines, named destinations, AcroForm fields, attachments, and page labels are frequently dropped or collide. Flatten forms before merging unless you have a specific reason to preserve them. If bookmarks matter, rebuild them after merge.

Reading third-party PDFs is the riskiest operation. Encrypted files, cross-reference streams, and malformed producers can fail. Normalize incoming files with qpdf in a preprocessing step if you control the pipeline; otherwise wrap reads in try/catch and fall back to an error state rather than a 500.

Encrypted PDFs: distinguish owner-password (permissions only) from user-password (content encrypted). Many readers can open owner-password files; user-password files need the password. Don’t assume the package handles both.

Barcodes need quiet zones and correct X-dimension. A barcode that scans on screen can fail on a laser scanner if it is scaled non-uniformly or cropped. Render at final size, keep at least 10× the module width as quiet zone, and validate with a real scanner or zbarimg on a rendered page.

Chart fonts and colors. Charts may use a separate default font from body text; set it explicitly. For print, convert RGB chart colors to CMYK-safe equivalents, or accept color shift.

Configuration caching in Laravel. If you publish config/pdf.php, remember php artisan config:cache in deploy. Paths computed at runtime (e.g., storage_path() inside the config file) can go stale; prefer resolving paths in a service, not in config.

Package maturity. At 11 stars and a recent release, the API may shift. Pin an exact version in composer.json, read the changelog before upgrading, and keep the wrapper interface thin. Expect to read source for undocumented behavior — the tests are often more current than the README.

Debugging toolkit. Keep these installed locally: qpdf --check, qpdf --qdf --object-streams=disable in.pdf out.pdf (readable PDF for inspection), mutool draw -o page-%d.png file.pdf (visual check), pdftotext -layout (content check), pdfinfo (metadata/page size), veraPDF (PDF/A), and zbarimg (barcodes). They turn "the PDF is broken" into a specific, fixable defect.

Extension points to look for. Custom font resolvers (loading fonts from storage/S3), image loaders (auth-protected URLs), barcode/QR renderers, chart drivers, HTML node handlers, and signer implementations. These are the seams for adapting the package to Laravel-specific infrastructure without forking.

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.
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
uni-method/symfony-lib