info about link
: Javascript : PHP Index : MySQL :
excudo pdf textblock
: Source : : Explanation : : Example : : Todo : : Feedback :

Excudo_Pdf_Textblock.php (v. 2)

This class solves the problem with the current version of Zend_Pdf, that it doesn't have any functionality for outputting a large string into multiple lines. (text-wrapping)

Added: 2008-07-22 07:00:00
Last update: 2009-01-17 11:15:14

download

<?

/**
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with the Zend Framework source files
* It is available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so they can send you a copy immediately.
*
* @package    Zend_Pdf
* @author     Martijn Korse (http://devshed.excudo.net)
* @license    http://framework.zend.com/license/new-bsd     New BSD License
*/

/**
* The source shown here contains the code of 3 classes.
* If you download the zip file, the source of these 3 classes will be split into three seperate files
*
* Please note that this code will only work with ZF >= 1.7
* If you are using a version < 1.7, please go to
*   http://devshed.excudo.net/scripts/php/explanation/excudo+pdf+textblock
* For instructions
*/

/** Excudo_Pdf_Textblock.php */

/**
* PDF Textblock
*
* A class that can handle big strings and output them like a textblock consisting of multiple lines with custom alignment
*
* @package    Zend_Pdf
* @license    http://framework.zend.com/license/new-bsd     New BSD License
* @version    2.0
*/
class Excudo_Pdf_Textblock
{
   
/**
    * Some constants of possible alignment values
    */
   
const ALIGN_LEFT   = 'left';
   const
ALIGN_RIGHT   = 'right';
   const
ALIGN_CENTER   = 'center';
   const
ALIGN_JUSTIFY   = 'justify';

   
/**
    * @var Zend_Pdf_Resource_Font
    */
   
protected $_font;

   
/**
    * The fontsize we have to work with in this textblock
    *
    * @var Float
    */
   
protected $_fontsize;

   
/**
    * The total width of the string that we have to convert into multiple lines (if necessary)
    *
    * @var Float
    */   
   
protected $_totalWidth = 0;
   
/**
    * The (maximum) width we want 1 line to have
    *
    * @var Float
    */
   
protected $_lineWidth = 0;
   
/**
    * The total height of all the lines of this textblock together
    * This is the sum of the height of all the lines + the gap between them
    *
    * @var Float
    */
   
protected $_totalHeight = 0;
   
/**
    * The height of 1 line
    * This is calculated based on the font passed to the constructor
    *
    * @var Float
    */
   
protected $_lineHeight = 0;
   
/**
    * The height of the gap between the individual lines
    * This is calculated based on the font passed to the constructor
    *
    * @var Float
    */
   
protected $_lineGap = 0;

   
/**
    * The width we want this block to have
    *
    * @var Float
    */
   
protected $_blockWidth = 0;

   
/**
    * Alignment of this textblock
    *
    * setting default to Left
    */
   
protected $_align = self::ALIGN_LEFT;

   
/**
    * Array that will hold all the individual lines
    */   
   
protected $_lines = array();
   
/**
    * Array that will hold all the widths of the lines in $this->_lines,
    * using the same indexes
    */
   
protected $_lineWidths = array();

   
/**
    *
    * Constructor
    *
    * @param Zend_Pdf_Resource_Font $font
    * @param Float $fontSize
    */

   
public function __construct(Zend_Pdf_Resource_Font $font, $fontsize, $blockWidth = 0)
   {
      
$this->_font = $font;
      
$this->_fontsize = $fontsize;
      
$this->_lineHeight = ($font->getLineHeight()  / $font->getUnitsPerEm()) * $fontsize;
      
$this->_lineGap = ($font->getLineGap()  / $font->getUnitsPerEm()) * $fontsize;
      
// if a width is passed, we set it
      
if ($blockWidth > 0)
      {
         
$this->setBlockWidth($blockWidth);
      }
   }

   
/**
    * Parses a string and returns the total width
    *
    * If font and fontsize are not set, the ones passed to the constructor are used
    *
    * Credits for this calculation go to Willie Alberty:
    * http://framework.zend.com/issues/browse/ZF-313
    *
    * @param String $string
    * @param Zend_Pdf_Resource_Font $font (optional)
    * @param Float $fontSize (optional)
    *
    * @return Float;
    */
   
function parseString($string, $font=null, $fontsize=null)
   {
      if (
is_null($font))
         
$font = $this->_font;
      if (
is_null($fontsize))
         
$fontsize = $this->_fontsize;

      
$drawingString = iconv('', 'UTF-16BE', $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;
    }
   
   
/**
    * Creates an array of lines based on one line
    * This array can later be used to create a textblock of individual lines
    *
    * This method doesn't return anything, but saves the lines in the _lines array
    * Use the public drawText() to output the lines.
    *
    * @param String $string
    * @param Float   $maxWidth
    *
    * @return Void
    *
    * @todo handle whitespaces better (currently any whitespace is handles as 1 space)
    * @todo handle words that are too long to fit in the textblock
    */
   
function createBlock($string, $maxWidth = null)
   {
      if (!
is_null($maxWidth))
      {
         
$this->setBlockWidth($maxWidth);
      }
      
      
$this->_lines = array();
      
// exploding on newlines so we get an array of paragraphs
      
$paragraphs = preg_split('/\r\n|\r|\n/', $string);
      foreach (
$paragraphs AS $paragraph)
      {
         if (!empty(
$paragraph))
         {
            
// explode on whitespaces so we get individual words
            // TODO: fix tabs, multiple spaces
            
$words = preg_split('/\s+/', $paragraph);
            
$tempLine = '';
            
$lastLine = '';
            foreach (
$words AS $word)
            {
               
$lastLine = $tempLine;
               
$tempLine .= $tempLine != '' ? ' '.$word : $word;
               if (
$this->parseString($tempLine) > $maxWidth)
               {
                  if (
$lastLine == '')
                  {
                     
// TODO: we have to break up the word
                  
}
                  else
                  {
                     
$this->addLine($lastLine, null, False);
                     
$tempLine = $word;
                  }
               }
            }
            
$this->addLine($tempLine);
         }
         else
         {
            
$this->addLine('');
         }
      }
   }

   
/**
    * uses an Excudo_Pdf_Page object to output
    *
    * @param Zend_Pdf_Page $page
    * @param Float $x
    * @param Float $y
    *
    * @return Float the totalheight of the textblock that was outputted
    */
   
public function drawText(Excudo_Pdf_Page &$page, $x, $y)
   {
      
$xPos = $x;
      
$yPos = $y;
      foreach (
$this->_lines AS $i => $line)
      {
         
// word space adjustment
         
$wsa = False;

         if (
$this->_align == self::ALIGN_RIGHT)
         {
            
$xPos = $x + ($this->_blockWidth - $line['width']);
         }
         elseif (
$this->_align == self::ALIGN_CENTER)
         {
            
$xPos = ( ($x + ($this->_blockWidth - $line['width'])) + $x ) / 2;
         }
         elseif (
$this->_align == self::ALIGN_JUSTIFY)
         {
            
$wordCount = count(preg_split('/\s+/', $line['text']));
            if (
$wordCount > 1 && !$line['lastInParagraph'])
               
$wsa = ($this->_blockWidth - $line['width']) / ($wordCount - 1);
         }
         if (
$this->_align == self::ALIGN_JUSTIFY)
            
$page->drawTextBlock($line['text'], $xPos, $yPos, null, $wsa);
         else
            
$page->drawText($line['text'], $xPos, $yPos);
         
$yPos -= ($this->_lineHeight + $this->_lineGap);
      }
      return
$this->_totalHeight;
   }

   
/**
    * Adds a line to the internal array of lines
    * If the width is not passed, it will be calculated
    *
    * @param String $string Line of text
    * @param Float $width the width of this line (in case you know it at the time of passing)
    * @param Boolean $lastInParagraph. When true, the width of this line is recorded
    */
   
protected function addLine($string, $width = null, $lastInParagraph = True)
   {
      if (
is_null($width))
      {
         
$width = $this->parseString($string);
      }
      
$this->_lines[] = array(
         
'text'         => $string,
         
'width'         => $width,
         
'lastInParagraph'   => $lastInParagraph
         
);
      
$this->_totalHeight += $this->_lineHeight + $this->_lineGap;
   }

   
/**
    * sets (and checks) the alignment of the textblock
    *
    * @param $align has to be a constant of this class, indicating the alignment
    */
   
public function setAlignment($align)
   {
      if (!
in_array($align, array(self::ALIGN_LEFT, self::ALIGN_RIGHT, self::ALIGN_CENTER, self::ALIGN_JUSTIFY)))
      {
         
throw new Exception('this is not a valid alignment value');
      }
      else
      {
         
$this->_align = $align;
      }
   }
   
/**
    * setter for the width of the textblock
    * whatever is passed will be converted to a float without checking
    *
    * @param $width
    */
   
public function setBlockWidth($width)
   {
      
$this->_blockWidth = (float) $width;
   }
   
/**
    * getter for the blockWidth
    *
    * @return Float
    */
   
public function getBlockWidth()
   {
      if (
False === 0)
         
throw new Exception('Textblock width not set');
      else
         return
$this->_blockWidth;
   }
}

/** Excudo_Pdf.php */

#require_once 'Excudo_Pdf_Page.php';

/**
* Excudo PDF
*
* Extension of Zend PDF.
* The only difference is that the method newPage creates and instance of Excudo_Pdf_Page instead of Zend_Pdf_Page.
*
* @package    Zend_Pdf
* @license    http://framework.zend.com/license/new-bsd     New BSD License
*/
class Excudo_Pdf extends Zend_Pdf
{
   
public function newPage($param1, $param2 = null)
   {
      if (
$param2 === null) {
         return new
Excudo_Pdf_Page($param1, $this->_objFactory);
      } else {
         return new
Excudo_Pdf_Page($param1, $param2, $this->_objFactory);
      }
   }   
}


/** Excudo_Pdf_Page.php */

/**
* Excudo PDF Page
*
* Extension of Zend Pdf Page.
* Has 1 extra method which can create text-blocks
*
* @package    Zend_Pdf
* @license    http://framework.zend.com/license/new-bsd     New BSD License
*/
class Excudo_Pdf_Page extends Zend_Pdf_Page
{
   
public function drawTextBlock($text, $x, $y, $charEncoding = '', $wordSpaceAdjust=False)
   {
      if (
$this->_font === null) {
         
throw new Zend_Pdf_Exception('Font has not been set');
      }

      
$this->_addProcSet('Text');

      
$textObj = new Zend_Pdf_Element_String($this->_font->encodeString($text, $charEncoding));
      
$xObj   = new Zend_Pdf_Element_Numeric($x);
      
$yObj   = new Zend_Pdf_Element_Numeric($y);

      
$this->_contents .= "BT";
      if (
False !== $wordSpaceAdjust)
      {
         
$this->_contents .= sprintf(" %.3f Tw", $wordSpaceAdjust);
      }
      
$this->_contents .= "\n"
                
.  $xObj->toString() . ' ' . $yObj->toString() . " Td\n"
                
.  $textObj->toString() . " Tj\n"
                
.  "ET\n";
   }
}

download

=[Disclaimer]=     © 2005-2008 Excudo.net