Répertoire de codes source
Zend_Pdf_Cell | |
---|---|
déposé par golivier le 12/03/2008 nombre de visites : 10127 |
Cette classe permet de gérer des cellules sur un fichier PDF. Elle est basée sur http://framework.zend.com/issues/browse/ZF-1254 avec pas mal de corrections sur les positionnements et ajout de fonctionnalités de positionnement. La classe à comme nom TI_Pdf_Cell afin de ne pas interférer avec celle de Zend qui sera peut être un jour mis en standard |
<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also 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 we can send you a copy immediately. * * @package Zend_Pdf * @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ /** Zend_Pdf_Exception */ require_once 'Zend/Pdf/Exception.php'; /** Zend_Pdf_Resource_Font */ require_once 'Zend/Pdf/Resource/Font.php'; /** Zend_Pdf_Page*/ require_once 'Zend/Pdf/Page.php'; /** Zend_Pdf_Cmap */ require_once 'Zend/Pdf/Cmap.php'; /** Zend_Pdf_Color */ require_once 'Zend/Pdf/Color.php'; class TI_Pdf_Cell { const ALIGN_LEFT=0; const ALIGN_RIGHT=1; const ALIGN_CENTER=2; const ALIGN_JUSTIFY=4; const VALIGN_CENTER=8; const POSITION_LEFT=0; const POSITION_RIGHT=1; const POSITION_CENTER_X=2; const POSITION_CENTER_Y=4; const POSITION_TOP=8; const POSITION_BOTTOM=16; /** * Width of the cell */ private $_width; /** * Height of the cell */ private $_height; /** * Upper left X coordinate */ private $_x; /** * Upper left Y coordinate */ private $_y; /** * Current page for the cell to belong to. */ private $_page; /** * How the cell should be positioned on the page */ private $_position; /** * 3 diminsional array that stores the text in the cell. * The first diminsion is for the line number. * The second diminsion is for the section number. * The third diminsion is the properties for that section. * * The only properties (3rd diminsion) valid for a non-zero section (second diminsion) * are text, font and encoding. */ private $_text; /** * Keeps track of the current line number */ private $_lineNumber; /** * Keeps track of the current section */ private $_section; /** * Current font that is being used. */ private $_font; /** * Current font's height */ private $_fontSize; /** * When we want to auto-calculate the height, this is the * height's current value. */ private $_autoHeight; /** * When we want to aut-calculate the width, this is the * width's current value. */ private $_autoWidth; /** * Border around the cell. * * Defaults to BORDER_NONE. */ private $_border; /** * Background color * * Defaults white */ private $_fillColor; private $_wordWrap = false; /** * Creates a new cell. * * @param Zend_Pdf_Page $page Page for the cell to belong to. * @param constant $position What type of position to have * @param int $width Width of the cell. Provide 0 for auto-width. * @param int $height Height of the cell. Provide 0 for auto-height. */ public function __construct($page,$position=TI_Pdf_Cell::POSITION_LEFT,$width=0,$height=0) { $this->setPage($page); $this->_lineNumber=0; $this->_section=0; $this->_font=$page->getFont(); $this->_fontSize=$page->getFontSize(); $this->_initializeLine(); $this->_initializeBorder(); $this->setPosition($position); $this->setWidth($width); $this->setHeight($height); $this->setFillColor(new Zend_Pdf_Color_RGB(255,255,255)); } private function _initializeBorder() { $this->_border['pattern']=Zend_Pdf_Page::LINE_DASHING_SOLID; $this->_border['size']=0; $this->_border['color']=new Zend_Pdf_Color_RGB(0,0,0); } /** * Wraps the section if needed. * * This function explodes the section's text, and adds one word at a time until it goes over * the boundry. Once it goes over the boundry, it wraps the rest of the text up into its * own text section and calls addText. If the current section had been wrapped, then * this function returns true, otherwise it returns false. * * @param array $section Section of text to wrap. * @return boolean True if it wrapped text, false if it did not. */ private function _wordWrap($section) { if (!$this->_wordWrap) return false; if ($this->isAutoWidth()) { $maxWidth=$this->_page->getWidth(); } else { $maxWidth=$this->_width; } $lineWidth=$this->_text[$this->_lineNumber]['width']; //Lignes $splitSection=explode("\n",$section['text']); if (count($splitSection)>1) { foreach ($splitSection as $ndx => $txt) { $this->addText($txt); if ($ndx+1 < count($splitSection)) $this->newLine(); } return true; } //if adding this section over flows borders if ($lineWidth+$section['width'] > $maxWidth) { //section of text is greater than our box's diminsions $splitSection=explode(' ',$section['text']); $maxTextSection=$this->_makeTextSection(array_shift($splitSection)); while($maxTextSection['text']!=null && $lineWidth + $maxTextSection['width'] < $maxWidth) { $this->addText($maxTextSection['text'].' '); $lineWidth=$this->_text[$this->_lineNumber]['width']; $maxTextSection=$this->_makeTextSection(array_shift($splitSection)); } $this->newLine(); $restOfText=implode(' ',$splitSection); $this->addText($maxTextSection['text'].' '.$restOfText); return true; } return false; } /** * Turns a string of text into a text section * * @param string $text String of text to turn into a section. * @return array A text section array. */ private function _makeTextSection($text,$charEncoding='') { $section=array(); $section['text']=$text; $section['encoding']=$charEncoding; $section['font']=$this->_font; $section['fontSize']=$this->_fontSize; $section['width']=$this->_getTextWidth($section); return $section; } /** * Adds more text to the cell. This will create a new section of text. * * The text is not written to the PDF until write() is called. * * If this section is not the first section, then the alignment, x and y variables are ignored. * * @param string $text Text to add to the section * @param mixed $alignment (Optional) How to align the text in the cell. If no alignment is * specified, then it uses the previous line's alignment. If this is the first line in the * cell, then it uses TI_Pdf_Cell::ALIGN_LEFT as default. * @param int $x (Optional) Offset of X from the relative position of this line in the cell. * Defaults to 0 * @param string $charEncoding (Optional) Encoding of this particular section of text. * Defaults to current locale. */ public function addText($text, $alignment=null, $offset=0, $charEncoding='') { //Set the alignment if ($alignment!==null) { $this->_text[$this->_lineNumber]['alignment']=$alignment; } else { $this->_text[$this->_lineNumber]['alignment']; if ($this->_lineNumber==0) { if ($this->_text[$this->_lineNumber]['alignment']==null) { //defaults to left alignment $this->_text[$this->_lineNumber]['alignment']=TI_Pdf_Cell::ALIGN_LEFT; } //we have word wrapped on the first line, so don't do anything } else { //else defaults to previous line's alignment $this->_text[$this->_lineNumber]['alignment']=$this->_text[$this->_lineNumber-1]['alignment']; } } $section=$this->_makeTextSection($text,$charEncoding); if ($this->_wordWrap(&$section)) { return; //word wrap has already taken care of calling addText } $this->_text[$this->_lineNumber][$this->_section]['text']=$section['text']; $this->_text[$this->_lineNumber][$this->_section]['encoding']=$section['encoding']; $this->_text[$this->_lineNumber][$this->_section]['font']=$section['font']; $this->_text[$this->_lineNumber][$this->_section]['fontSize']=$section['fontSize']; $this->_text[$this->_lineNumber][$this->_section]['width']=$section['width']; //if we haven't set a width, do so and initialize it to the width of this piece of text, //otherwise just add the width to this piece of text. if (isset($this->_text[$this->_lineNumber]['width'])) { $this->_text[$this->_lineNumber]['width']+=$this->_text[$this->_lineNumber][$this->_section]['width']; } else { $this->_text[$this->_lineNumber]['width']=$this->_text[$this->_lineNumber][$this->_section]['width']; } $this->_text[$this->_lineNumber]['x']=$offset; //calculate the max width of the cell if we have an auto-size width cell if ($this->isAutoWidth()) { $this->_autoWidth = max($this->_text[$this->_lineNumber]['width'],$this->_autoWidth); } //calculate the max height of this line if we have an auto-size height cell if ($this->isAutoHeight()) { if (isset($this->_text[$this->_lineNumber]['height'])) { $this->_text[$this->_lineNumber]['height'] = max($this->_text[$this->_lineNumber]['height'], ($this->_font->getLineHeight()/$this->_font->getUnitsPerEm())*$this->_fontSize); $this->_autoHeight=max($this->_autoHeight,$this->_text[$this->_lineNumber]['height']); $this->_text[$this->_lineNumber]['fontHeight'] = ($this->_font->getLineHeight()/$this->_font->getUnitsPerEm())*$this->_fontSize; } else { $this->_text[$this->_lineNumber]['height']=($this->_font->getLineHeight()/$this->_font->getUnitsPerEm())*$this->_fontSize; $this->_autoHeight=($this->_font->getLineHeight()/$this->_font->getUnitsPerEm())*$this->_fontSize; $this->_text[$this->_lineNumber]['fontHeight'] = ($this->_font->getLineHeight()/$this->_font->getUnitsPerEm())*$this->_fontSize; } } else { $this->_text[$this->_lineNumber]['height'] = $this->_height; $this->_text[$this->_lineNumber]['fontHeight'] = ($this->_font->getLineHeight()/$this->_font->getUnitsPerEm())*$this->_fontSize; } $this->_section++; } public function addHtml($text, $alignment=null, $offset=0) { $this->addText($text, $alignment, $offset); $this->_text[$this->_lineNumber][$this->_section-1]['html']=true; $this->_drawHtml($text, 0,0,'', true); //Initialise les infos seulement } /** * Initialize a new line in the cell. * * @return void */ private function _initializeLine() { $this->_text[$this->_lineNumber]['x']=0; $this->_text[$this->_lineNumber]['width']=0; $this->_text[$this->_lineNumber]['height']=0; $this->_text[$this->_lineNumber]['alignment']=null; $this->_text[$this->_lineNumber]['fontHeight']=0; } /** * Adds a new line to the cell. */ public function newLine() { $this->_section=0; $this->_lineNumber++; $this->_text[$this->_lineNumber]=array(); $this->_text[$this->_lineNumber][0]['text']=''; $this->_text[$this->_lineNumber][0]['encoding']=''; $this->_text[$this->_lineNumber][0]['font']=$this->_font; $this->_text[$this->_lineNumber][0]['fontSize']=$this->_fontSize; $this->_text[$this->_lineNumber][0]['width']=0; $this->_text[$this->_lineNumber][0]['fontHeight']=0; $this->_initializeLine(); $this->_text[$this->_lineNumber]['alignment']=$this->_text[$this->_lineNumber-1]['alignment']; //add the last cell's height to the auto height if we have an auto-height box. if ($this->isAutoHeight()) { $this->_autoHeight+=$this->_text[$this->_lineNumber-1]['height']; } } /** * Returns the width of the cell, without the border * * @return int Width of cell, in pixels, without the border. */ public function getWidth() { if ($this->isAutoWidth()) return $this->_autoWidth; else return $this->_width; } /** * Returns the height of the cell, without the border * * @return int Width of cell, in pixels, without the border. */ public function getHeight() { if ($this->isAutoHeight()) return $this->_autoHeight; else return $this->_height; } public function getPosition() { return $this->_position; } public function getPage() { return $this->_page; } /** * Returns the type of border. * * Currently only a solid border is supported. * * @return int Pattern of border */ public function getBorderPattern() { return $this->_border['pattern']; } /** * Returns the size in pixels of the border around the cell. * * @return int Size of the border, in pixels. */ public function getBorderSize() { return $this->_border['size']; } /** * Returns the color of the border * * @return Zend_Pdf_Color Color of the border. */ public function getBorderColor() { return $this->_border['color']; } /** * Sets the border around the cell. * * A border may be placed around the cell. If the border is greater than * one pixel in width, then the border grows outwards away from the cell. * * For Example: * * If you create a cell with a width of 100 pixels, and you have a border of * 10 pixels, then the total width of the cell is 120 pixels. * * @todo Implement more borders. * @param int $pattern Type of border to draw. * @param int $size Size of border, in pixels. * @param Zend_Pdf_Color $color Color for the border. Defaults to * Zend_Pdf_Color_RGB(0,0,0) */ public function setBorder($size=1,$pattern=null,$color=null) { if ($pattern===null) { $this->_border['pattern']=Zend_Pdf_Page::LINE_DASHING_SOLID; } else { $this->_border['pattern']=$pattern; } $this->_border['size']=$size; if ($color===null) { $this->_border['color']=new Zend_Pdf_Color_RGB(0,0,0); } } /** * Sets the location of where the cell's upper left corner should be placed. * If the alignment is set, then x and y are offsets to the alignment. * * @param int $x X offset for the cell. * @param int $y Y offset for the cell. * @param mixed $alignment (Optional) How to align the cell with the X and Y as offsets. * Defaults to none. */ public function setLocation($x, $y, $alignment=TI_Pdf_Cell::ALIGN_LEFT) { $this->_position=$alignment; $this->_x=$x; $this->_y=$y; } /** * Changes the fill color * * @param Zend_Pdf_Color $color */ public function setFillColor($color) { $this->_fillColor = $color; } /** * Changes the word wrap possibility * * @param Bool $ww */ public function setWordWrap($ww) { $this->_wordWrap = $ww; } /** * Changes the current section's font to the one specified. * * @param Zend_Pdf_Font $font Font of the current section. */ public function setFont($font, $size) { $this->_font=$font; $this->_fontSize=$size; } /** * Sets the current section's encoding to the encoding specified. * * @param string $encoding Character encoding of the text. */ public function setCharEncoding($encoding) { $this->_text[$this->_lineNumber][$this->_section]['encoding']=$encoding; } public function setPage($page) { $this->_page=$page; } /** * Sets the maximum height of the cell, without the border. * * @todo Decide what to do if size of text exceeds cell height. * @param int $height Maximum height of the cell, in pixels. */ public function setHeight($height) { $this->_height=$height; $this->_autoHeight=$height; } /** * Sets the maximum width of the cell, without the border. * * If text is added that exceeds the width of the cell, then the text * will be wrapped at a space. * * @todo Decide what to do if one word exceeds border width. * @param int $width Maximum width of the cell, in pixels. */ public function setWidth($width) { $this->_width=$width; $this->_autoWidth=$width; } public function setPosition($position) { $this->_position=$position; } /** * Returns true if this cell is using auto width * * @return boolean If this cell is set to be auto-width */ public function isAutoWidth() { if ($this->_width==0) { return true; } else { return false; } } /** * Returns true if the cell is using auto height * * @return boolean If this cell is set to be auto height. */ public function isAutoHeight() { if ($this->_height==0) { return true; } else { return false; } } /** * Draws the cell to the PDF page. * * This function will parse the internal $_text field and draw the cell to the PDF page. */ public function write() { if (!($this->_page instanceof Zend_Pdf_Page)) { throw new Zend_Pdf_Exception("The PDF page that the cell is attempting to write to is not a valid page."); } if (!($this->_font instanceof Zend_Pdf_Resource_Font)) { throw new Zend_Pdf_Exception('No font has been set'); } if ($this->isAutoHeight()) { $this->_height=$this->_autoHeight; } if ($this->isAutoWidth()) { $this->_width=$this->_autoWidth; } //positions of the cell's box //initalize the diminsions to defaults $top=$this->_y; $left=$this->_x; $right=$left+$this->getWidth(); $bottom=$top-$this->getHeight(); if ($this->_position & TI_Pdf_Cell::POSITION_BOTTOM) { $top=$this->getHeight()+5; $bottom=$top+$this->getHeight(); } if ($this->_position & TI_Pdf_Cell::POSITION_CENTER_X) { $left=(($this->_page->getWidth()-$this->getWidth()) + $this->_x)/2; $right=$left+$this->getWidth(); } if ($this->_position & TI_Pdf_Cell::POSITION_CENTER_Y) { $top=$this->_page->getHeight()/2 + $this->getHeight()/2 - $this->_y; $bottom=$top-$this->getHeight(); } if ($this->_position & TI_Pdf_Cell::POSITION_TOP ) { $top=$this->_page->getHeight()-$top; $bottom=$top-$this->getHeight(); } if ($this->_position & TI_Pdf_Cell::POSITION_RIGHT) { $left=$this->_page->getWidth() - $this->getWidth(); $right=$left+$this->getWidth(); } $currentY=$top; //save the page's font so we can put it back after writing the cell $pageFont=$this->_page->getFont(); $fontSize=$this->_page->getFontSize(); //restore old size and font $this->_page->setFont($pageFont,$fontSize); //draw the border if ($this->_border['size']>0) { $style=new Zend_Pdf_Style(); $style->setLineColor($this->getBorderColor()); $style->setFillColor($this->_fillColor); $style->setLineDashingPattern($this->getBorderPattern()); $style->setLineWidth($this->_border['size']); $this->_page->setStyle($style); $this->_page->drawRectangle($right+2,$top-2,$left-2,$bottom-4); $style->setFillColor(new Zend_Pdf_Color_RGB(0,0,0)); $this->_page->setStyle($style); } //draw every section of every page. for ($i=0; $i<count($this->_text); $i++) { $currentX=$left; if ($this->_text[$i]['alignment'] & TI_Pdf_Cell::ALIGN_RIGHT) $currentX=$right - $this->_text[$i]['width']; elseif ($this->_text[$i]['alignment'] & TI_Pdf_Cell::ALIGN_CENTER) $currentX=($right-$left)/2+$left-$this->_text[$i]['width']/2; //elseif ($this->_text[$i]['alignment'] & TI_Pdf_Cell::ALIGN_JUSTIFY) @todo if ($currentX<$left) $currentX = $left; if (($this->_text[$i]['alignment'] & TI_Pdf_Cell::VALIGN_CENTER) and (isset($this->_text[$i]['fontHeight']))) $currentY+=($top-$bottom-$this->_text[$i]['fontHeight'])/2; //add the offset $currentX+=$this->_text[$i]['x']; $currentY-=$this->_text[$i]['height']; //count() - 4 because of the 4 properties to this text. for ($j=0; $j<count($this->_text[$i])-5; $j++) { //$this->_page->clipRectangle($currentX,$currentY,$currentX+$this->_text[$i][$j]['width'],$currentY-$this->_text[$i][$j]['height']); $this->_page->setFont($this->_text[$i][$j]['font'],$this->_text[$i][$j]['fontSize']); if (isset($this->_text[$i][$j]['html'])) $this->_drawHtml($this->_text[$i][$j]['text'],$currentX,$currentY,$this->_text[$i][$j]['encoding']); else $this->_page->drawText($this->_text[$i][$j]['text'],$currentX,$currentY,$this->_text[$i][$j]['encoding']); $currentX+=$this->_text[$i][$j]['width']; } } } private function _drawHtml($html, $X, $Y, $encode, $initInfo = false) { $html=strip_tags($html,"<img>"); //remove all unsupported tags //replace carriage returns, newlines and tabs $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); $html = strtr($html, $repTable); $pattern = '/(<[^>]+>)/Uu'; $a = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //explodes the string foreach($a as $key=>$element) { if (!preg_match($pattern, $element)) { $this->_page->drawText($element, $X, $Y, $encode); } else { $element = substr($element, 1, -1); //Tag if($element{0}=='/') { return; } else { //Extract attributes // get tag name preg_match('/([a-zA-Z0-9]*)/', $element, $tag); $tag = strtolower($tag[0]); // get attributes preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER); $attr = array(); // reset attribute array while(list($id,$name)=each($attr_array[1])) { $attr[strtolower($name)] = $attr_array[2][$id]; } if ($initInfo) $this->_initInfoHtmlTag($tag, $attr, $this->_text[$this->_lineNumber]); else $this->_drawHtmlTag($tag, $X, $Y, $attr, $encode); } } } } private function _drawHtmlTag($tag, $X, $Y, $attr, $encode) { switch ($tag) { case 'img' : if (isset($attr['src'])) { require_once 'Zend/Pdf/Resource/ImageFactory.php'; $image = Zend_Pdf_Resource_ImageFactory::factory($attr['src']); $this->_page->drawImage($image, $X, $Y-2, $X+$image->getPixelWidth(), $Y+$image->getPixelHeight()-2); } break; } } private function _initInfoHtmlTag($tag, $attr, &$info) { switch ($tag) { case 'img' : if (isset($attr['src'])) { require_once 'Zend/Pdf/Resource/ImageFactory.php'; $image = Zend_Pdf_Resource_ImageFactory::factory($attr['src']); $info['width'] = $image->getPixelWidth(); $info['fontHeight']= $image->getPixelHeight(); } break; } } /** * Returns the width of the text * * @param array $textSection A section of text that has the font, character encoding and text. * @return integer Number of PDF units wide the text should be. * @todo Make work for non ASCII characters. */ private function _getTextWidth($textSection) { //make into a character array $charArray=array(); for ($x=0;$x<strlen($textSection['text']);$x++) { $charArray[]=ord(substr($textSection['text'],$x,1)); } $charArray=$textSection['font']->cmap->glyphNumbersForCharacters($charArray); //get the lengths $lengths=$textSection['font']->widthsForGlyphs($charArray); $fontGlyphWidth=array_sum($lengths); return $fontGlyphWidth/$this->_font->getUnitsPerEm()*$this->_fontSize; } } |
|