I’ve previously written a post on creating a PDF generator class using Zend_Pdf, which detailed some Zend_Pdf usage and introduced a class which could be used to create PDFs easily based on an XML configuration file or such.
Zend_Pdf, while generally quite good, has one big issue: It does not support word wrapping text!
There’s a new and still a bit obscure framework called RE Framework, which has an excellent PDF component – it includes support for word wrapping, better support for PDF templates, image loading etc.
Let’s check out how to use RE_Pdf to improve the earlier CU_PdfGenerator class!
The XML configuration
Let’s first modify the code which parses the XML file. We will also add a new feature: Setting dimensions for a text box.
RE_Pdf shares a similar interface as Zend_Pdf, so for this part the rewrite will be quite simple: We’ll just replace Zend_ with RE_:
$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(RE_Pdf_Font::fontWithName($fontFamily), $fontSize); foreach($xml->elements[0] as $element) { $key = (string)$element['key']; //New code to add support for width/height of textboxes! $w = 0; $h = 0; if(isset($element['width'])) { $w = (int)$element['width']; $h = (int)$element['height']; } $generator->setPosition($key, (int)$element['x'], (int)$element['y'], $w, $h); 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 = RE_Pdf_Font::fontWithName($family); $generator->setFontForKey($key, $font, $size); } } |
So instead of creating new Zend_Pdf_Fonts, we use RE_Pdf_Font. We also added new code which grabs width and height from the XML configuration.
As a refresher, let’s have a look at the XML config too:
<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> <element key="description" x="10" y="90" width="100" height="150" /> </elements> </pdf> |
If you compare this to the old example, we’ve added a new element which also includes new attributes width and height.
Next, we’ll modify the actual generator class itself to support word-wrap.
Modifying the generator class
Again, we’re going to replace all references to Zend_Pdf_Font with RE_Pdf_Font. We’ll also modify the setPosition method to accept a width and a height. Finally, the getPdf() method needs to be modified to use RE_Pdf’s classes.
You can get the original code here.
You can just do a search and replace for Zend_ and replace with RE_ to start. That will cover all other places except the two methods we will look at next:
public function setPosition($key, $x, $y, $width = 0, $height = 0) { $this->_positions[$key] = array($x, $y, $width, $height); return $this; } |
Now setPosition accepts width and height – by default they are both 0, meaning that if they’re unset, there will be no word wrap or anything.
public function getPdf() { $pdf = new RE_Pdf_Document(); //Need to load the template PDF file as an image: $template = RE_Pdf_Image::imageWithPath($this->_template); //We select the first page of the template as that's what we want to use $template->selectPage(1); for($i = 0, $dataCount = count($this->_data); $i < $dataCount; $i++) { //Need to draw the template on the new page for it to display: $page = $pdf->newPage(); $template->draw($page, new RE_Rect(new RE_Point(), new RE_Point($template->getPixelWidth(), $template->getPixelHeight()))); $values = $this->_data[$i]; foreach($values as $key => $value) { $font = $this->getFontForKey($key); $size = $this->getFontSizeForKey($key); if($font != null) { $page->setFont($font, $size); } else { $page->setFont($this->_font, $this->_fontSize); } $position = $this->getPosition($key); if($position == null) { continue; } //Store position in variables to make it clearer list($x, $y, $width, $height) = $position; $yFromTop = $page->getHeight() - $y; //If there's no width, we draw text as a single line: if($width === 0) { $page->drawText($value, $x, $yFromTop); } else { //otherwise we use RE's layoutmanager to create a wrapping text box: $text = new RE_Pdf_Text($value); RE_Pdf_LayoutManager::drawText($text, $page, new RE_Rect( $x, $yFromTop - $height, $width, $height )); } } } return $pdf; } |
So there!
Now we can draw word wrapping textboxes in our PDF by simply adding a width and height to the XML configuration. Does it get any easier than this?
RE_Pdf supports various things that Zend_Pdf doesn’t. In my opinion, it’s easier to use than FPDF as well – I couldn’t find a way to use a template PDF using FPDF for example.
You can learn more about RE from their site, or by joining their IRC channel, #refw on irc.freenode.net. They are working on improving the documentation, too. When they get that done, they’ll have easily the best PDF component.