Dynamically Generating PDF Files with PHP and Haru

The Ultimate Assistant

When it comes to working with different file formats, PHP is like a Swiss Army knife: it has a tool to handle almost anything you can throw at it. JPEG images, XML files, Word documents, ZIP archives…you name it, and there’s a PHP extension to handle it!

This incredible versatility also applies to the topic of today’s article: the PDF, or Portable Document Format. Not content with just one, PHP actually comes with two different extensions that allow developers to dynamically generate PDF documents: the PDFlib extension, and the libHaru extension. Over the next few pages, I’m going to take a quick look at the Haru extension, providing you with a brief overview of its functions and demonstrating how you can use it in your PHP development. So come on it, and let’s get going!

Tooling Up

LibHaru support in PHP comes through PECL’s ext/haru extension, which is maintained by Antony Dovga and provides an object-oriented API for PDF document generation. This ext/haru extension, in turn, requires you to have the libHaru PDF library installed on your system. This library is available for both Windows and UNIX systems.

If you’re using a UNIX system, begin by downloading, compiling and installing the libHaru library from http://www. libharu.org/. This article uses libHaru 2.0.8.

This procedure should create a loadable PHP module named haru.so in your PHP extension directory. You should now enable the extension in the php.ini configuration file, restart your Web server, and check that the extension is enabled with a quick call to phpinfo():

Windows users have a much easier time of it; a pre-compiled version of the Haru extension is included by default with the Windows version of PHP. If you’re using a Windows system, first download a pre-compiled version of the libHaru library (libhpdf.dll) and place it in your PHP directory. Then, activate the included Haru extension in your php.ini configuration file, restart your Web server, and check the extension’s status with phpinfo(), as described above.

Sword Fight

Once you’ve got the pieces installed, let’s take Haru out for a quick spin. The following example demonstrates how to create a PDF document containing a famous Shakespearean quotation:

Try running this and once completed, you should have a PDF file at /tmp/test.pdf, which looks something like this:

So what does this script do? It starts off by initializing a new HaruDoc object, which represents the PDF document to be created. It then adds a new page to this document via the addPage() method, which creates a new HaruPage object. This HaruPage object is the primary interface for everything related to the corresponding page. The script then uses this object to set the orientation and size of the page via the setSize() method, and loads the fonts needed for the page via the loadTTF() method. Once loaded, the getFont() method returns an instance of a HaruFont object, which represents the font for all subsequent use.

Now to actually write some text! The first step is to decide which of the loaded fonts to use, and set this with the HaruPage object’s setFontAndSize() method. Next, the HaruPage object’s beginText() and endText() methods are used to mark the beginning and end of a text block. Within these two methods calls, the moveTextPos() method is used to move the text cursor to a specific offset on the page, and the showText() method is used to actually write text to the page beginning from that location. Note that the moveTextPos() method moves the cursor relative to its last position or, if called for the first time, relative to the document origin (0,0) or the bottom left corner of the page.

Once all the text is written, the PDF file is written to disk with the HaruDoc object’s save() method. As an alternative, it’s also possible to output the PDF file direct to the client browser via the output() method (you’ll see this in an upcoming example). Any errors in the PDF creation process will generate an exception, which can be caught by wrapping the script in a try-catch block.

A quick word here about fonts and font files. The listing above demonstrated the loadTTF() method, which is a convenient way to import TrueType font files from disk into a PDF document. It’s also possible to import fonts from Type1 font files and TrueType font collection files, via the loadType1() and loadTTC() methods. All of these methods return the name of the font file, which is required by the getFont() method to generate a HaruFont instance. It’s also possible to directly use a built-in font, such as ‘Courier’ or ‘Times-Roman’, by passing the font name directly to the getFont() method. A complete list of built-in font names can be found at http://www.php.net/manual/en/haru.builtin.php#haru.builtin.fonts.

An alternative way of writing text to a page is with the textOut() method, which accepts absolute coordinates for the text cursor. This is often easier than using moveTextPos(), which uses relative offsets. Here’s an example, which replicates the output of the previous listing:

This script looks somewhat complex, but it really isn’t. It begins by setting some configuration values, such as the source text file, the font and font size to use, and the line spacing required. It then reads the source file into a string, and passes this string to the HaruPage object’s measureText() method to calculate how many characters will fit on to each line of the page, given the specified page width (with some margin allowance) and font size. Once this character limit is obtained, PHP’s wordwrap() function is used to split the file data into an array of lines, such that the number of characters in each line does not exceed this limit.

The second stage is to actually generate a PDF version of the text file. This is accomplished by creating a new HaruDoc, setting the font and size, and then iterating over the line array in a loop. This loop takes care of printing each line to the page with the showText() method, and then moving the text cursor down to the next line with the moveTextPos() method. On each iteration of the loop, the current position of the text cursor is obtained via the getCurrentTextPos() method. If this position appears to be below the end of the page, a new page is dynamically added to the document via the addPage() method. In this manner, a multi-page PDF document is dynamically generated from a text file.

Here’s an example of the output:

