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

Zendpdf Laravel Package

zendframework/zendpdf

ZendPdf is a PHP library for creating and manipulating PDF documents. It supports drawing text, images, and shapes, page management, and importing existing PDFs. Originally part of Zend Framework, it is a legacy component and no longer actively maintained.

View on GitHub
Deep Wiki
Context7

Getting Started

zendframework/zendpdf is the standalone extraction of ZF1's Zend_Pdf: a pure-PHP library that can both write PDFs from scratch and load, modify, and re-save existing PDFs. It needs no GD, no ImageMagick, and no headless browser for text-and-vector work. That "load an existing PDF" capability is its main differentiator versus dompdf/mpdf.

Two-minute setup:

composer require zendframework/zendpdf

Composer will print an "abandoned" warning — expected, see Gotchas. Then generate a file:

use ZendPdf\PdfDocument;
use ZendPdf\Page;
use ZendPdf\Font;
use ZendPdf\Color\Html;

$pdf  = new PdfDocument();
$page = $pdf->newPage(Page::A4);                 // also appended to $pdf->pages
$page->setFont(Font::fontWithName(Font::FONT_HELVETICA), 11);
$page->setFillColor(new Html('#222222'));

$y = $page->getHeight() - 50;                    // origin is bottom-left, y grows up
$page->drawText('Invoice #1042', 40, $y, 'UTF-8');

$pdf->save(storage_path('app/invoices/1042.pdf'));

Where to look first: the five classes you will use 95% of the time are PdfDocument (pages, load/save/render), Page (all drawing methods), Font (fontWithName / fontWithPath), Color\* (Html, Rgb, GrayLevel), and Image::imageWithPath(). Read the Page class methods once end-to-end — that is the API surface. Skip everything else.

First real use case: invoice or report generation where you already have a letterhead/stationery PDF. Load the template, overlay the variable data, save. That workflow is where this package still earns its keep.

Implementation Patterns

Template overlay (the killer feature). Load a static PDF, clone the page into a fresh document, draw on top:

$template = PdfDocument::load(resource_path('pdf/letterhead.pdf'));
$pdf  = new PdfDocument();
$page = clone $template->pages[0];       // clone: never mutate the source object
$pdf->pages[] = $page;

$page->setFont(Font::fontWithName(Font::FONT_HELVETICA), 10);
$page->drawText($customer->name, 60, $page->getHeight() - 180, 'UTF-8');
$pdf->save($out);

Multi-page reports with a cursor helper. There is no flow layout, so wrap it once:

final class Cursor
{
    public function __construct(
        private PdfDocument $pdf,
        private Font $font,
        private float $size = 10,
        private float $left = 40,
        private float $bottom = 50,
    ) {}

    public function line(string $text): void
    {
        $page = end($this->pdf->pages) ?: $this->pdf->newPage(Page::A4);
        if ($this->y < $this->bottom) {
            $page = $this->pdf->newPage(Page::A4);
        }
        $page->setFont($this->font, $this->size);
        $page->drawText($text, $this->left, $this->y, 'UTF-8');
        $this->y -= $this->size * 1.45;
    }
}

Initialise $this->y = $page->getHeight() - 60 and reset it on every newPage().

Laravel wiring. There is no Laravel integration and you don't need one — a single injectable service is enough:

class InvoicePdf
{
    public function render(Invoice $invoice): string
    {
        // build $pdf ...
        return $pdf->render();           // raw bytes
    }
}
return response($invoicePdf->render(), 200, [
    'Content-Type'        => 'application/pdf',
    'Content-Disposition' => 'inline; filename="invoice.pdf"',
]);

For big documents prefer save() to a temp path and response()->file($path) so you aren't holding the rendered string plus the document in memory.

Images. Dimensions are in points, not pixels — convert explicitly:

$img = Image::imageWithPath(public_path('logo.png'));
$w   = 120;                                                     // points
$h   = $w * $img->getPixelHeight() / $img->getPixelWidth();
$page->drawImage($img, 40, $page->getHeight() - 40 - $h, $w, $h);

Merging documents. Push page objects from several sources into one PdfDocument's pages array (clone them if the source may outlive the merge). Useful for concatenating pre-rendered sections — e.g. a cover page from one file plus body pages from another.

Custom fonts for anything non-ASCII. Font::fontWithPath(base_path('resources/fonts/DejaVuSans.ttf')) embeds and subsets a TrueType font; then drawText(..., 'UTF-8') produces correct glyphs. Do this the moment you need €, accents, or non-Latin text.

Gotchas and Tips

  • The package is archived. Last releases target PHP 5.3–5.6. On PHP 8 expect deprecation noise and possibly hard failures (each(), curly-brace string offsets, dynamic properties). Run a smoke test on your target PHP before designing around it, and isolate it behind a PdfGenerator interface so swapping to tecnickcom/tcpdf, mpdf, or Browsershot is a one-file change.
  • Coordinates are bottom-left, y-up. Every tutorial you'll find for FPDF/TCPDF uses top-left, y-down. Do the getHeight() - offset conversion once in a helper and never inline it again.
  • drawText positions the text baseline, not the top of the glyphs. Reserve ~0.75 × fontSize of leading or your first line will look clipped against a border.
  • No text wrapping, tables, HTML, or page-break logic. You measure and break lines yourself. ZendPdf's font classes give you unitsPerEm and glyph metrics; in practice most teams precompute approximate character widths or use a monospace font for tabular columns.
  • Standard-14 fonts are WinAnsi only. Passing UTF-8 to Helvetica silently mangles characters. If the input is user-supplied, embed a TTF.
  • newPage() registers the page for you ($pdf->pages[] happens inside). Calling it and then also appending manually produces blank pages.
  • Clone template pages. Drawing on $template->pages[0] directly mutates the loaded document; if you cached it or reuse it across a request, the previous overlay bleeds through.
  • No support for encrypted PDFs, AcroForm fields, annotations, digital signatures, or PDF/A. load() will throw a ZendPdf\Exception\RuntimeException on anything structural it doesn't understand. Catch broadly around load() and fall back to generating from scratch.
  • Memory scales with page count and image size. Image data is embedded into the output; a 6 MB JPEG at full resolution bloats the PDF and the PHP heap. Downscale before imageWithPath().
  • Debug by writing files, not by echoing. save('/tmp/out.pdf') and inspect with pdfinfo, pdftotext, or open in a viewer. When text "disappears", it is almost always a y-coordinate below zero, a missing setFont() on a new page, or a fill colour matching the background.
  • Page size constants (Page::A4, LETTER, …) are the reliable route; if you need landscape, create the page then check getWidth()/getHeight() and lay out against those rather than assuming a fixed 595×842.
  • render() vs save() — render() returns the full byte string; for anything above a few MB, write to disk and stream the file.
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.
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
noith/settings
kda/laravel-package