Zend Developer Zone
Where the ElePHPants come to learn

Zend Developer Zone

Zend_Pdf tutorial

Incredibly Old Content Warning!

The content you are viewing is more than three years old. It is here for archival purposes. Do not use any advice in this article.

::Jedi Hand Wave:: This is not the information you are looking for. Move along.


By: Alexander Veremyev

Introduction

The Zend_Pdf component of the Zend Framework is intended to allow you to create or manipulate PDF documents from within your applications. In addition to its text handling capabilities, it comes complete with drawing features that allow you to create or manipulate graphical primitives. When working with text Zend_Pdf gives you the option of using the built-in fonts or custom TrueType fonts. Its page manipulation capabilities allow you to create new pages, remove existing pages or change the order of pages already in the document. In this tutorial my goal is to give you an overview of the capabilities of Zend_Pdf.

Loading Zend_Pdf module.

First things first, you should make sure that you have the latest version of the Zend Framework installed and properly configured. If you are in doubt, check out the Zend Framework manual for instructions.

Now, let’s dive right in and look at the code. If you are working in an applications written with the Zend Framework, you can use the loadClass() method to load Zend_Pdf.

// Load Zend_Pdf class 
Zend_Loader::loadClass('Zend_Pdf'); 

However, if you are using Zend_Pdf standalone in your application, you can simply include it with a require_once() call:

// Load Zend_Pdf class 
require_once('Zend/Pdf.php'); 

Creating new or loading existing document.

Once you have the class loaded you can create an instance. PDF document is represented by Zend_Pdf class. There are three ways to accomplish this.

  • Create a new PDF
    // Create new PDF document. 
    $pdf = new Zend_Pdf();
    

  • Load an existing document
     // Load PDF document from a file. 
    $fileName = '/path/to/your/file.pdf'; 
    $pdf = new Zend_Pdf($fileName); 
    

  • Create a document by parsing a string
    // Parse PDF document from a string. 
    $pdf = Zend_Pdf::parse($pdfString); 
    

Document pages

Now that we have our document, we can start manipulating the pages. To access the pages of our document, we turn to the public property ‘pages’. Pages is an array of Zend_Pdf_Page objects. Because it is a native PHP array, any of PHP’s array handling methods can be used to manipulate it. For instance.

// Reverse document pages. 
$pdf->pages = array_reverse($pdf->pages); 

// Add new page 
$pdf->pages[] = new Zend_Pdf_Page(Zend_Pdf_Page::SIZE_A4); 

// Add new page 
$pdf->pages[] = $pdf->newPage(Zend_Pdf_Page::SIZE_A4); 

// Remove specified page. 
unset($pdf->pages[$id]); 

There are two methods that you can use to create a new page. First, you can directly instantiate a Zend_Pdf_Page object and add it to the array yourself. Second, you can use the newPage() method of Zend_Pdf object. newPage() method is a little bit faster, but needs document object. Either way, you end up with a new Zend_Pdf_Page object to work with. Both methods take page size as a parameter. Page size may be specified using one of the following Zend_Pdf_Page class constants.

  • Zend_Pdf_Page::SIZE_A4
  • Zend_Pdf_Page::SIZE_A4_LANDSCAPE
  • Zend_Pdf_Page::SIZE_LETTER
  • Zend_Pdf_Page::SIZE_LETTER_LANDSCAPE

using special pagesize name (names are case insensitive):

  • ‘A4′
  • ‘A4-Landscape’
  • ‘Letter’
  • ‘Letter-Landscape’

or using special notation: ‘x_size:y_size:’ where size is given in points.

// Add new page (A4 size) 
$pdf->pages[] = $pdf->newPage('595:842:'); 

Saving A Document

So far you have created your document, you’ve added pages, you’ve made it pretty. Let’s save it now so you can share it with others. There are three ways to save a PDF once you have created it. The easiest is to simply call the save() method. save() takes one parameter, a file name. Even if you’ve already saved the document once, or you loaded it form an existing document, you still have to specify the file name again.

// Save document as a new file or rewrite existing document 
$pdf->save($fileName); 

The second way is to use the “incremental update” PDF feature. Using this, only file modifications are appended. If you want to use the incremental update feature, you pass true as the second parameter of save().