Notice also that the example above uses a built-in font, Courier, instead of loading a TrueType font file. In many cases, this approach can reduce the size of the output PDF file.

Pretty As A Picture

It’s not just text, either – libHaru can also import image files of different formats into a PDF document, via its loadJPEG(), loadRAW() and loadPNG() methods. Here’s an example, which demonstrates the process:

Here, the loadJPEG() method of the HaruDoc object is used to load a JPEG image from disk. Then, the HaruPage object’s drawImage() method is used to place this image on the page, at a specified set of coordinates. In addition to the image and the location, the drawImage() method must also be provided with the dimensions of the image. In the example above, these dimensions are dynamically obtained from the image file via the getWidth() and getHeight() methods, and then reduced to 10% of their original value. Once the image has been written to the page, the document can be saved to disk with a call to save(), as usual.

Here’s the output:

Lining Up

Why stop there? PHP’s Haru extension comes with a whole bag of functions designed to let you draw lines, circles and other shapes in your PDF document. Consider the following example, which demonstrates the process of drawing a line:

With Haru, drawing a line is a three-step process. First, move the drawing cursor to the line’s start point, by calling the HaruPage object’s moveTo() method with the correct coordinates. Then, mark the end-point of the line by calling the lineTo() position with the end-point coordinates. Finally, render the actual line by calling the stroke() method.

The stroke colour is set via a call to the HaruDoc setRGBStroke() method, which accepts RGB values corresponding to the selected colour. It’s important to note that these values must be specified in terms of percentage intensity – that is, the intensity of that colour, expressed as a percentage of the maximum intensity possible. So, for example, if you wanted to set red (RGB: 255,0,0) as the stroke colour, you would invoke the method as $page->setRGBStroke(1, 0, 0). It’s also possible to set a fill colour, with the setRGBFill() method; you’ll see this in the next example.

Now, look closely at the script above and you’ll see that it actually draws more than just a single line. It actually draws two lines, one in the page header and one in the page footer, and it also mixes things up a little by adding an image to the top left corner of the page and footer text to the bottom right corner, using methods you’ve seen in previous examples. Here’s an example of what the final output looks like:

Here’s another example, this one demonstrating how to draw a grid of vertical and horizontal lines using the lineTo() method:

Here, loops are used to dynamically draw a series of lines across the page, first vertically and then horizontally, to generate a rectangular grid. Here’s what the output looks like:

A Square Face

Lines aren’t the only thing you can draw; circles and rectangles also figure prominently on the menu. Take a look at the following example, which demonstrates:

This script attempts to draw a face, using filled rectangles of different sizes for the eyes, nose and mouth. As before, it first uses the setRGBFill() and setRGBStroke() to define the fill and stroke colour for each rectangle. The rectangle itself is marked out using the HaruPage object’s rectangle() method, which accepts four arguments: the X and Y coordinates of the rectangle’s lower left corner, and the width and height of the rectangle, in pixels. Once the rectangle has been marked out, the fillStroke() method is used to simultaneously fill and stroke it with the previously-selected colours.

Here’s what the output looks like:

Circling The Wagons

Drawing circles is also fairly easy: simply call the circle() method with the coordinates of the circle center and the length of the radius, and Haru will draw you a circle with the specified parameters. You can then stroke it, fill it or do both with the stroke(), fill() or fillStroke() methods.

Here’s an example, which rewrites the previous one and replaces the rectangular eyes with circular ones instead:

And here’s the output:

It’s also possible to draw arcs, with the HaruPage object’s arc() method. This method accepts five arguments: the X and Y coordinates of the arc center, the arc radius, and the angles for the arc’s start- and end-point. To see this in action, consider yet another revision of the previous listing, which replaces the rectangular mouth with a curved smile and – for good measure – adds a pair of eyebrows. Take a look at the code:

And here’s the new output:

Who says PDF can’t put a smile on your face?

Linking Out

The Haru library also supports the creation of text annotations within your PDF document, via the – what else? – createTextAnnotation() method. This method accepts an array of coordinates for the annotation, together with the annotation text. Here’s an example:

And here’s what the output looks like:

It’s also possible to create link and URL annotations, with the createLinkAnnotation() and createURLAnnotation() methods, respectively.

Access Denied

You can set document information with the HaruDoc object’s setInfoAttr() method; this information identifies the document creator, title, subject and content keywords. You can also set the document’s creation and last modification date, with the setInfoDateAttr() method. The following example demonstrates both:

In a similar vein, you can set user and owner passwords for the PDF document with the setPassword() method, and control whether the document can be read, copied, printed and edited with the setPermission() method. Here’s an example:

The setPermission() method accepts a series of permission values, separated with the bitwise OR operator (|). The available permissions are:

  • HaruDoc::ENABLE_READ
  • HaruDoc::ENABLE_COPY
  • HaruDoc::ENABLE_EDIT

And that’s about all I have for the moment. I hope this article demonstrated some of the most important features of the Haru PDF extension, and intrigued you enough to go out there and start experimenting with it. Happy coding!

This article copyright Melonfire, 2008. All rights reserved.