]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/Sugarpdf/Sugarpdf.php
Release 6.5.16
[Github/sugarcrm.git] / include / Sugarpdf / Sugarpdf.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38
39
40 if(file_exists('custom/include/Sugarpdf/sugarpdf_config.php')){
41     require_once('custom/include/Sugarpdf/sugarpdf_config.php');
42 } else {
43     require_once('include/Sugarpdf/sugarpdf_config.php');
44 }
45
46 require_once('include/tcpdf/tcpdf.php');
47 require_once('include/Sugarpdf/SugarpdfHelper.php');
48
49 class Sugarpdf extends TCPDF
50 {
51     /**
52      * Stretch options constants
53      */
54     const STRETCH_NONE = 0;
55     const STRETCH_SCALE = 1;
56     const STRETCH_SCALE_FORCED = 2;
57     const STRETCH_SPACING = 3;
58     const STRETCH_SPACING_FORCED = 4;
59
60     /**
61      * This array is meant to hold an objects/data that we would like to pass between
62      * the controller and the view.  The bean will automatically be set for us, but this
63      * is meant to hold anything else.
64      */
65     var $sugarpdf_object_map = array();
66     /**
67      * The name of the current module.
68      */
69     var $module = '';
70     /**
71      * The name of the current action.
72      */
73     var $action = '';
74     /**
75      */
76     var $bean = null;
77      /**
78      * Any errors that occured this can either be set by the view or the controller or the model
79      */
80     var $errors = array();
81     /**
82      * Use to set the filename of the output pdf file.
83      */
84     var $fileName = PDF_FILENAME;
85     /**
86      * Use for the ACL access.
87      */
88     var $aclAction = PDF_ACL_ACCESS;
89     /**
90      * Constructor which will peform the setup.
91      */
92
93
94     function __construct($bean = null, $sugarpdf_object_map = array(),$orientation=PDF_PAGE_ORIENTATION, $unit=PDF_UNIT, $format=PDF_PAGE_FORMAT, $unicode=true, $encoding='UTF-8', $diskcache=false){
95         global $locale;
96       //  $encoding = $locale->getExportCharset();
97         if(empty($encoding)){
98             $encoding = "UTF-8";
99         }
100         parent::__construct($orientation,$unit,$format,$unicode,$encoding,$diskcache);
101         $this->module = $GLOBALS['module'];
102         $this->bean = $bean;
103         $this->sugarpdf_object_map = $sugarpdf_object_map;
104         if(!empty($_REQUEST["sugarpdf"])){
105             $this->action = $_REQUEST["sugarpdf"];
106         }
107     }
108
109     /**
110      * This method will be called from the controller and is not meant to be overridden.
111      */
112     function process(){
113         $this->preDisplay();
114         $this->display();
115
116     }
117
118     /**
119      * This method will display the errors on the page.
120      */
121     function displayErrors(){
122         foreach($this->errors as $error) {
123             echo '<span class="error">' . $error . '</span><br>';
124         }
125     }
126
127     /**
128      * [OVERRIDE] - This method is meant to overidden in a subclass. The purpose of this method is
129      * to allow a view to do some preprocessing before the display method is called. This becomes
130      * useful when you have a view defined at the application level and then within a module
131      * have a sub-view that extends from this application level view.  The application level
132      * view can do the setup in preDisplay() that is common to itself and any subviews
133      * and then the subview can just override display(). If it so desires, can also override
134      * preDisplay().
135      */
136     function preDisplay(){
137         // set document information
138         $this->SetCreator(PDF_CREATOR);
139         $this->SetAuthor(PDF_AUTHOR);
140         $this->SetTitle(PDF_TITLE);
141         $this->SetSubject(PDF_SUBJECT);
142         $this->SetKeywords(PDF_KEYWORDS);
143
144         // set other properties
145         $compression=false;
146         if(PDF_COMPRESSION == "on"){
147             $compression=true;
148         }
149         $this->SetCompression($compression);
150         $protection=array();
151         if(PDF_PROTECTION != ""){
152             $protection=explode(",",PDF_PROTECTION);
153         }
154
155         $this->SetProtection($protection,blowfishDecode(blowfishGetKey('sugarpdf_pdf_user_password'), PDF_USER_PASSWORD),blowfishDecode(blowfishGetKey('sugarpdf_pdf_owner_password'), PDF_OWNER_PASSWORD));
156         $this->setCellHeightRatio(K_CELL_HEIGHT_RATIO);
157         $this->setJPEGQuality(intval(PDF_JPEG_QUALITY));
158         $this->setPDFVersion(PDF_PDF_VERSION);
159
160         // set default header data
161         $this->setHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE, PDF_HEADER_STRING);
162
163         // set header and footer fonts
164         $this->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
165         $this->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
166
167         //set margins
168         $this->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
169         $this->setHeaderMargin(PDF_MARGIN_HEADER);
170         $this->setFooterMargin(PDF_MARGIN_FOOTER);
171
172         //set auto page breaks
173         $this->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
174
175         //set image scale factor
176         $this->setImageScale(PDF_IMAGE_SCALE_RATIO);
177
178         //set some language-dependent strings
179         //$this->setLanguageArray($l);
180
181         // ---------------------------------------------------------
182
183     }
184
185     /**
186      * [OVERRIDE] - This method is meant to overidden in a subclass.
187      */
188     function display(){
189         $this->AddPage();
190         $this->SetFont(PDF_FONT_NAME_MAIN,'B',16);
191         $this->MultiCell(0,0,'Tcpdf class for this module and action has not been implemented.',0,'C');
192         $this->Info();
193
194
195     }
196
197     /**
198      * [OVERRIDE]
199      * This method override the regular Header() method to enable the custom image directory in addition to the OOB image directory.
200      * This method is used to render the page header.
201      * It is automatically called by AddPage().
202      * @access public
203     * @see include/tcpdf/TCPDF#Header()
204      */
205     public function Header() {
206         $ormargins = $this->getOriginalMargins();
207         $headerfont = $this->getHeaderFont();
208         $headerdata = $this->getHeaderData();
209
210         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
211
212             // START SUGARPDF
213             $logo = K_PATH_CUSTOM_IMAGES.$headerdata['logo'];
214             $imsize = @getimagesize($logo);
215             if ($imsize === FALSE) {
216                 // encode spaces on filename
217                 $logo = str_replace(' ', '%20', $logo);
218                 $imsize = @getimagesize($logo);
219                 if ($imsize === FALSE) {
220                     $logo = K_PATH_IMAGES.$headerdata['logo'];
221                 }
222             }
223             // END SUGARPDF
224
225             $this->Image($logo, $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
226             $imgy = $this->getImageRBY();
227         } else {
228             $imgy = $this->GetY();
229         }
230         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
231         // set starting margin for text data cell
232         if ($this->getRTL()) {
233             $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
234         } else {
235             $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
236         }
237         $this->SetTextColor(0, 0, 0);
238         // header title
239         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
240         $this->SetX($header_x);
241         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
242         // header string
243         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
244         $this->SetX($header_x);
245         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
246         // print an ending header line
247         $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
248         $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
249         if ($this->getRTL()) {
250             $this->SetX($ormargins['right']);
251         } else {
252             $this->SetX($ormargins['left']);
253         }
254         $this->Cell(0, 0, '', 'T', 0, 'C');
255     }
256
257     /**
258     * [OVERRIDE] SetFont method in TCPDF Library
259     * This method override the regular SetFont() method to enable the custom font directory in addition to the OOB font directory.
260     *
261     * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
262     * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
263     * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
264     * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
265     * @access public
266     * @see include/tcpdf/TCPDF#SetFont()
267     */
268     public function SetFont($family, $style='', $size=0, $fontfile='') {
269
270         if(empty($fontfile) && defined('K_PATH_CUSTOM_FONTS')){
271             // This will force addFont to search the custom directory for font before the OOB directory
272             $fontfile = K_PATH_CUSTOM_FONTS."phantomFile.phantom";
273         }
274         parent::SetFont($family, $style, $size, $fontfile);
275     }
276
277     function Info(){
278
279         $this->SetFont(PDF_FONT_NAME_MAIN,'',12);
280         $this->MultiCell(0,0,'---',0,'L');
281         $this->MultiCell(0,0,'Class: '.get_class($this),0,'L');
282         $this->MultiCell(0,0,'Extends: '.get_parent_class($this),0,'L');
283         $this->MultiCell(0,0,'---',0,'L');
284         $this->MultiCell(0,0,'Module: '.$this->module,0,'L');
285         $this->MultiCell(0,0,'Tcpdf Action: '.$this->action,0,'L');
286         $this->MultiCell(0,0,'Bean ID: '.$this->bean->getFieldValue('id'),0,'L');
287         $this->SetFont(PDF_FONT_NAME_MAIN,'',12);
288         $this->MultiCell(0,0,'---',0,'L');
289
290     }
291
292     /**
293      * [OVERRIDE] Cell method in tcpdf library.
294      * Handle charset conversion and HTML entity decode.
295      * This method override the regular Cell() method to apply the prepare_string() function to
296      * the string to print in the PDF.
297      * The cell method is used by all the methods which print text (Write, MultiCell).
298      * @see include/tcpdf/TCPDF#Cell()
299      */
300     public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
301         parent::Cell($w, $h, prepare_string($txt), $border, $ln, $align, $fill, $link, $stretch);
302     }
303
304     /**
305      * This Ln1() method will always print a line break of one line height.
306      * The regular Ln() method print a line break which has the height of the last printed cell.
307      */
308     public function Ln1() {
309         parent::Ln($this->FontSize * $this->cell_height_ratio + 2 * $this->cMargin, false);
310     }
311
312
313     /**
314      * This method allow printing a table using the MultiCell method with a formatted options array in parameter
315      * Options :
316      * header options override the regular options for the header's cells - $options['header']
317      * cell options override the regular options for the specific cell - Array[line number (0 to x)][cell header]['options']
318      * @param $item Array[line number (0 to x)][cell header] = Cell content OR
319      *              Array[line number (0 to x)][cell header]['value'] = Cell content AND
320      *              Array[line number (0 to x)][cell header]['options'] = Array[cell properties] = values
321      * @param $options Array which can contain : width (array 'column name'=>'width value + % OR nothing'), isheader (bool), header (array), fill (string: HTML color), ishtml (bool) default: false, border (0: no border (defaul), 1: frame or all of the following characters: L ,T ,R ,B), align (L: left align, C: center, R: right align, J: justification), stretch (array 'column name'=>stretch type)
322      * @see MultiCell()
323      */
324     public function writeCellTable($item, $options=NULL)
325     {
326         // Save initial font values
327         $fontFamily = $this->getFontFamily();
328         $fontSize = $this->getFontSizePt();
329         $fontStyle = $this->getFontStyle();
330         $this->SetTextColor(0, 0, 0);
331
332         $options = $this->initOptionsForWriteCellTable($options, $item);
333
334         // HEADER
335         if(!isset($options['isheader']) || $options['isheader'] == true){
336             $headerOptions = $options;
337             if(!empty($options['header']) && is_array($options['header'])){
338                 $headerOptions = $this->initOptionsForWriteCellTable($options['header'], $item);
339             }
340             foreach($item[0] as $k => $v){
341                 $header[$k]=$k;
342             }
343             $h = $this->getLineHeightFromArray($header, $options["width"]);
344             foreach ($header as $v)
345                 $this->MultiCell($options["width"][$v],$h,$v,$headerOptions['border'],$headerOptions['align'],$headerOptions['fillstate'],0,'','',true, $options['stretch'][$v], $headerOptions['ishtml']);
346             $this->SetFillColorArray($this->convertHTMLColorToDec($options['fill']));
347             $this->Ln();
348         }
349
350         // MAIN
351         // default font
352         $this->SetFont($fontFamily,$fontStyle,$fontSize);
353         $this->SetTextColor(0, 0, 0);
354         $even=true;
355         $firstrow = true;
356         // LINES
357         foreach($item as $k=>$line){
358             $even=!$even;
359             $h = $this->getLineHeightFromArray($line, $options["width"]);
360             // in the case when cell height is greater than page height
361             // need to adjust the current page number
362             // so the following output will not overlap the previous output
363             if ($this->getNumPages() != $this->getPage()) {
364                 if (!empty($this->currentY)) {
365                     $this->y = $this->currentY;
366                     $this->currentY = 0;
367                 }
368                 $this->setPage($this->getNumPages());
369             }
370             $firstcell = true;
371             //CELLS
372             foreach($line as $kk=>$cell){
373                 $cellOptions = $options;
374                 $value = $cell;
375
376                 if(is_array($cell)){
377                     $value = $cell['value'];
378                     if(!empty($cell['options']) && is_array($cell['options'])){
379                         $cellOptions = $this->initOptionsForWriteCellTable($cell['options'], $item);
380                     }
381                 }
382
383                                 //Bug45077-replacing single quote entities
384                                         $value=str_replace("&#039;","'",$value);
385                                 //Bug45077-replacing double quote entities
386                                         $value=str_replace("&quot;",'"',$value);
387
388                 if($even && !empty($options['evencolor'])){
389                     $this->SetFillColorArray($this->convertHTMLColorToDec($options['evencolor']));
390                     $cellOptions['fillstate']=1;
391                 }else if(!$even && !empty($options['oddcolor'])){
392                     $this->SetFillColorArray($this->convertHTMLColorToDec($options['oddcolor']));
393                     $cellOptions['fillstate']=1;
394                 }
395
396                 if ($firstrow) {
397                     $this->MultiCell($options["width"][$kk],$h,$value,$cellOptions['border'],$cellOptions['align'],$cellOptions['fillstate'],0,'','',true, $options['stretch'][$kk], $cellOptions['ishtml'], true, 0, false);
398                 } else {
399                     if ($firstcell) {
400                         // add page only once (for the first cell)
401                         $this->MultiCell($options["width"][$kk],$h,$value,$cellOptions['border'],$cellOptions['align'],$cellOptions['fillstate'],0,'','',true,0,$cellOptions['ishtml'], true, 0, true);
402                         $firstcell = false;
403                     } else {
404                         $this->MultiCell($options["width"][$kk],$h,$value,$cellOptions['border'],$cellOptions['align'],$cellOptions['fillstate'],0,'','',true,0,$cellOptions['ishtml'], true, 0, false);
405                     }
406                 }
407
408                 $this->SetFillColorArray($this->convertHTMLColorToDec($options['fill']));
409             }
410             $this->Ln();
411             $firstrow = false;
412         }
413         $this->SetFont($fontFamily,$fontStyle,$fontSize);
414         $this->SetTextColor(0, 0, 0);
415     }
416
417     /**
418      * This method allow printing a table using the writeHTML method with a formatted array in parameter
419      * This method can also return the table as HTML code
420      * @param $item Array[line number (0 to x)][cell header] = Cell content OR
421      *              Array[line number (0 to x)][cell header]['value'] = Cell content AND
422      *              Array[line number (0 to x)][cell header]['options'] = Array[cell properties] = values
423      * @param $returnHtml (bool) Return the table as HTML code instead of printing the HTML table
424      * @param $options Array which can contain : table (array of "HTML proprty"=>"value"),td (array of "HTML proprty"=>"value"), tr (array of "HTML proprty"=>"value"), isheader(bool), header (array of "HTML proprty"=>"value"), width (array 'column name'=>'width value + unit OR nothing')
425      * @return the HTML code if $returnHtml set to true
426      */
427     public function writeHTMLTable($item, $returnHtml=false, $options=NULL){
428         //TODO ISSUE - width in % for the td have to be multiply by the number of column.
429         //     ex: for a width of 20% in a table of 6 columns the width will have to be 120% (20*6).
430         $html="";
431         $line="";
432         if(!empty($options)){
433             foreach($options as $k=>$v){
434                 $tmp[strtolower($k)]=$v;
435             }
436             $options=$tmp;
437         }else{
438             $options=array();
439         }
440         if(!isset($options["isheader"]) || $options["isheader"] == true){
441             if(!empty($options["header"])){
442                 foreach($options["header"] as $k=>$v){
443                     $tmp[strtolower($k)]=$v;
444                 }
445                 $options["header"]=$tmp;
446             }else{
447                 $options["header"]=array("tr"=>array("bgcolor"=>"#DCDCDC"),"td"=>array());
448             }
449
450             foreach($item[0] as $k => $v){
451                 if(!empty($options["width"]))$options["header"]["td"]["width"]=$options["width"][$k];
452                 $line.=$this->wrap("td", $k, $options["header"]);
453             }
454             $html.=$this->wrap("tr", $line, $options["header"]);
455         }
456         $even = true;
457         foreach ($item as $k=>$v){
458             $even = !$even;
459             $line="";
460
461             if($even){
462                 if (isset($options['evencolor']))
463                 {
464                     $options["tr"]["bgcolor"] = $options['evencolor'];
465                 }
466             } else {
467                 if (isset($options['oddcolor']))
468                 {
469                     $options["tr"]["bgcolor"] = $options['oddcolor'];
470                 }
471             }
472             foreach($v as $kk => $vv){
473                 if(!empty($options["width"]) && isset($options["width"][$kk]))$options["td"]["width"]=$options["width"][$kk];
474                 $line.=$this->wrap("td", $vv, $options);
475             }
476             $html.=$this->wrap("tr", $line, $options);
477         }
478         $html=$this->wrap("table", $html, $options);
479         if($returnHtml){
480             return $html;
481         }else{
482             $this->writeHTML($html);
483         }
484     }
485
486     /**
487      * return the HTML code of the value wrap with the tag $tag. This method handle options (general and specific)
488      * @param $tag
489      * @param $value
490      * @param $options
491      * @return the HTML wrapped code
492      */
493     private function wrap($tag, $value, $options){
494         if(empty($options[$tag])){
495             $options[$tag] = array();
496         }
497         if(is_array($value)){
498             if(isset($value["options"])){
499                 // The options of a specific entity overwrite the general options
500                 $options[$tag] = $value["options"];
501             }
502             if(isset($value["value"])){
503                 $value = $value["value"];
504             }else{
505                 $value = "";
506             }
507         }
508         return wrapTag($tag, $value, $options[$tag]);
509     }
510
511     /**
512      * Return the heigth of a line depending of the width, the font and the content
513      * @param $line Array containing the data of all the cells of the line
514      * @param $width Array containing the width of all the cells of the line
515      * @return The heigth of the line
516      */
517     private function getLineHeightFromArray($line, $width){
518         $h=0;
519         foreach($line as $kk=>$cell){
520             $cellValue = $cell;
521             if(is_array($cellValue)){
522                 $tmp = $cellValue['value'];
523                 $cellValue = $tmp;
524             }
525             if($h<$this->getNumLines($cellValue, $width[$kk])){
526                 $h=$this->getNumLines($cellValue, $width[$kk]);
527             }
528         }
529         return $h * $this->FontSize * $this->cell_height_ratio + 2 * $this->cMargin;
530     }
531
532     /**
533      * Private method for writeCellTable which format and initialize the options array.
534      * @param $options array
535      * @param $item array
536      * @return $options array
537      */
538     private function initOptionsForWriteCellTable($options, $item){
539        if(!empty($options)){
540             foreach($options as $k=>$v){
541                 $tmp[strtolower($k)]=$v;
542             }
543             $options=$tmp;
544         }else{
545             $options=array();
546         }
547         // set to default if empty
548         if(empty($options["width"]) || !is_array($options["width"])){
549             $colNum = count($item[0]);
550             $defaultWidth = $this->getRemainingWidth()/$colNum;
551             foreach($item[0] as $k => $v){
552                 $options["width"][$k]=$defaultWidth;
553             }
554         }else{
555             foreach($options["width"] as $k => $v){
556                 $options["width"][$k] = $this->getHTMLUnitToUnits($v, $this->getRemainingWidth());
557             }
558
559         }
560
561         if(empty($options["border"])){
562             $options["border"]=0;
563         }
564
565         if(empty($options["align"])){
566             $options["align"]="L";
567         }
568
569         if(empty($options['ishtml'])){
570             $options['ishtml'] = false;
571         }
572         if(empty($options['border'])){
573             $options['border'] = 0;
574         }
575         foreach($item[0] as $k => $v)
576             if (empty($options['stretch'][$k]))
577                 $options['stretch'][$k] = self::STRETCH_NONE;
578
579         if(!empty($options['fill'])){
580             $this->SetFillColorArray($this->convertHTMLColorToDec($options['fill']));
581             $options['fillstate']=1;
582         }else{
583             $options['fill']="#FFFFFF";//white
584             $options['fillstate']=0;
585         }
586
587         if(!empty($options['fontfamily'])){
588             $fontFamily = $options['fontfamily'];
589         }else{
590             $fontFamily = $this->getFontFamily();
591         }
592         if(!empty($options['fontsize'])){
593             $fontSize = $options['fontsize'];
594         }else{
595             $fontSize = $this->getFontSizePt();
596         }
597         if(!empty($options['fontstyle'])){
598             $fontStyle = $options['fontstyle'];
599         }else{
600             $fontStyle = $this->getFontStyle();
601         }
602         if(!empty($options['textcolor'])){
603             $this->SetTextColorArray($this->convertHTMLColorToDec($options['textcolor']));
604         }else{
605             $this->SetTextColor(0, 0, 0);//black
606         }
607
608         $this->SetFont($fontFamily, $fontStyle, $fontSize);
609
610         return $options;
611     }
612
613     /**
614     * This is method is fix for a better handling of the count. This method now handle the line break
615     * between words.
616     * This method returns the estimated number of lines required to print the text.
617     * @param string $txt text to print
618     * @param float $w width of cell. If 0, they extend up to the right margin of the page.
619     * @return int Return the estimated number of lines.
620     * @access public
621     * @since 4.5.011
622     * @OVERRIDE
623     */
624     public function getNumLines($txt, $w=0) {
625         $lines = 0;
626         if (empty($w) OR ($w <= 0)) {
627             if ($this->rtl) {
628                 $w = $this->x - $this->lMargin;
629             } else {
630                 $w = $this->w - $this->rMargin - $this->x;
631             }
632         }
633         // max column width
634         $wmax = $w - (2 * $this->cMargin);
635         // remove carriage returns
636         $txt = str_replace("\r", '', $txt);
637         // divide text in blocks
638         $txtblocks = explode("\n", $txt);
639         // for each block;
640         foreach ($txtblocks as $block) {
641             // estimate the number of lines
642             if(empty($block)){
643                 $lines++;
644             // If the block is in more than one line
645             }else if(ceil($this->GetStringWidth($block) / $wmax)>1){
646                 //divide into words
647                 $words = explode(" ", $block);
648                 //TODO explode with space is not the best things to do...
649                 $wordBlock = "";
650                 $first=true;
651                 $lastNum = 0;
652                 $run = false;
653
654                 for($i=0; $i<count($words); $i++){
655                     if($first){
656                         $wordBlock = $words[$i];
657                     }else{
658                         $wordBlock .= " ".$words[$i];
659                     }
660                     if(ceil($this->GetStringWidth($wordBlock) / $wmax)>1){
661                         if($first){
662                             $lastNum = ceil($this->GetStringWidth($wordBlock) / $wmax);
663                             $run = true;
664                             $first = false;
665                         }else{
666                             if($run && $lastNum == ceil($this->GetStringWidth($wordBlock) / $wmax)){
667                                 // save the number of line if it is the last loop
668                                 if($i+1 == count($words)){
669                                     $lines += ceil($this->GetStringWidth($wordBlock) / $wmax);
670                                 }
671                                 continue;
672                             }else{
673                                 $first = true;
674                                 $lines += ceil($this->GetStringWidth( substr($wordBlock, 0, (strlen($wordBlock) - strlen(" ".$words[$i]))) ) / $wmax);
675                                 $i--;
676                                 $lastNum = 0;
677                                 $run = false;
678                             }
679                         }
680
681                     }else{
682                         $first = false;
683                     }
684                     // save the number of line if it is the last loop
685                     if($i+1 == count($words)){
686                         $lines += ceil($this->GetStringWidth($wordBlock) / $wmax);
687                     }
688                 }
689
690             }else{
691                 $lines++;
692             }
693         }
694         return $lines;
695     }
696
697     /**
698      * Disable zlib output compression if we are downloading the PDF.
699      *
700      * @see TCPDF::Output()
701      */
702     public function Output($name='doc.pdf', $dest='I')
703     {
704         if ( $dest == 'I' || $dest == 'D') {
705             ini_set('zlib.output_compression', 'Off');
706         }
707
708         return parent::Output($name,$dest);
709     }
710 }
711