// Update existing document 
// (only document modifications are appended to a file) 
$pdf->save($fileName, true); 

You should be careful when using this second method. You have to use the same file name for loading and for saving the updates.

Finally, prepared PDF document can be also rendered into a string. This is useful if you plan to return file through HTTP.

// Create new PDF 
$pdf = new Zend_Pdf(); 

// Add new page to the document 
$page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4); 
$pdf->pages[] = $page; 

// Draw something on a page 
... 

// Get PDF document as a string 
$pdfData = $pdf->render(); 

header("Content-Disposition: inline; filename=result.pdf"); 
header("Content-type: application/x-pdf"); 
echo $pdfData; 

Text drawing

So, we have a page, but honestly, a PDF full of blank pages isn’t that exciting. To use the PDF to convey ideas we need text and we need images. Let’s work with Text first. For those not familiar with PDFs, everything is “drawn” on the page, even text. Therefore we will refer to the placing of text on the page from here on out as “drawing text”.

Here is an example of most simple text drawing:

// Create new PDF 
$pdf = new Zend_Pdf(); 

// Add new page to the document 
$page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4); 
$pdf->pages[] = $page; 

// Set font 
$page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA), 20); 

// Draw text 
$page->drawText('Hello world!', 100, 510); 

Congratulations, you have created your Hello World PDF! See, it wasn’t nearly as hard as you thought it would be. Now, before we move on, there are some other things you need to know about drawing text. First, Zend_Pdf supports the 14 standard PDF fonts. Here is a list of the fonts.

  • Zend_Pdf_Font::FONT_COURIER
  • Zend_Pdf_Font::FONT_COURIER_BOLD
  • Zend_Pdf_Font::FONT_COURIER_OBLIQUE (identical to Zend_Pdf_Font::FONT_COURIER_ITALIC)
  • Zend_Pdf_Font::FONT_COURIER_BOLD_OBLIQUE (identical to Zend_Pdf_Font::FONT_COURIER_BOLD_ITALIC)
  • Zend_Pdf_Font::FONT_HELVETICA
  • Zend_Pdf_Font::FONT_HELVETICA_BOLD
  • Zend_Pdf_Font::FONT_HELVETICA_OBLIQUE (identical to Zend_Pdf_Font::FONT_HELVETICA_ITALIC)
  • Zend_Pdf_Font::FONT_HELVETICA_BOLD_OBLIQUE (identical to Zend_Pdf_Font::FONT_HELVETICA_BOLD_ITALIC)
  • Zend_Pdf_Font::FONT_SYMBOL
  • Zend_Pdf_Font::FONT_TIMES_ROMAN
  • Zend_Pdf_Font::FONT_TIMES
  • Zend_Pdf_Font::FONT_TIMES_BOLD
  • Zend_Pdf_Font::FONT_TIMES_ITALIC
  • Zend_Pdf_Font::FONT_ZAPFDINGBATS

Second, you always have to set a font before drawing text onto the page. (always) Here is another example of the drawText() command.

// Set font 
$page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA), 20); 
// Draw text 
$page->drawText($message, 100, 510, 'UTF-8'); 

The second and third parameters are the drawing coordinates and are given in points starting from left bottom page corner. The forth and optional parameter of drawText() is the encoding. Each of the standard 14 fonts, with the exceptions of Symbol and ZapfDingbats, support the Latin character set which may be represented in any encoding supported by iconv library.

In addition to the standard 14 fonts, Zend_Pdf also supports custom TrueType. Custom TrueType fonts may be added into your document with Zend_Pdf_Font::fontWithPath() method, like this.

// Set font 
$page->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::fontWithPath($fontPath)), $size); 

// Draw text 
$page->drawText('Hello world!', $x, $y); 

In the example above $size is the font size to draw in. Font sizes are expressed in points.

Image Drawing

Text is great, text helps convey ideas, but you know the old saying about the worth of a picture. Lucky for us, PDF and Zend_Pdf support drawing images. Raster images are drawn on a page using special image object. JPEG (requires GD PHP extension), PNG (requires ZLIB PHP extension for images with Alpha channel) and TIFF images are supported.

// Load image 
$image = Zend_Pdf_Image::imageWithPath($imagePath); 

