Generating Invoices with Zend_Pdf

Share this article

The PDF format is currently the most used format to exchange documents. If you provide your website users with printable versions of invoices, event tickets and other similar documents, you’ll most likely want to generate them as PDFs on the fly. In this article you will see how you can use Zend_Pdf to auto-generate PDF invoices.

Getting Started

Zend_Pdf allows you to create new PDF documents or load existing ones and modify them. While creating a new document isn’t that difficult, it makes more sense to create a template of the invoice with the information that doesn’t change (company name, address, logo, etc.). It’s better to design your invoice in a program like Microsoft Word or LibreOffice Writer and export it as a PDF. Then you can update the PDF with Zend_Pdf with customer and transaction-specific information. You’ll save coding time because you don’t have to position every element, and it will take less time for the PDF to be generated. This is what the invoice template I will be using in this article looks like. Your invoice can look different depending on your needs, but the concepts in this article will remain the same.

invoice template

If this is the first time you’re using Zend Framework, download the latest version from www.zendframework.com, upload and unpack the compressed file on your server, and use the following lines at the top of your script to configure the class autoloader. The autoloader will let you create new objects without having to explicitly include all the required files in your code.
<?php
define("ZF_PATH", realpath("/path/to/zf/library/"));
set_include_path(get_include_path() . PATH_SEPARATOR . ZF_PATH);
require_once "Zend/Loader/Autoloader.php";
$loader = Zend_Loader_Autoloader::getInstance();
Remember to change the value of the ZF_PATH constant to the path where you uploaded the copy of the Zend Framework library. Editor Note Oct 14 2012: The accompanying code on GitHub has been updated to use Composer for installing Zend Framework. As ZF2 is not backwards compatible with ZF1, the version has been locked at 1.11.11. Please refer to the code for suitable ZF_PATH.

Loading the PDF Template

Zend_Pdf allows you to load an existing PDF document with the static load() method.
<?php
// load the invoice
$invoice = Zend_Pdf::load("/path/to/invoice-template.pdf");
The load() method reads the file at the given path and returns an instance of a Zend_Pdf object which manages everything related to the PDF. Zend_Pdf represents each page of the PDF as instances of the Zend_Pdf_Page object. Its public field pages is an array of the objects in the same order the pages are found in the document. You can benefit from this behavior if you have a PDF document with many pages and you want to reorder its pages; whichever order the Zend_Pdf_Page objects are in the pages array, that is the order the pages will be rendered in the final document.
<?php
// access the first page
$page = $invoice->pages[0];

Specifying Fonts and Colors

When you write text to the PDF page, you are actually drawing shapes that looks like text, and these shapes are defined by the chosen font. So before writing/drawing text, you must specify which font you want to use. The Zend_Pdf_Font class is used to create a font resource which you then use to specify the font used when placing text. There are two ways to load a font. The first is to use the static method fontWithName() and provide one of the following constants that represent the 14 standard fonts that all PDF viewers support:
  • Zend_Pdf_Font::FONT_COURIER (_BOLD, _ITALIC, _BOLD_ITALIC)
  • Zend_Pdf_Font::FONT_HELVETICA (_BOLD, _ITALIC, _BOLD_ITALIC)
  • Zend_Pdf_Font::FONT_TIMES (_BOLD, _ITALIC, _BOLD_ITALIC)
  • Zend_Pdf_Font::FONT_SYMBOL
  • Zend_Pdf_Font::FONT_ZAPFDINGBATS
The second way is to provide the path to the font file that resides on your file system to the static method fontWithPath(). Make sure that the font is a TrueType font or else fontWithPath() will throw a Zend_Pdf_Exception.
<?php
//loading a font by its name
$font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_TIMES_BOLD);

//load font from file system
$font = Zend_Pdf_Font::fontWithPath("/path/to/myfont.ttf");
The font resource is then passed to the Zend_Pdf_Page‘s setFont() instance method as the first parameter, and the font size measured in points as the second parameter (1 point = 1/72 inches). The represented font will then be used whenever text is drawn to that page.
<?php
$page->setFont($font, 12);
It’s not mandatory to set a color for text before drawing it; black will be used by default if none is specified. But if do you want to use a certain color, you can specify one with one of the Zend_Pdf_Color* objects. Zend_Pdf supports three different color spaces; you can use Gray Scale, RGB, or CMYK. Also, you can specify colors using HTML-style notation.
<?php
// Gray Scale colors range from 0.0 (black) to 1.0 (white)
$color = new Zend_Pdf_Color_GrayScale(0.7);

// RGB uses 3 float values from 0.0 to 1.0 for each color component
$color = new Zend_Pdf_Color_Rgb($r,$g,$b);

// CMYK uses 4 float values from 0.0 to 1.0 for each color component
$color = new Zend_Pdf_Color_Cmyk($c,$m,$y,$k);

// HTML uses any valid color name or hex notation
$color = new Zend_Pdf_Color_HTML("blue");
$color = new Zend_Pdf_Color_HTML("#FF52ED");
The color is then passed to the setFillColor() method of the Zend_Pdf_Page instance.
<?php
$page->setFillColor($color)

Adding Content

Drawing text on a page is done using Zend_Pdf_Page
‘s drawText() instance method. It takes a string of text, and the X and Y coordinates where you want the text to be placed. The coordinates are measured in points, and the origin 0,0 is placed at the bottom-left corner of the page. An increasing X value moves the position towards the right, and an increasing Y value moves the position up.

PDF X,Y coords

Typically you’ll retrieve and calculate information to put on the invoice from a database, but for the sake of example I’ve come up with the following:
<?php
$customerName = "Angelina Jolie";
$invoiceId = "DF-00025786423";

