Zend_PDF rocks! It makes drawing a PDF almost child’s play. The main time eater though is the trial and error with the co-ordinates. So I stopped all this nonsense, and used Illustrator.

In adobe Illustrator, set the ruler origin to the bottom left of the page. Now in the transform pallette you can get the XY co-ordinates! Once you have created a mock up of the PDF, save a copy minus all the dynamic stuff, which we’ll drop in from our controller action.

First up, ditch the view and layout:

//disable the layout
 Zend_Layout::getMvcInstance()->disableLayout(); 
 $this->_helper->viewRenderer->setNoRender();

Next we create the Zend_PDF and templates, and set default fonts and colours:

//create a zend pdf
 $template = Zend_Pdf::load(APPLICATION_PATH.'/layouts/pdfs/proplist.pdf');
 $this->_pdf = new Zend_Pdf();
 $extractor = new Zend_Pdf_Resource_Extractor();
//set font
 $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA);
 $bold = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA_BOLD);
//set colors
 $black = new Zend_Pdf_Color_Html('#000000');
 $red = new Zend_Pdf_Color_Html('#ED1F24');
 $green = new Zend_Pdf_Color_Html('#009444');
 $blue = new Zend_Pdf_Color_Html('#0069B5');

We will have so many records per page, so the next bit goes in a for loop:

//add page
 $page = $extractor->clonePage($template->pages[0]);

 //get properties for this page
 $props = $this->_helper->paginator($rows,6);

We can set colours and fonts like this:

$page->setFillColor($black);
$page->setFont($bold, 11);

Add the page to the pdf like this

$pdf->pages[] = $page;

I created a function for getting the coordinates of each element, which adjusts depending which record on the page we are dealing with. This could be tweaked to suit your own project.

private function getCoordinates($row, $elem)
 {
 $xCoords = array(
 'header' => 21,
 'avail_date' => 419,
 'price' => 541,
 'summary' => 21,
 'hol_dep' => 70,
 'dep_date' => 128,
 'board_up' => 217,
 'keys' => 267,
 'tax_band' => 333,
 'on_web' => 388,
 'ref' => 437,
 'images' => 560,
 'condition' => 59,
 'move_date' => 130,
 'gas_safety' => 221 ,
 'hwsa' => 271 ,
 'let_fee' => 327,
 'man_fee' => 391,
 'landlord' => 452,
 'lha' => 551,
 'comment' => 64
 );

 $yCoords = array(
 'header' => 759,
 'avail_date' => 759,
 'price' => 759,
 'summary' => 739,
 'hol_dep' => 692,
 'dep_date' => 692,
 'board_up' => 692,
 'keys' => 692,
 'tax_band' => 692,
 'on_web' => 692,
 'ref' => 692,
 'images' => 692,
 'condition' => 680,
 'move_date' => 680,
 'gas_safety' => 680,
 'hwsa' => 680,
 'let_fee' => 680,
 'man_fee' => 680,
 'landlord' => 680,
 'lha' => 680,
 'comment' => 667
 );

 $z = array();
 $z['x'] = $xCoords[$elem];

 switch($row)
 {
 case 1:
 default:
 $z['y'] = $yCoords[$elem];
 break;
 case 2:
 $z['y'] = $yCoords[$elem] - 127.5;
 break;
 case 3:
 $z['y'] = $yCoords[$elem] - (127.5 * 2);
 break;
 case 4:
 $z['y'] = $yCoords[$elem] - (127.5 * 3);
 break;
 case 5:
 $z['y'] = $yCoords[$elem] - (127.5 * 4);
 break;
 case 6:
 $z['y'] = $yCoords[$elem] - (127.5 * 5);
 break;
 }
 return $z;
 }

Finally we can draw the text like this:

$text = '?';
 $page->setFillColor($blue);
 $z = $this->getCoordinates($row, 'condition');
 $page->drawText($text, $z['x'], $z['y'],'UTF-8');

Other things include fitting text to a specific width, or making a paragraph of multi lines:

private function widthForStringUsingFontSize($string, $font, $fontSize)
 {
 $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $string);
 //$drawingString = $string;
 $characters = array();
 for ($i = 0; $i < strlen($drawingString); $i++) {
 $characters[] = (ord($drawingString[$i++]) << 8 ) | ord($drawingString[$i]);
 }
 $glyphs = $font->glyphNumbersForCharacters($characters);
 $widths = $font->widthsForGlyphs($glyphs);
 $stringWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
 return $stringWidth;
 }
private function getFontSizeForWidth($text,$font,$desiredSize,$maxWidth)
 {
 do 
 {
 $stringWidth = $this->widthForStringUsingFontSize($text, $font, $desiredSize);
 $desiredSize = $desiredSize - 0.5; 
 }
 while($stringWidth > $maxWidth);
 return $desiredSize + 0.5;
 }
private function getWrappedText($string, $font, $fontsize,$max_width)
 {
 $wrappedText = '' ;
 $lines = explode("\n",$string) ;
 foreach($lines as $line) {
 $words = explode(' ',$line) ;
 $word_count = count($words) ;
 $i = 0 ;
 $wrappedLine = '' ;
 while($i < $word_count)
 {
 /* if adding a new word isn't wider than $max_width,
 we add the word */
 if($this->widthForStringUsingFontSize($wrappedLine.' '.$words[$i]
 ,$font
 , $fontsize) < $max_width) {
 if(!empty($wrappedLine)) {
 $wrappedLine .= ' ' ;
 }
 $wrappedLine .= $words[$i] ;
 } else {
 $wrappedText .= $wrappedLine."\n" ;
 $wrappedLine = $words[$i] ;
 }
 $i++ ;
 }
 $wrappedText .= $wrappedLine."\n" ;
 }
 return $wrappedText ;
 }

To align text to the right, we subtract the width:

$width = $this->widthForStringUsingFontSize($text, $bold, 12);
 $z = $this->getCoordinates($row, 'price');
 $z['x'] = $z['x'] - $width;

And if we need wrapped text:

$text = $this->getWrappedText($text, $font,10, 550);

Awesome! Another what used to be pain in the arse task is now a piece of cake (or zf rather lol)!

Advertisements