// Draw image 
$page->drawImage($image, $left, $bottom, $right, $top); 

drawImage() takes 5 parameters, first the image represented as a Zend_Pdf_Image object. Then the four corners of the placement box represented by $left, $bottom, $right and $top.

Shape drawing

In addition to placing raster images on the page, Zend_Pdf_Page has a number of methods for drawing common shapes

  • Zend_Pdf_Page::drawLine(…)
  • Zend_Pdf_Page::drawRectangle(…)
  • Zend_Pdf_Page::drawPolygon(…)
  • Zend_Pdf_Page::drawCircle(…)
  • Zend_Pdf_Page::drawEllipse(…)

See Zend_Pdf documentation for more details.

Let’s take a look at sample code to draw a few shapes.

// Draw rectangle 
$page->setFillColor(new Zend_Pdf_Color_GrayScale(0.8)); 
$page->setLineColor(new Zend_Pdf_Color_GrayScale(0.2)); 
$page->setLineDashingPattern(array(3, 2, 3, 4), 1.6); 
$page->drawRectangle(60, 400, 400, 350); 

// Draw circle 
$page->setLineDashingPattern(Zend_Pdf_Page::LINE_DASHING_SOLID); 
$page->setFillColor(new Zend_Pdf_Color_Rgb(1, 0, 0)); 
$page->drawCircle(85, 375, 25); 

// Draw sectors 
$page->drawCircle(200, 375, 25, 2*M_PI/3, -M_PI/6); 
$page->setFillColor(new Zend_Pdf_Color_Cmyk(1, 0, 0, 0)); 
$page->drawCircle(200, 375, 25, M_PI/6, 2*M_PI/3); 
$page->setFillColor(new Zend_Pdf_Color_Rgb(1, 1, 0)); 
$page->drawCircle(200, 375, 25, -M_PI/6, M_PI/6); 

// Draw ellipse 
$page->setFillColor(new Zend_Pdf_Color_Rgb(1, 0, 0)); 
$page->drawEllipse(250, 400, 400, 350); 
$page->setFillColor(new Zend_Pdf_Color_Cmyk(1, 0, 0, 0)); 
$page->drawEllipse(250, 400, 400, 350, M_PI/6, 2*M_PI/3); 
$page->setFillColor(new Zend_Pdf_Color_Rgb(1, 1, 0)); 
$page->drawEllipse(250, 400, 400, 350, -M_PI/6, M_PI/6); 

// Draw and fill polygon 
$page->setFillColor(new Zend_Pdf_Color_Rgb(1, 0, 1)); 
$x = array(); 
$y = array(); 
for ($count = 0; $count < 8; $count++) { 
$x[] = 140 + 25*cos(3*M_PI_4*$count); 
$y[] = 375 + 25*sin(3*M_PI_4*$count); 
} 
$page->drawPolygon($x, $y, 
Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE, 
Zend_Pdf_Page::FILL_METHOD_EVEN_ODD); 

// Draw line 
$page->setLineWidth(0.5); 
$page->drawLine(60, 375, 400, 375); 

Result of this code usage:

Colors and Line Dashing Patterns

The code above demonstrates usage of colors objects and assigning them to fill color and line color. In addition, lines may also be decorated with Zend_Pdf_Page::setLineDashingPattern($pattern, $phase) method, where $pattern is an array of floats: array(on_length, off_length, on_length, off_length, …) and $phase is a shift from the beginning of line.

The Zend_Pdf component operates with gray scale, RGB, CMYK and HTML colors.

// $grayLevel (float number). 0.0 (black) - 1.0 (white) 
$color1 = new Zend_Pdf_Color_GrayScale($grayLevel); 

// $r, $g, $b (float numbers). 0.0 (minimum intensity) - 1.0 (maximum intensity) 
$color2 = new Zend_Pdf_Color_Rgb($r, $g, $b); 

// $c, $m, $y, $k (float numbers). 0.0 (minimum intensity) - 1.0 (maximum intensity) 
$color3 = new Zend_Pdf_Color_Cmyk($c, $m, $y, $k); 

// HTML colors 
$color4 = new Zend_Pdf_Color_Html('#3366FF'); 
$color5 = new Zend_Pdf_Color_Html('silver'); 
$color6 = new Zend_Pdf_Color_Html('forestgreen'); 

