There are some expensive libraries that you can use to generate PDF files… but why bother with them, when there are good free alternatives like Zend_Pdf?

In three steps, you can generate a fancy report:

Create a PDF template Insert data to template with Zend_Pdf Profit $$$

Since this is a quite repetitive task, let’s also create a PDF generator class to help us. In case you don’t have Zend_Pdf, just download Zend Framework to get it.

Step 1

I won’t go into much detail with this, since it’s essentially up to you: Just choose your favorite application that’s capable of creating PDF files, and create a PDF file for your report or whatever you’re going to output. You can insert your logo, labels for data etc., just make sure you leave the area where you want the actual data from your application to show up empty.

Step 2

Now that you have an empty PDF template, you’ll need to write some code that will insert the data from your application into the template and output the result.

At the very least, you will need to load the template and set a font to be able to write on the page:

$pdf = Zend_Pdf :: load ( 'template.pdf' ) ; $page = $pdf -> pages [ 0 ] ; $font = Zend_Pdf_Font :: fontWithName ( 'Helvetica' ) ; $page -> setFont ( $font , 12 ) ; //now we can write: $page -> drawText ( 'Hello World' , $x , $y ) ; $pdf = Zend_Pdf::load('template.pdf'); $page = $pdf->pages[0]; $font = Zend_Pdf_Font::fontWithName('Helvetica'); $page->setFont($font, 12); //now we can write: $page->drawText('Hello World', $x, $y);

So this one is pretty simple too. The tricky part is figuring out $x and $y: you will need to find the specific location of each value – even though PDF has placeholder stuff, Zend_Pdf doesn’t support it (but hopefully in the future).

Now, you would probably end up with a lot of code that just says draw text here, draw text there and so on. You might even have many kinds of PDFs that you wish to be able to create, so it would only seem natural that we create a class that can take care of the mundane PDF creation, based on something that’s easy for us to edit… say, XML.

PDF generator

I’ve created a class, CU_PdfGenerator, for this task.

You can define a template PDF file, define a font, define locations for each data key and define specific fonts for data keys if needed. It’s quite basic, but it does the job and it should be easy to extend to allow, say, different font colors.

Basically, you could do something like this:

$generator = new CU_PdfGenerator ( ) ; $generator -> setTemplate ( 'data/template.pdf' ) -> setFont ( Zend_Pdf_Font :: fontWithName ( 'Helvetica' ) , 12 ) ; //Some example data: $data = array ( 'firstname' => 'Peter' , 'lastname' => 'Pan' ) ; //Put the data in an array, as the generator can //do multiple pages of data with one index per page $generator -> setData ( array ( $data ) ) //Finally, set X and Y positions for the data: $generator -> setPosition ( 'firstname' , 10 , 10 ) ; -> setPosition ( 'lastname' , 10 , 50 ) ; //Get resulting Zend_Pdf object: $pdf = $generator -> getPdf ( ) ; $generator = new CU_PdfGenerator(); $generator->setTemplate('data/template.pdf') ->setFont(Zend_Pdf_Font::fontWithName('Helvetica'), 12); //Some example data: $data = array( 'firstname' => 'Peter', 'lastname' => 'Pan' ); //Put the data in an array, as the generator can //do multiple pages of data with one index per page $generator->setData(array($data)) //Finally, set X and Y positions for the data: $generator->setPosition('firstname', 10, 10); ->setPosition('lastname', 10, 50); //Get resulting Zend_Pdf object: $pdf = $generator->getPdf();

One thing to note here is that I made the generator to use a different coordinate system for the Y position. Normally, if you said y = 0 for positioning in a PDF, it would go to the bottom of the page. I modified it so, that y = 0 means top, since I’m more familiar with that way of positioning things.

The final $pdf variable would contain a Zend_Pdf object with all the data applied. You can use this to get the final PDF data for outputting with $pdf->render()

Using the generator with XML

Also, since I mentioned using XML, let’s take a quick look at some code which can be used in together with the generator to parse the PDF settings from a simple XML document.

Basically, we’d want to allow users to write all the small details of generating the PDF in XML: the template, the font family and size, the locations of the data on the pages and their fonts… The resulting XML could look like this:

<pdf > <base > data/template.pdf </base > <font family = "Helvetica" size = "12" /> <elements > <element key = "firstname" x = "10" y = "10" /> <element key = "lastname" x = "10" y = "50" > <font size = "16" /> </element > </elements > </pdf > <pdf> <base>data/template.pdf</base> <font family="Helvetica" size="12" /> <elements> <element key="firstname" x="10" y="10" /> <element key="lastname" x="10" y="50"> <font size="16" /> </element> </elements> </pdf>

That would equal the definition we did in code in the previous chapter, with the exception of the size 16 font for lastname. Now, let’s check out the code for parsing that:

$xml = simplexml_load_file ( 'path/to/definition.xml' ) ; $fontSize = ( int ) $xml -> font [ 'size' ] ; $fontFamily = ( string ) $xml -> font [ 'family' ] ; $generator = CU_PdfGenerator :: create ( ) -> setTemplate ( ( string ) $xml -> base ) -> setFont ( Zend_Pdf_Font :: fontWithName ( $fontFamily ) , $fontSize ) ; foreach ( $xml -> elements [ 0 ] as $element ) { $key = ( string ) $element [ 'key' ] ; $generator -> setPosition ( $key , ( int ) $element [ 'x' ] , ( int ) $element [ 'y' ] ) ; if ( isset ( $element -> font ) ) { $family = $fontFamily ; $size = $fontSize ; if ( isset ( $element -> font [ 'family' ] ) ) $family = ( string ) $element -> font [ 'family' ] ; if ( isset ( $element -> font [ 'size' ] ) ) $size = ( int ) $element -> font [ 'size' ] ; $font = Zend_Pdf_Font :: fontWithName ( $family ) ; $generator -> setFontForKey ( $key , $font , $size ) ; } } $xml = simplexml_load_file('path/to/definition.xml'); $fontSize = (int)$xml->font['size']; $fontFamily = (string)$xml->font['family']; $generator = CU_PdfGenerator::create() ->setTemplate((string)$xml->base) ->setFont(Zend_Pdf_Font::fontWithName($fontFamily), $fontSize); foreach($xml->elements[0] as $element) { $key = (string)$element['key']; $generator->setPosition($key, (int)$element['x'], (int)$element['y']); if(isset($element->font)) { $family = $fontFamily; $size = $fontSize; if(isset($element->font['family'])) $family = (string)$element->font['family']; if(isset($element->font['size'])) $size = (int)$element->font['size']; $font = Zend_Pdf_Font::fontWithName($family); $generator->setFontForKey($key, $font, $size); } }

The above with the XML would set up the generator to do about the same as the code-setup earlier, but modifying the reports would be much simpler since you can just edit the XML files and not have to worry about the code as much.

Conclusion

Generating PDF’s is quite easy with the right tools. The PdfGenerator here is a bit limited, but it should serve as a good starting point for you to expand further or write your own tool for the job.

I also wrote a similar approach on generating PDFs using RE_Pdf, which supports some better features that aren’t available in Zend_Pdf, such as word-wrapping text.

There’s also a PHP library called dompdf, which can be used to convert HTML into PDFs, which could serve as a basic PDF generation tool as well.