// items in the array are product description,
// quantity purchased, unit price, and total price
$items = array(array("Golden Globe Polish", 1, 25.50, 25.50),
               array("Trophy Shelf", 2, 180.00, 360.00),
               array("DIY Tattoo Kit", 1, 149.99, 149.99));

$subtotal = 535.49;
$discount = 10;
$amountDue = 481.94;
The trickiest part is finding the correct location to place the text on the page and you may require some trial and error even if you’ve meticulously measured your distances. The good news is that since the template elements are static, once you know what coordinates to use they won’t change.
<?php
// specify font
$fontBold = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_TIMES_BOLD);
$page->setFont($fontBold, 12);

// specify color
$color = new Zend_Pdf_Color_HTML("navy");
$page->setFillColor($color);

$page->drawText($customerName, 110,641);
// another font
$fontNormal = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_TIMES);
$page->setFont($fontNormal, 12);

// invoice information
$page->drawText($invoiceId, 420,642);
$page->drawText(date("M d, Y"), 420,628);
$page->drawText('$' . number_format($subtotal, 2), 510,143);
$page->drawText($discount . "%", 510,123);
$page->drawText('$' . number_format($amountDue, 2), 510,103);

// purchase items
$posY = 560;
foreach ($itmes as $item) {
    $page->drawText($item[0], 50, $posY);
    $page->drawText($item[1], 350, $posY);
    $page->drawText(number_format($item[2], 2), 430, $posY);
    $page->drawText(number_format($item[3], 2), 510, $posY);
    $posY -= 22.7;
}

Rendering the Invoice

After adding the information to the template, you can either save it to a different file (be careful not to overwrite your template!) or send it to the browser. To save it, you use Zend_Pdf‘s instance method save().
<?php
$invoice->save($pathToFile);
Using the instance method render() will return the PDF document as a string. This is probably what you want if you are generating a document on the fly and want the user to download it without saving copy of the document on your server first. You can output the correct HTTP headers so the browser will know how to handle the file, and then send the document string.
<?php
// instruct browser to download the PDF
header("Content-Type: application/x-pdf");
header("Content-Disposition: attachment; filename=invoice-". date("Y-m-d-H-i") . ".pdf");
header("Cache-Control: no-cache, must-revalidate");

// output the PDF
echo $invoice->render();

Summary

Now you know the basics of using Zend_PDF to create PDF documents, though there are still many more features available than the ones I’ve mentioned in this article. The source code for this article is available on GitHub if you’d like to clone it and experiment. And of course, feel free to leave comments with any problems you encounter. Image via Christina DeRidder / Shutterstock

Frequently Asked Questions (FAQs) about Generating Invoices with Zend PDF

How Can I Install Zend PDF in My Project?

To install Zend PDF in your project, you need to use Composer, a tool for dependency management in PHP. You can install it by running the following command in your terminal: composer require zendframework/zendpdf. This command will download and install the Zend PDF library in your project. Make sure you have Composer installed in your system before running this command.

How Can I Create a New PDF Document Using Zend PDF?

Creating a new PDF document using Zend PDF is straightforward. You need to use the ZendPdf\PdfDocument class. Here is a simple example: $pdf = new ZendPdf\PdfDocument();. This line of code will create a new, empty PDF document.

How Can I Add Text to a PDF Document?

To add text to a PDF document, you need to create a new page using the ZendPdf\Page class and then add it to the document. Here is an example: $page = new ZendPdf\Page(ZendPdf\Page::SIZE_A4); $page->drawText('Hello world!', 72, 720); $pdf->pages[] = $page;.

How Can I Save a PDF Document to a File?

To save a PDF document to a file, you can use the save() method of the ZendPdf\PdfDocument class. Here is an example: $pdf->save('path/to/file.pdf');. This line of code will save the PDF document to the specified file.

How Can I Load an Existing PDF Document?

To load an existing PDF document, you can use the load() method of the ZendPdf\PdfDocument class. Here is an example: $pdf = ZendPdf\PdfDocument::load('path/to/file.pdf');. This line of code will load the specified PDF document.

How Can I Add an Image to a PDF Document?

To add an image to a PDF document, you need to create an image resource using the ZendPdf\Image class and then draw it on a page. Here is an example: $image = ZendPdf\Image::imageWithPath('path/to/image.jpg'); $page->drawImage($image, 72, 720, 144, 820);.

How Can I Set the Font and Size of Text?

To set the font and size of text, you can use the setFont() and setFontSize() methods of the ZendPdf\Page class. Here is an example: $font = ZendPdf\Font::fontWithName(ZendPdf\Font::FONT_HELVETICA); $page->setFont($font, 12);.

How Can I Add a Link to a PDF Document?

To add a link to a PDF document, you need to create a new annotation using the ZendPdf\Annotation class and then add it to a page. Here is an example: $annotation = new ZendPdf\Annotation\Url('http://www.example.com'); $page->attachAnnotation($annotation);.

How Can I Add a Table to a PDF Document?

Adding a table to a PDF document is a bit more complex. You need to manually calculate the position of each cell and draw them using the drawRectangle() and drawText() methods of the ZendPdf\Page class.

How Can I Handle Errors in Zend PDF?

Zend PDF uses exceptions to handle errors. You can catch these exceptions using a try-catch block. Here is an example: try { $pdf = ZendPdf\PdfDocument::load('path/to/file.pdf'); } catch (ZendPdf\Exception\ExceptionInterface $e) { echo 'Error: ', $e->getMessage(); }.

Ahmed ShreefAhmed Shreef
View Author

Ahmed Shreef is an Egyptian software engineer. He has been an active contributor to the open source software community since 2006 and is the co-founder of Eventtus. When he's not coding, Ahmed likes to spend his time analyzing how people behave online. You can learn more about him by visiting Shreef.com or you can follow him on twitter as @shreef .

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week