Making a PDF generator class using Zend_Pdf

October 4, 2008 – 12:38 pm Tags: , ,

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:

  1. Create a PDF template
  2. Insert data to template with Zend_Pdf
  3. 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);

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();

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>

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);
    }
}

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.

Share this:
  1. 11 Responses to “Making a PDF generator class using Zend_Pdf”

  2. Great article Jani.

    I just thought of neat ideas of how I can implement PDF generation into my current project.

    By Tom Graham on Oct 4, 2008

  3. Great Article. How do I go about putting page numbers in my multiple page PDFs at in the lower right corner of the pages? Thanks in advance.

    -j

    By James Eastman on Oct 31, 2008

  4. the simplest approach would probably be to add a pageNumber in the locations, and then pass the numbers in the data. Alternatively you could modify the loop which generates the pages and add some code there.

    By Jani Hartikainen on Oct 31, 2008

  5. You may also be interested in a project called phpLiveDocx. It offers a really easy way to create PDF files and also DOC, DOCX and RTF.

    With phpLiveDocx you can generate documents by combining structured data from PHP with a template, created in a word processor. The resulting document can be saved as a PDF, DOCX, DOC or RTF file. The concept is the same as with mail-merge.

    Learn more here:
    http://tinyurl.com/blnqd6

    By Jonathan Maron on Feb 2, 2009

  6. is it possible to load a template with bookmarks and change the text of bookmarks with the text i need?
    thank you

    By gianmarco on Mar 19, 2009

  7. If by bookmarks you’re refering to pre-defined “areas” that you can create in Acrobat or other PDF authoring tools – no, this isn’t supported.

    By Jani Hartikainen on Mar 19, 2009

  8. thank you for your reply..
    yes, i was thinking of a placeholder like in the template.
    i load the template and change the placeholder with the phone number, then i save the file.

    By gianmarco on Mar 19, 2009

  1. 4 Trackback(s)

  2. Mar 14, 2009: Eamonn O’Brien-Strain :: links for 2009-03-13
  3. Mar 20, 2009: Improved PDF generation with RE Framework RE_Pdf | CodeUtopia
  4. Apr 13, 2012: XML und Pdf / Mail - Zend Framework Forum
  5. Feb 5, 2014: Routing and complex URLs in Zend Framework

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)