Using Styles

An instance of the Zend_Pdf_Style object may be used to assign a set of drawing properties.

// Create new Style 
$style = new Zend_Pdf_Style(); 

After creation style may collect number of drawing settings like line color, fill color, line width, line dashing pattern and phase, current font and font size.

$style->setFillColor(new Zend_Pdf_Color_Rgb(0, 0, 0.9)); 
$style->setLineColor(new Zend_Pdf_Color_GrayScale(0.2)); 
$style->setLineWidth(3); 
$style->setLineDashingPattern(array(3, 2, 3, 4), 1.6); 
$style->setFont(Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA_BOLD), 32); 

Then all these settings may be applied through one step.

// Apply style 
$page->setStyle($style); 

Rotations

Because nothing is ever quite as easy as it seems, to get angled text or graphics on a page, you need to rotate the page before drawing; luckily for you, Zend_Pdf makes this easy. Zend_Pdf_Page has a method rotate() that will allow you to easily rotate a page by a given angle, around a specific point on the page. Ok, that sounds a bit confusing so just read the code.

// Rotate page coordinate system 
$page->rotate($x, $y, $angel); 

See, not nearly as difficult as it sounds.

Here is an example of several scripts and resulting pages (axes drawing is skipped):

1. Source page.

$page->drawText('Hello world!', 150, 100); 

2. Page rotated around left bottom corner.

$page->rotate(0, 0, M_PI/12); 
$page->drawText('Hello world!', 150, 100); 

3. Page rotated around specified point.

$page->rotate(150, 100, M_PI/12); 
$page->drawText('Hello world!', 150, 100); 

Clipping

Clipping is the process of limiting the region a paint operator will work on. The default for a paint operator paints the entire page, in most cases, this is not the desired result. Clipping effects can also be applied to images, as can be seen in this example below.

// Create new image object 
$image = Zend_Pdf_Image::imageWithPath($imagePath); 

// Draw part of the image within a circle 
$page->saveGS(); 
$page->clipCircle(200, 150, 50);
$page->drawImage($image, 200, 100, 300, 200);
$page->restoreGS();

Result without clipping:

Result with clipping:

Graphics state

The last thing we need to cover is “graphics state”. Any time the graphic state of the page changes (current font, font size, line color, fill color, line style, page rotation, clip area) you can use saveGS() and restorGS() to save it or restore it. This allows you to save the current graphic state, make changes and draw things, then restore it. Each save graphics state call mast have corresponded restore graphic state method:

foreach ($pdf->pages as $page){ 
$page->saveGS(); 
$page->setStyle($style); 
$page->rotate(0, 0, M_PI_2/3); 

$page->saveGS(); 
$page->clipCircle(550, -10, 50); 
$page->drawImage($stampImage, 500, -60, 600, 40); 
$page->restoreGS(); 

$page->drawText('Modified', 150, 0); 
$page->restoreGS(); 
} 

close()

We really hope that this short tutorial has given you a taste for what is possible with Zend_PDF and more importantly, sparked your imagination on how you can use it in your own applications.
As with all parts of the Zend Framework, Zend_Pdf is constantly undergoing review, refactoring and enhancement. as of this writing, there are new features that have been checked in for release in Zend Framework 1.1 and additional features on the roadmap that have yet to be pinned down.

  • Document info processing.
    Get/set document title, author, subject and other pieces of metadata about the document.
  • Pages cloning.
    Allows to clone existing page and use it as a template for another pages. This feature is complete, tested, planned to be included into ZF 1.1 It is already available if you fetch the Zend Framework from SVN or download a nightly snapshot.
  • Detaching page (and other resources) from a PDF document.
    This feature, coupled with page cloning will allow developers to mix pages from different documents to create a new one. This feature is scheduled to be available at the release of Zend Framework 1.1
  • Rich text drawing functions.

For more information on Zend_Pdf or the Zend Framework in general, check out the extensive documentation. If you still have questions, pop over to the #zftalk channel on freenode.net and chat with other Zend Framework users. We also encourage you to voice your opinion. What new features of Zend_Pdf are important to you?

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is happily married to wife 1.31, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Nomad PHP