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($lastLinenullFalse);
                     
$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 && !$line['lastInParagraph'])
               
$wsa = ($this->_blockWidth $line['width']) / ($wordCount 1);
         }
         if (
$this->_align == self::ALIGN_JUSTIFY)
            
$page->drawTextBlock($line['text'], $xPos$yPosnull$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_LEFTself::ALIGN_RIGHTself::ALIGN_CENTERself::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-2018 Excudo.net