source: pmb4.2/trunk/fuentes/pmb/classes/html2pdf/html2pdf.class.php @ 815

Last change on this file since 815 was 815, checked in by jrpelegrina, 5 years ago

Initial release of pmb 4.2

  • Property svn:executable set to *
File size: 245.7 KB
Line 
1<?php
2/**
3 * HTML2PDF Librairy - main class
4 *
5 * HTML => PDF convertor
6 * distributed under the LGPL License
7 *
8 * @author  Laurent MINGUET <webmaster@html2pdf.fr>
9 * @version 4.04
10 */
11
12if (!defined('__CLASS_HTML2PDF__')) {
13
14    define('__CLASS_HTML2PDF__', '4.04');
15    define('HTML2PDF_USED_TCPDF_VERSION', '5.0.002');
16
17    require_once(dirname(__FILE__).'/_class/exception.class.php');
18    require_once(dirname(__FILE__).'/_class/locale.class.php');
19    require_once(dirname(__FILE__).'/_class/myPdf.class.php');
20    require_once(dirname(__FILE__).'/_class/parsingHtml.class.php');
21    require_once(dirname(__FILE__).'/_class/parsingCss.class.php');
22
23    class HTML2PDF
24    {
25        /**
26         * HTML2PDF_myPdf object, extends from TCPDF
27         * @var HTML2PDF_myPdf
28         */
29        public $pdf = null;
30
31        /**
32         * CSS parsing
33         * @var HTML2PDF_parsingCss
34         */
35        public $parsingCss = null;
36
37        /**
38         * HTML parsing
39         * @var HTML2PDF_parsingHtml
40         */
41        public $parsingHtml = null;
42
43        protected $_langue           = 'fr';        // locale of the messages
44        protected $_orientation      = 'P';         // page orientation : Portrait ou Landscape
45        protected $_format           = 'A4';        // page format : A4, A3, ...
46        protected $_encoding         = '';          // charset encoding
47        protected $_unicode          = true;        // means that the input text is unicode (default = true)
48
49        protected $_testTdInOnepage  = true;        // test of TD that can not take more than one page
50        protected $_testIsImage      = true;        // test if the images exist or not
51        protected $_testIsDeprecated = false;       // test the deprecated functions
52
53        protected $_parsePos         = 0;           // position in the parsing
54        protected $_tempPos          = 0;           // temporary position for complex table
55        protected $_page             = 0;           // current page number
56
57        protected $_subHtml          = null;        // sub html
58        protected $_subPart          = false;       // sub HTML2PDF
59        protected $_subHEADER        = array();     // sub action to make the header
60        protected $_subFOOTER        = array();     // sub action to make the footer
61        protected $_subSTATES        = array();     // array to save some parameters
62
63        protected $_isSubPart        = false;       // flag : in a sub html2pdf
64        protected $_isInThead        = false;       // flag : in a thead
65        protected $_isInTfoot        = false;       // flag : in a tfoot
66        protected $_isInOverflow     = false;       // flag : in a overflow
67        protected $_isInFooter       = false;       // flag : in a footer
68        protected $_isInDraw         = null;        // flag : in a draw (svg)
69        protected $_isAfterFloat     = false;       // flag : is just after a float
70        protected $_isInForm         = false;       // flag : is in a float. false / action of the form
71        protected $_isInLink         = '';          // flag : is in a link. empty / href of the link
72        protected $_isInParagraph    = false;       // flag : is in a paragraph
73        protected $_isForOneLine     = false;       // flag : in a specific sub html2pdf to have the height of the next line
74
75        protected $_maxX             = 0;           // maximum X of the current zone
76        protected $_maxY             = 0;           // maximum Y of the current zone
77        protected $_maxE             = 0;           // number of elements in the current zone
78        protected $_maxH             = 0;           // maximum height of the line in the current zone
79        protected $_maxSave          = array();     // save the maximums of the current zone
80        protected $_currentH         = 0;           // height of the current line
81
82        protected $_defaultLeft      = 0;           // default marges of the page
83        protected $_defaultTop       = 0;
84        protected $_defaultRight     = 0;
85        protected $_defaultBottom    = 0;
86        protected $_defaultFont      = null;        // default font to use, is the asked font does not exist
87
88        protected $_margeLeft        = 0;           // current marges of the page
89        protected $_margeTop         = 0;
90        protected $_margeRight       = 0;
91        protected $_margeBottom      = 0;
92        protected $_marges           = array();     // save the different marges of the current page
93        protected $_pageMarges       = array();     // float marges of the current page
94        protected $_background       = array();     // background informations
95
96        protected $_hideHeader       = array();     // array : list of pages which the header gonna be hidden
97        protected $_firstPage        = true;        // flag : first page
98        protected $_defList          = array();     // table to save the stats of the tags UL and OL
99
100        protected $_lstAnchor        = array();     // list of the anchors
101        protected $_lstField         = array();     // list of the fields
102        protected $_lstSelect        = array();     // list of the options of the current select
103        protected $_previousCall     = null;        // last action called
104
105        protected $_debugActif       = false;       // flag : mode debug is active
106        protected $_debugOkUsage     = false;       // flag : the function memory_get_usage exist
107        protected $_debugOkPeak      = false;       // flag : the function memory_get_peak_usage exist
108        protected $_debugLevel       = 0;           // level in the debug
109        protected $_debugStartTime   = 0;           // debug start time
110        protected $_debugLastTime    = 0;           // debug stop time
111
112        static protected $_subobj    = null;        // object html2pdf prepared in order to accelerate the creation of sub html2pdf
113        static protected $_tables    = array();     // static table to prepare the nested html tables
114
115        /**
116         * class constructor
117         *
118         * @access public
119         * @param  string   $orientation page orientation, same as TCPDF
120         * @param  mixed    $format      The format used for pages, same as TCPDF
121         * @param  $tring   $langue      Langue : fr, en, it...
122         * @param  boolean  $unicode     TRUE means that the input text is unicode (default = true)
123         * @param  String   $encoding    charset encoding; default is UTF-8
124         * @param  array    $marges      Default marges (left, top, right, bottom)
125         * @return HTML2PDF $this
126         */
127        public function __construct($orientation = 'P', $format = 'A4', $langue='fr', $unicode=true, $encoding='UTF-8', $marges = array(5, 5, 5, 8))
128        {
129            // init the page number
130            $this->_page         = 0;
131            $this->_firstPage    = true;
132
133            // save the parameters
134            $this->_orientation  = $orientation;
135            $this->_format       = $format;
136            $this->_langue       = strtolower($langue);
137            $this->_unicode      = $unicode;
138            $this->_encoding     = $encoding;
139
140            // load the Local
141            HTML2PDF_locale::load($this->_langue);
142
143            // create the  HTML2PDF_myPdf object
144            $this->pdf = new HTML2PDF_myPdf($orientation, 'mm', $format, $unicode, $encoding);
145
146            // init the CSS parsing object
147            $this->parsingCss = new HTML2PDF_parsingCss($this->pdf);
148            $this->parsingCss->fontSet();
149            $this->_defList = array();
150
151            // init some tests
152            $this->setTestTdInOnePage(true);
153            $this->setTestIsImage(true);
154            $this->setTestIsDeprecated(true);
155
156            // init the default font
157            $this->setDefaultFont(null);
158
159            // init the HTML parsing object
160            $this->parsingHtml = new HTML2PDF_parsingHtml($this->_encoding);
161            $this->_subHtml = null;
162            $this->_subPart = false;
163
164            // init the marges of the page
165            if (!is_array($marges)) $marges = array($marges, $marges, $marges, $marges);
166            $this->_setDefaultMargins($marges[0], $marges[1], $marges[2], $marges[3]);
167            $this->_setMargins();
168            $this->_marges = array();
169
170            // init the form's fields
171            $this->_lstField = array();
172
173            return $this;
174        }
175
176        /**
177         * Destructor
178         *
179         * @access public
180         * @return null
181         */
182        public function __destruct()
183        {
184
185        }
186
187        /**
188         * Clone to create a sub HTML2PDF from HTML2PDF::$_subobj
189         *
190         * @access public
191         */
192        public function __clone()
193        {
194            $this->pdf = clone $this->pdf;
195            $this->parsingHtml = clone $this->parsingHtml;
196            $this->parsingCss = clone $this->parsingCss;
197            $this->parsingCss->setPdfParent($this->pdf);
198        }
199
200        /**
201         * set the debug mode to On
202         *
203         * @access public
204         * @return HTML2PDF $this
205         */
206        public function setModeDebug()
207        {
208            $time = microtime(true);
209
210            $this->_debugActif     = true;
211            $this->_debugOkUsage   = function_exists('memory_get_usage');
212            $this->_debugOkPeak    = function_exists('memory_get_peak_usage');
213            $this->_debugStartTime = $time;
214            $this->_debugLastTime  = $time;
215
216            $this->_DEBUG_stepline('step', 'time', 'delta', 'memory', 'peak');
217            $this->_DEBUG_add('Init debug');
218
219            return $this;
220        }
221
222        /**
223         * Set the test of TD thdat can not take more than one page
224         *
225         * @access public
226         * @param  boolean  $mode
227         * @return HTML2PDF $this
228         */
229        public function setTestTdInOnePage($mode = true)
230        {
231            $this->_testTdInOnepage = $mode ? true : false;
232
233            return $this;
234        }
235
236        /**
237         * Set the test if the images exist or not
238         *
239         * @access public
240         * @param  boolean  $mode
241         * @return HTML2PDF $this
242         */
243        public function setTestIsImage($mode = true)
244        {
245            $this->_testIsImage = $mode ? true : false;
246
247            return $this;
248        }
249
250        /**
251         * Set the test on deprecated functions
252         *
253         * @access public
254         * @param  boolean  $mode
255         * @return HTML2PDF $this
256         */
257        public function setTestIsDeprecated($mode = true)
258        {
259            $this->_testIsDeprecated = $mode ? true : false;
260
261            return $this;
262        }
263
264        /**
265         * Set the default font to use, if no font is specify, or if the asked font does not exist
266         *
267         * @access public
268         * @param  string   $default name of the default font to use. If null : Arial is no font is specify, and error if the asked font does not exist
269         * @return HTML2PDF $this
270         */
271        public function setDefaultFont($default = null)
272        {
273            $this->_defaultFont = $default;
274            $this->parsingCss->setDefaultFont($default);
275
276            return $this;
277        }
278
279        /**
280         * add a font, see TCPDF function addFont
281         *
282         * @access public
283         * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
284         * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
285         * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
286         * @return HTML2PDF $this
287         * @see TCPDF::addFont
288         */
289        public function addFont($family, $style='', $file='')
290        {
291            $this->pdf->AddFont($family, $style, $file);
292
293            return $this;
294        }
295
296        /**
297         * display a automatic index, from the bookmarks
298         *
299         * @access public
300         * @param  string  $titre         index title
301         * @param  int     $sizeTitle     font size of the index title, in mm
302         * @param  int     $sizeBookmark  font size of the index, in mm
303         * @param  boolean $bookmarkTitle add a bookmark for the index, at his beginning
304         * @param  boolean $displayPage   display the page numbers
305         * @param  int     $onPage        if null : at the end of the document on a new page, else on the $onPage page
306         * @param  string  $fontName      font name to use
307         * @return null
308         */
309        public function createIndex($titre = 'Index', $sizeTitle = 20, $sizeBookmark = 15, $bookmarkTitle = true, $displayPage = true, $onPage = null, $fontName = 'helvetica')
310        {
311            $oldPage = $this->_INDEX_NewPage($onPage);
312            $this->pdf->createIndex($this, $titre, $sizeTitle, $sizeBookmark, $bookmarkTitle, $displayPage, $onPage, $fontName);
313            if ($oldPage) $this->pdf->setPage($oldPage);
314        }
315
316        /**
317         * clean up the objects
318         *
319         * @access protected
320         */
321        protected function _cleanUp()
322        {
323            HTML2PDF::$_subobj = null;
324            HTML2PDF::$_tables = array();
325        }
326
327        /**
328         * Send the document to a given destination: string, local file or browser.
329         * Dest can be :
330         *  I : send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.
331         *  D : send to the browser and force a file download with the name given by name.
332         *  F : save to a local server file with the name given by name.
333         *  S : return the document as a string. name is ignored.
334         *  FI: equivalent to F + I option
335         *  FD: equivalent to F + D option
336         *  true  => I
337         *  false => S
338         *
339         * @param  string $name The name of the file when saved.
340         * @param  string $dest Destination where to send the document.
341         * @return string content of the PDF, if $dest=S
342         * @see TCPDF::close
343         * @access public
344
345         */
346        public function Output($name = '', $dest = false)
347        {
348            // close the pdf and clean up
349            $this->_cleanUp();
350
351            // if on debug mode
352            if ($this->_debugActif) {
353                $this->_DEBUG_add('Before output');
354                $this->pdf->Close();
355                exit;
356            }
357
358            // complete parameters
359            if ($dest===false) $dest = 'I';
360            if ($dest===true)  $dest = 'S';
361            if ($dest==='')    $dest = 'I';
362            if ($name=='')     $name='document.pdf';
363
364            // clean up the destination
365            $dest = strtoupper($dest);
366            if (!in_array($dest, array('I', 'D', 'F', 'S', 'FI','FD'))) $dest = 'I';
367
368            // the name must be a PDF name
369            if (strtolower(substr($name, -4))!='.pdf') {
370                throw new HTML2PDF_exception(0, 'The output document name "'.$name.'" is not a PDF name');
371            }
372
373            // call the output of TCPDF
374            return $this->pdf->Output($name, $dest);
375        }
376
377        /**
378         * convert HTML to PDF
379         *
380         * @access public
381         * @param  string   $html
382         * @param  boolean  $debugVue  enable the HTML debug vue
383         * @return null
384         */
385        public function writeHTML($html, $debugVue = false)
386        {
387            // if it is a real html page, we have to convert it
388            if (preg_match('/<body/isU', $html))
389                $html = $this->getHtmlFromPage($html);
390
391            $html = str_replace('[[date_y]]', date('Y'), $html);
392            $html = str_replace('[[date_m]]', date('m'), $html);
393            $html = str_replace('[[date_d]]', date('d'), $html);
394
395            $html = str_replace('[[date_h]]', date('H'), $html);
396            $html = str_replace('[[date_i]]', date('i'), $html);
397            $html = str_replace('[[date_s]]', date('s'), $html);
398
399            // If we are in HTML debug vue : display the HTML
400            if ($debugVue) {
401                return $this->_vueHTML($html);
402            }
403
404            // convert HTMl to PDF
405            $this->parsingCss->readStyle($html);
406            $this->parsingHtml->setHTML($html);
407            $this->parsingHtml->parse();
408            $this->_makeHTMLcode();
409        }
410
411        /**
412         * convert the HTML of a real page, to a code adapted to HTML2PDF
413         *
414         * @access public
415         * @param  string HTML of a real page
416         * @return string HTML adapted to HTML2PDF
417         */
418        public function getHtmlFromPage($html)
419        {
420            $html = str_replace('<BODY', '<body', $html);
421            $html = str_replace('</BODY', '</body', $html);
422
423            // extract the content
424            $res = explode('<body', $html);
425            if (count($res)<2) return $html;
426            $content = '<page'.$res[1];
427            $content = explode('</body', $content);
428            $content = $content[0].'</page>';
429
430            // extract the link tags
431            preg_match_all('/<link([^>]*)>/isU', $html, $match);
432            foreach ($match[0] as $src)
433                $content = $src.'</link>'.$content;
434
435            // extract the css style tags
436            preg_match_all('/<style[^>]*>(.*)<\/style[^>]*>/isU', $html, $match);
437            foreach ($match[0] as $src)
438                $content = $src.$content;
439
440            return $content;
441        }
442
443        /**
444         * init a sub HTML2PDF. does not use it directly. Only the method createSubHTML must use it
445         *
446         * @access public
447         * @param  string  $format
448         * @param  string  $orientation
449         * @param  array   $marge
450         * @param  integer $page
451         * @param  array   $defLIST
452         * @param  integer $myLastPageGroup
453         * @param  integer $myLastPageGroupNb
454         */
455        public function initSubHtml($format, $orientation, $marge, $page, $defLIST, $myLastPageGroup, $myLastPageGroupNb)
456        {
457            $this->_isSubPart = true;
458
459            $this->parsingCss->setOnlyLeft();
460
461            $this->_setNewPage($format, $orientation, null, null, ($myLastPageGroup!==null));
462
463            $this->_saveMargin(0, 0, $marge);
464            $this->_defList = $defLIST;
465
466            $this->_page = $page;
467            $this->pdf->setMyLastPageGroup($myLastPageGroup);
468            $this->pdf->setMyLastPageGroupNb($myLastPageGroupNb);
469            $this->pdf->setXY(0, 0);
470            $this->parsingCss->fontSet();
471        }
472
473        /**
474         * display the content in HTML moden for debug
475         *
476         * @access protected
477         * @param  string $contenu
478         */
479        protected function _vueHTML($content)
480        {
481            $content = preg_replace('/<page_header([^>]*)>/isU', '<hr>'.HTML2PDF_locale::get('vue01').' : $1<hr><div$1>', $content);
482            $content = preg_replace('/<page_footer([^>]*)>/isU', '<hr>'.HTML2PDF_locale::get('vue02').' : $1<hr><div$1>', $content);
483            $content = preg_replace('/<page([^>]*)>/isU', '<hr>'.HTML2PDF_locale::get('vue03').' : $1<hr><div$1>', $content);
484            $content = preg_replace('/<\/page([^>]*)>/isU', '</div><hr>', $content);
485            $content = preg_replace('/<bookmark([^>]*)>/isU', '<hr>bookmark : $1<hr>', $content);
486            $content = preg_replace('/<\/bookmark([^>]*)>/isU', '', $content);
487            $content = preg_replace('/<barcode([^>]*)>/isU', '<hr>barcode : $1<hr>', $content);
488            $content = preg_replace('/<\/barcode([^>]*)>/isU', '', $content);
489            $content = preg_replace('/<qrcode([^>]*)>/isU', '<hr>qrcode : $1<hr>', $content);
490            $content = preg_replace('/<\/qrcode([^>]*)>/isU', '', $content);
491
492            echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
493<html>
494    <head>
495        <title>'.HTML2PDF_locale::get('vue04').' HTML</title>
496        <meta http-equiv="Content-Type" content="text/html; charset='.$this->_encoding.'" >
497    </head>
498    <body style="padding: 10px; font-size: 10pt;font-family:    Verdana;">
499'.$content.'
500    </body>
501</html>';
502            exit;
503        }
504
505        /**
506         * set the default margins of the page
507         *
508         * @access protected
509         * @param  int $left   (mm, left margin)
510         * @param  int $top    (mm, top margin)
511         * @param  int $right  (mm, right margin, if null => left=right)
512         * @param  int $bottom (mm, bottom margin, if null => bottom=8mm)
513         */
514        protected function _setDefaultMargins($left, $top, $right = null, $bottom = null)
515        {
516            if ($right===null)  $right = $left;
517            if ($bottom===null) $bottom = 8;
518
519            $this->_defaultLeft   = $this->parsingCss->ConvertToMM($left.'mm');
520            $this->_defaultTop    = $this->parsingCss->ConvertToMM($top.'mm');
521            $this->_defaultRight  = $this->parsingCss->ConvertToMM($right.'mm');
522            $this->_defaultBottom = $this->parsingCss->ConvertToMM($bottom.'mm');
523        }
524
525        /**
526         * create a new page
527         *
528         * @access protected
529         * @param  mixed   $format
530         * @param  string  $orientation
531         * @param  array   $background background information
532         * @param  integer $curr real position in the html parseur (if break line in the write of a text)
533         * @param  boolean $resetPageNumber
534         */
535        protected function _setNewPage($format = null, $orientation = '', $background = null, $curr = null, $resetPageNumber=false)
536        {
537            $this->_firstPage = false;
538
539            $this->_format = $format ? $format : $this->_format;
540            $this->_orientation = $orientation ? $orientation : $this->_orientation;
541            $this->_background = $background!==null ? $background : $this->_background;
542            $this->_maxY = 0;
543            $this->_maxX = 0;
544            $this->_maxH = 0;
545            $this->_maxE = 0;
546
547            $this->pdf->SetMargins($this->_defaultLeft, $this->_defaultTop, $this->_defaultRight);
548
549            if ($resetPageNumber) {
550                $this->pdf->startPageGroup();
551            }
552
553            $this->pdf->AddPage($this->_orientation, $this->_format);
554
555            if ($resetPageNumber) {
556                $this->pdf->myStartPageGroup();
557            }
558
559            $this->_page++;
560
561            if (!$this->_subPart && !$this->_isSubPart) {
562                if (is_array($this->_background)) {
563                    if (isset($this->_background['color']) && $this->_background['color']) {
564                        $this->pdf->setFillColorArray($this->_background['color']);
565                        $this->pdf->Rect(0, 0, $this->pdf->getW(), $this->pdf->getH(), 'F');
566                    }
567
568                    if (isset($this->_background['img']) && $this->_background['img'])
569                        $this->pdf->Image($this->_background['img'], $this->_background['posX'], $this->_background['posY'], $this->_background['width']);
570                }
571
572                $this->_setPageHeader();
573                $this->_setPageFooter();
574            }
575
576            $this->_setMargins();
577            $this->pdf->setY($this->_margeTop);
578
579            $this->_setNewPositionForNewLine($curr);
580            $this->_maxH = 0;
581        }
582
583        /**
584         * set the real margin, using the default margins and the page margins
585         *
586         * @access protected
587         */
588        protected function _setMargins()
589        {
590            // prepare the margins
591            $this->_margeLeft   = $this->_defaultLeft   + (isset($this->_background['left'])   ? $this->_background['left']   : 0);
592            $this->_margeRight  = $this->_defaultRight  + (isset($this->_background['right'])  ? $this->_background['right']  : 0);
593            $this->_margeTop    = $this->_defaultTop    + (isset($this->_background['top'])    ? $this->_background['top']    : 0);
594            $this->_margeBottom = $this->_defaultBottom + (isset($this->_background['bottom']) ? $this->_background['bottom'] : 0);
595
596            // set the PDF margins
597            $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
598            $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
599
600            // set the float Margins
601            $this->_pageMarges = array();
602            if ($this->_isInParagraph!==false) {
603                $this->_pageMarges[floor($this->_margeTop*100)] = array($this->_isInParagraph[0], $this->pdf->getW()-$this->_isInParagraph[1]);
604            } else {
605                $this->_pageMarges[floor($this->_margeTop*100)] = array($this->_margeLeft, $this->pdf->getW()-$this->_margeRight);
606            }
607        }
608
609        /**
610         * add a debug step
611         *
612         * @access protected
613         * @param  string  $name step name
614         * @param  boolean $level (true=up, false=down, null=nothing to do)
615         * @return $this
616         */
617        protected function _DEBUG_add($name, $level=null)
618        {
619            // if true : UP
620            if ($level===true) $this->_debugLevel++;
621
622            $name   = str_repeat('  ', $this->_debugLevel). $name.($level===true ? ' Begin' : ($level===false ? ' End' : ''));
623            $time  = microtime(true);
624            $usage = ($this->_debugOkUsage ? memory_get_usage() : 0);
625            $peak  = ($this->_debugOkPeak ? memory_get_peak_usage() : 0);
626
627            $this->_DEBUG_stepline(
628                $name,
629                number_format(($time - $this->_debugStartTime)*1000, 1, '.', ' ').' ms',
630                number_format(($time - $this->_debugLastTime)*1000, 1, '.', ' ').' ms',
631                number_format($usage/1024, 1, '.', ' ').' Ko',
632                number_format($peak/1024, 1, '.', ' ').' Ko'
633            );
634
635            $this->_debugLastTime = $time;
636
637            // it false : DOWN
638            if ($level===false) $this->_debugLevel--;
639
640            return $this;
641        }
642
643        /**
644         * display a debug line
645         *
646         *
647         * @access protected
648         * @param  string $name
649         * @param  string $timeTotal
650         * @param  string $timeStep
651         * @param  string $memoryUsage
652         * @param  string $memoryPeak
653         */
654        protected function _DEBUG_stepline($name, $timeTotal, $timeStep, $memoryUsage, $memoryPeak)
655        {
656            $txt = str_pad($name, 30, ' ', STR_PAD_RIGHT).
657                    str_pad($timeTotal, 12, ' ', STR_PAD_LEFT).
658                    str_pad($timeStep, 12, ' ', STR_PAD_LEFT).
659                    str_pad($memoryUsage, 15, ' ', STR_PAD_LEFT).
660                    str_pad($memoryPeak, 15, ' ', STR_PAD_LEFT);
661
662            echo '<pre style="padding:0; margin:0">'.$txt.'</pre>';
663        }
664
665        /**
666         * get the Min and Max X, for Y (use the float margins)
667         *
668         * @access protected
669         * @param  float $y
670         * @return array(float, float)
671         */
672        protected function _getMargins($y)
673        {
674            $y = floor($y*100);
675            $x = array($this->pdf->getlMargin(), $this->pdf->getW()-$this->pdf->getrMargin());
676
677            foreach ($this->_pageMarges as $mY => $mX)
678                if ($mY<=$y) $x = $mX;
679
680            return $x;
681        }
682
683        /**
684         * Add margins, for a float
685         *
686         * @access protected
687         * @param  string $float (left / right)
688         * @param  float  $xLeft
689         * @param  float  $yTop
690         * @param  float  $xRight
691         * @param  float  $yBottom
692         */
693        protected function _addMargins($float, $xLeft, $yTop, $xRight, $yBottom)
694        {
695            // get the current float margins, for top and bottom
696            $oldTop    = $this->_getMargins($yTop);
697            $oldBottom = $this->_getMargins($yBottom);
698
699            // update the top float margin
700            if ($float=='left'  && $oldTop[0]<$xRight) $oldTop[0] = $xRight;
701            if ($float=='right' && $oldTop[1]>$xLeft)  $oldTop[1] = $xLeft;
702
703            $yTop = floor($yTop*100);
704            $yBottom = floor($yBottom*100);
705
706            // erase all the float margins that are smaller than the new one
707            foreach ($this->_pageMarges as $mY => $mX) {
708                if ($mY<$yTop) continue;
709                if ($mY>$yBottom) break;
710                if ($float=='left' && $this->_pageMarges[$mY][0]<$xRight)  unset($this->_pageMarges[$mY]);
711                if ($float=='right' && $this->_pageMarges[$mY][1]>$xLeft) unset($this->_pageMarges[$mY]);
712            }
713
714            // save the new Top and Bottom margins
715            $this->_pageMarges[$yTop] = $oldTop;
716            $this->_pageMarges[$yBottom] = $oldBottom;
717
718            // sort the margins
719            ksort($this->_pageMarges);
720
721            // we are just after float
722            $this->_isAfterFloat = true;
723        }
724
725        /**
726         * Save old margins (push), and set new ones
727         *
728         * @access protected
729         * @param  float  $ml left margin
730         * @param  float  $mt top margin
731         * @param  float  $mr right margin
732         */
733        protected function _saveMargin($ml, $mt, $mr)
734        {
735            // save old margins
736            $this->_marges[] = array('l' => $this->pdf->getlMargin(), 't' => $this->pdf->gettMargin(), 'r' => $this->pdf->getrMargin(), 'page' => $this->_pageMarges);
737
738            // set new ones
739            $this->pdf->SetMargins($ml, $mt, $mr);
740
741            // prepare for float margins
742            $this->_pageMarges = array();
743            $this->_pageMarges[floor($mt*100)] = array($ml, $this->pdf->getW()-$mr);
744        }
745
746        /**
747         * load the last saved margins (pop)
748         *
749         * @access protected
750         */
751        protected function _loadMargin()
752        {
753            $old = array_pop($this->_marges);
754            if ($old) {
755                $ml = $old['l'];
756                $mt = $old['t'];
757                $mr = $old['r'];
758                $mP = $old['page'];
759            } else {
760                $ml = $this->_margeLeft;
761                $mt = 0;
762                $mr = $this->_margeRight;
763                $mP = array($mt => array($ml, $this->pdf->getW()-$mr));
764            }
765
766            $this->pdf->SetMargins($ml, $mt, $mr);
767            $this->_pageMarges = $mP;
768        }
769
770        /**
771         * save the current maxs (push)
772         *
773         * @access protected
774         */
775        protected function _saveMax()
776        {
777            $this->_maxSave[] = array($this->_maxX, $this->_maxY, $this->_maxH, $this->_maxE);
778        }
779
780        /**
781         * load the last saved current maxs (pop)
782         *
783         * @access protected
784         */
785        protected function _loadMax()
786        {
787            $old = array_pop($this->_maxSave);
788
789            if ($old) {
790                $this->_maxX = $old[0];
791                $this->_maxY = $old[1];
792                $this->_maxH = $old[2];
793                $this->_maxE = $old[3];
794            } else {
795                $this->_maxX = 0;
796                $this->_maxY = 0;
797                $this->_maxH = 0;
798                $this->_maxE = 0;
799            }
800        }
801
802        /**
803         * draw the PDF header with the HTML in page_header
804         *
805         * @access protected
806         */
807        protected function _setPageHeader()
808        {
809            if (!count($this->_subHEADER)) return false;
810           
811            if (in_array($this->pdf->getPage(), $this->_hideHeader)) return false;
812
813            $oldParsePos = $this->_parsePos;
814            $oldParseCode = $this->parsingHtml->code;
815
816            $this->_parsePos = 0;
817            $this->parsingHtml->code = $this->_subHEADER;
818            $this->_makeHTMLcode();
819
820            $this->_parsePos = $oldParsePos;
821            $this->parsingHtml->code = $oldParseCode;
822        }
823
824        /**
825         * draw the PDF footer with the HTML in page_footer
826         *
827         * @access protected
828         */
829        protected function _setPageFooter()
830        {
831            if (!count($this->_subFOOTER)) return false;
832
833            $oldParsePos = $this->_parsePos;
834            $oldParseCode = $this->parsingHtml->code;
835
836            $this->_parsePos = 0;
837            $this->parsingHtml->code = $this->_subFOOTER;
838            $this->_isInFooter = true;
839            $this->_makeHTMLcode();
840            $this->_isInFooter = false;
841
842            $this->_parsePos = $oldParsePos;
843            $this->parsingHtml->code = $oldParseCode;
844        }
845
846        /**
847         * new line, with a specific height
848         *
849         * @access protected
850         * @param float   $h
851         * @param integer $curr real current position in the text, if new line in the write of a text
852         */
853        protected function _setNewLine($h, $curr = null)
854        {
855            $this->pdf->Ln($h);
856            $this->_setNewPositionForNewLine($curr);
857        }
858
859        /**
860         * calculate the start position of the next line,  depending on the text-align
861         *
862         * @access protected
863         * @param  integer $curr real current position in the text, if new line in the write of a text
864         */
865        protected function _setNewPositionForNewLine($curr = null)
866        {
867            // get the margins for the current line
868            list($lx, $rx) = $this->_getMargins($this->pdf->getY());
869            $this->pdf->setX($lx);
870            $wMax = $rx-$lx;
871            $this->_currentH = 0;
872
873            // if subPart => return because align left
874            if ($this->_subPart || $this->_isSubPart || $this->_isForOneLine) {
875                $this->pdf->setWordSpacing(0);
876                return null;
877            }
878
879            // create the sub object
880            $sub = null;
881            $this->_createSubHTML($sub);
882            $sub->_saveMargin(0, 0, $sub->pdf->getW()-$wMax);
883            $sub->_isForOneLine = true;
884            $sub->_parsePos = $this->_parsePos;
885            $sub->parsingHtml->code = $this->parsingHtml->code;
886
887            // if $curr => adapt the current position of the parsing
888            if ($curr!==null && $sub->parsingHtml->code[$this->_parsePos]['name']=='write') {
889                $txt = $sub->parsingHtml->code[$this->_parsePos]['param']['txt'];
890                $txt = str_replace('[[page_cu]]', $sub->pdf->getMyNumPage($this->_page), $txt);
891                $sub->parsingHtml->code[$this->_parsePos]['param']['txt'] = substr($txt, $curr+1);
892            } else
893                $sub->_parsePos++;
894
895            // for each element of the parsing => load the action
896            $res = null;
897            for ($sub->_parsePos; $sub->_parsePos<count($sub->parsingHtml->code); $sub->_parsePos++) {
898                $action = $sub->parsingHtml->code[$sub->_parsePos];
899                $res = $sub->_executeAction($action);
900                if (!$res) break;
901            }
902
903            $w = $sub->_maxX; // max width
904            $h = $sub->_maxH; // max height
905            $e = ($res===null ? $sub->_maxE : 0); // maxnumber of elemets on the line
906
907            // destroy the sub HTML
908            $this->_destroySubHTML($sub);
909
910            // adapt the start of the line, depending on the text-align
911            if ($this->parsingCss->value['text-align']=='center')
912                $this->pdf->setX(($rx+$this->pdf->getX()-$w)*0.5-0.01);
913            else if ($this->parsingCss->value['text-align']=='right')
914                $this->pdf->setX($rx-$w-0.01);
915            else
916                $this->pdf->setX($lx);
917
918            // set the height of the line
919            $this->_currentH = $h;
920
921            // if justify => set the word spacing
922            if ($this->parsingCss->value['text-align']=='justify' && $e>1) {
923                $this->pdf->setWordSpacing(($wMax-$w)/($e-1));
924            } else {
925                $this->pdf->setWordSpacing(0);
926            }
927        }
928
929        /**
930         * prepare HTML2PDF::$_subobj (used for create the sub HTML2PDF objects
931         *
932         * @access protected
933         */
934        protected function _prepareSubObj()
935        {
936            $pdf = null;
937
938            // create the sub object
939            HTML2PDF::$_subobj = new HTML2PDF(
940                $this->_orientation,
941                $this->_format,
942                $this->_langue,
943                $this->_unicode,
944                $this->_encoding,
945                array($this->_defaultLeft,$this->_defaultTop,$this->_defaultRight,$this->_defaultBottom)
946            );
947
948            // init
949            HTML2PDF::$_subobj->setTestTdInOnePage($this->_testTdInOnepage);
950            HTML2PDF::$_subobj->setTestIsImage($this->_testIsImage);
951            HTML2PDF::$_subobj->setTestIsDeprecated($this->_testIsDeprecated);
952            HTML2PDF::$_subobj->setDefaultFont($this->_defaultFont);
953            HTML2PDF::$_subobj->parsingCss->css            = &$this->parsingCss->css;
954            HTML2PDF::$_subobj->parsingCss->cssKeys        = &$this->parsingCss->cssKeys;
955
956            // clone font from the original PDF
957            HTML2PDF::$_subobj->pdf->cloneFontFrom($this->pdf);
958
959            // remove the link to the parent
960            HTML2PDF::$_subobj->parsingCss->setPdfParent($pdf);
961        }
962
963        /**
964         * create a sub HTML2PDF, to calculate the multi-tables
965         *
966         * @access protected
967         * @param  &HTML2PDF $subHtml sub HTML2PDF to create
968         * @param  integer   $cellmargin if in a TD : cellmargin of this td
969         */
970        protected function _createSubHTML(&$subHtml, $cellmargin=0)
971        {
972            // prepare the subObject, if never prepare before
973            if (HTML2PDF::$_subobj===null) {
974                $this->_prepareSubObj();
975            }
976
977            // calculate the width to use
978            if ($this->parsingCss->value['width']) {
979                $marge = $cellmargin*2;
980                $marge+= $this->parsingCss->value['padding']['l'] + $this->parsingCss->value['padding']['r'];
981                $marge+= $this->parsingCss->value['border']['l']['width'] + $this->parsingCss->value['border']['r']['width'];
982                $marge = $this->pdf->getW() - $this->parsingCss->value['width'] + $marge;
983            } else {
984                $marge = $this->_margeLeft+$this->_margeRight;
985            }
986
987            // BUGFIX : we have to call the method, because of a bug in php 5.1.6
988            HTML2PDF::$_subobj->pdf->getPage();
989
990            // clone the sub oject
991            $subHtml = clone HTML2PDF::$_subobj;
992            $subHtml->parsingCss->table = $this->parsingCss->table;
993            $subHtml->parsingCss->value = $this->parsingCss->value;
994            $subHtml->initSubHtml(
995                $this->_format,
996                $this->_orientation,
997                $marge,
998                $this->_page,
999                $this->_defList,
1000                $this->pdf->getMyLastPageGroup(),
1001                $this->pdf->getMyLastPageGroupNb()
1002            );
1003        }
1004
1005        /**
1006         * destroy a subHTML2PDF
1007         *
1008         * @access protected
1009         */
1010        protected function _destroySubHTML(&$subHtml)
1011        {
1012            unset($subHtml);
1013            $subHtml = null;
1014        }
1015
1016        /**
1017         * Convert a arabic number in roman number
1018         *
1019         * @access protected
1020         * @param  integer $nbArabic
1021         * @return string  $nbRoman
1022         */
1023        protected function _listeArab2Rom($nbArabic)
1024        {
1025            $nbBaseTen    = array('I','X','C','M');
1026            $nbBaseFive    = array('V','L','D');
1027            $nbRoman    = '';
1028
1029            if ($nbArabic<1)    return $nbArabic;
1030            if ($nbArabic>3999) return $nbArabic;
1031
1032            for ($i=3; $i>=0 ; $i--) {
1033                $chiffre=floor($nbArabic/pow(10, $i));
1034                if ($chiffre>=1) {
1035                    $nbArabic=$nbArabic-$chiffre*pow(10, $i);
1036                    if ($chiffre<=3) {
1037                        for ($j=$chiffre; $j>=1; $j--) {
1038                            $nbRoman=$nbRoman.$nbBaseTen[$i];
1039                        }
1040                    } else if ($chiffre==9) {
1041                        $nbRoman=$nbRoman.$nbBaseTen[$i].$nbBaseTen[$i+1];
1042                    } else if ($chiffre==4) {
1043                    $nbRoman=$nbRoman.$nbBaseTen[$i].$nbBaseFive[$i];
1044                    } else {
1045                        $nbRoman=$nbRoman.$nbBaseFive[$i];
1046                        for ($j=$chiffre-5; $j>=1; $j--) {
1047                            $nbRoman=$nbRoman.$nbBaseTen[$i];
1048                        }
1049                    }
1050                }
1051            }
1052            return $nbRoman;
1053        }
1054
1055        /**
1056         * add a LI to the current level
1057         *
1058         * @access protected
1059         */
1060        protected function _listeAddLi()
1061        {
1062            $this->_defList[count($this->_defList)-1]['nb']++;
1063        }
1064
1065        /**
1066         * get the width to use for the column of the list
1067         *
1068         * @access protected
1069         * @return string $width
1070         */
1071        protected function _listeGetWidth()
1072        {
1073            return '7mm';
1074        }
1075
1076        /**
1077         * get the padding to use for the column of the list
1078         *
1079         * @access protected
1080         * @return string $padding
1081         */
1082        protected function _listeGetPadding()
1083        {
1084            return '1mm';
1085        }
1086
1087        /**
1088         * get the information of the li on the current level
1089         *
1090         * @access protected
1091         * @return array(fontName, small size, string)
1092         */
1093        protected function _listeGetLi()
1094        {
1095            $im = $this->_defList[count($this->_defList)-1]['img'];
1096            $st = $this->_defList[count($this->_defList)-1]['style'];
1097            $nb = $this->_defList[count($this->_defList)-1]['nb'];
1098            $up = (substr($st, 0, 6)=='upper-');
1099
1100            if ($im) return array(false, false, $im);
1101
1102            switch($st)
1103            {
1104                case 'none':
1105                    return array('helvetica', true, ' ');
1106
1107                case 'upper-alpha':
1108                case 'lower-alpha':
1109                    $str = '';
1110                    while ($nb>26) {
1111                        $str = chr(96+$nb%26).$str;
1112                        $nb = floor($nb/26);
1113                    }
1114                    $str = chr(96+$nb).$str;
1115
1116                    return array('helvetica', false, ($up ? strtoupper($str) : $str).'.');
1117
1118                case 'upper-roman':
1119                case 'lower-roman':
1120                    $str = $this->_listeArab2Rom($nb);
1121
1122                    return array('helvetica', false, ($up ? strtoupper($str) : $str).'.');
1123
1124                case 'decimal':
1125                    return array('helvetica', false, $nb.'.');
1126
1127                case 'square':
1128                    return array('zapfdingbats', true, chr(110));
1129
1130                case 'circle':
1131                    return array('zapfdingbats', true, chr(109));
1132
1133                case 'disc':
1134                default:
1135                    return array('zapfdingbats', true, chr(108));
1136            }
1137        }
1138
1139        /**
1140         * add a level to the list
1141         *
1142         * @access protected
1143         * @param  string $type  : ul, ol
1144         * @param  string $style : lower-alpha, ...
1145         * @param  string $img
1146         */
1147        protected function _listeAddLevel($type = 'ul', $style = '', $img = null)
1148        {
1149            // get the url of the image, if we want to use a image
1150            if ($img) {
1151                if (preg_match('/^url\(([^)]+)\)$/isU', trim($img), $match)) {
1152                    $img = $match[1];
1153                } else {
1154                    $img = null;
1155                }
1156            } else {
1157                $img = null;
1158            }
1159
1160            // prepare the datas
1161            if (!in_array($type, array('ul', 'ol'))) $type = 'ul';
1162            if (!in_array($style, array('lower-alpha', 'upper-alpha', 'upper-roman', 'lower-roman', 'decimal', 'square', 'circle', 'disc', 'none'))) $style = '';
1163
1164            if (!$style) {
1165                if ($type=='ul')    $style = 'disc';
1166                else                $style = 'decimal';
1167            }
1168
1169            // add the new level
1170            $this->_defList[count($this->_defList)] = array('style' => $style, 'nb' => 0, 'img' => $img);
1171        }
1172
1173        /**
1174         * remove a level to the list
1175         *
1176         * @access protected
1177         */
1178        protected function _listeDelLevel()
1179        {
1180            if (count($this->_defList)) {
1181                unset($this->_defList[count($this->_defList)-1]);
1182                $this->_defList = array_values($this->_defList);
1183            }
1184        }
1185
1186        /**
1187         * execute the actions to convert the html
1188         *
1189         * @access protected
1190         */
1191        protected function _makeHTMLcode()
1192        {
1193            // foreach elements of the parsing
1194            for ($this->_parsePos=0; $this->_parsePos<count($this->parsingHtml->code); $this->_parsePos++) {
1195
1196                // get the action to do
1197                $action = $this->parsingHtml->code[$this->_parsePos];
1198
1199                // if it is a opening of table / ul / ol
1200                if (in_array($action['name'], array('table', 'ul', 'ol')) && !$action['close']) {
1201
1202                    //  we will work as a sub HTML to calculate the size of the element
1203                    $this->_subPart = true;
1204
1205                    // get the name of the opening tag
1206                    $tagOpen = $action['name'];
1207
1208                    // save the actual pos on the parsing
1209                    $this->_tempPos = $this->_parsePos;
1210
1211                    // foreach elements, while we are in the opened tag
1212                    while (isset($this->parsingHtml->code[$this->_tempPos]) && !($this->parsingHtml->code[$this->_tempPos]['name']==$tagOpen && $this->parsingHtml->code[$this->_tempPos]['close'])) {
1213                        // make the action
1214                        $this->_executeAction($this->parsingHtml->code[$this->_tempPos]);
1215                        $this->_tempPos++;
1216                    }
1217
1218                    // execute the closure of the tag
1219                    if (isset($this->parsingHtml->code[$this->_tempPos])) {
1220                        $this->_executeAction($this->parsingHtml->code[$this->_tempPos]);
1221                    }
1222
1223                    // end of the sub part
1224                    $this->_subPart = false;
1225                }
1226
1227                // execute the action
1228                $this->_executeAction($action);
1229            }
1230        }
1231
1232        /**
1233         * execute the action from the parsing
1234         *
1235         * @access protected
1236         * @param  array $action
1237         */
1238        protected function _executeAction($action)
1239        {
1240            // name of the action
1241            $fnc = ($action['close'] ? '_tag_close_' : '_tag_open_').strtoupper($action['name']);
1242
1243            // parameters of the action
1244            $param = $action['param'];
1245
1246            // if it the first action of the first page, and if it is not a open tag of PAGE => create the new page
1247            if ($fnc!='_tag_open_PAGE' && $this->_firstPage) {
1248                $this->_setNewPage();
1249            }
1250
1251            // the action must exist
1252            if (!is_callable(array(&$this, $fnc))) {
1253                throw new HTML2PDF_exception(1, strtoupper($action['name']), $this->parsingHtml->getHtmlErrorCode($action['html_pos']));
1254            }
1255
1256            // lauch the action
1257            $res = $this->{$fnc}($param);
1258
1259            // save the name of the action
1260            $this->_previousCall = $fnc;
1261
1262            // return the result
1263            return $res;
1264        }
1265
1266        /**
1267         * get the position of the element on the current line, depending on his height
1268         *
1269         * @access protected
1270         * @param  float $h
1271         * @return float
1272         */
1273        protected function _getElementY($h)
1274        {
1275            if ($this->_subPart || $this->_isSubPart || !$this->_currentH || $this->_currentH<$h)
1276                return 0;
1277
1278            return ($this->_currentH-$h)*0.8;
1279        }
1280
1281        /**
1282         * make a break line
1283         *
1284         * @access protected
1285         * @param  float $h current line height
1286         * @param  integer $curr real current position in the text, if new line in the write of a text
1287         */
1288        protected function _makeBreakLine($h, $curr = null)
1289        {
1290            if ($h) {
1291                if (($this->pdf->getY()+$h<$this->pdf->getH() - $this->pdf->getbMargin()) || $this->_isInOverflow || $this->_isInFooter)
1292                    $this->_setNewLine($h, $curr);
1293                else
1294                    $this->_setNewPage(null, '', null, $curr);
1295            } else {
1296                $this->_setNewPositionForNewLine($curr);
1297            }
1298
1299            $this->_maxH = 0;
1300            $this->_maxE = 0;
1301        }
1302
1303        /**
1304         * display a image
1305         *
1306         * @access protected
1307         * @param  string $src
1308         * @param  boolean $subLi if true=image of a list
1309         * @return boolean depending on "isForOneLine"
1310         */
1311        protected function _drawImage($src, $subLi=false)
1312        {
1313            // get the size of the image
1314            // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini
1315            $infos=@getimagesize($src);
1316
1317            // if the image does not exist, or can not be loaded
1318            if (count($infos)<2) {
1319                // if the test is activ => exception
1320                if ($this->_testIsImage) {
1321                    throw new HTML2PDF_exception(6, $src);
1322                }
1323
1324                // else, display a gray rectangle
1325                $src = null;
1326                $infos = array(16, 16);
1327            }
1328
1329            // convert the size of the image in the unit of the PDF
1330            $imageWidth = $infos[0]/$this->pdf->getK();
1331            $imageHeight = $infos[1]/$this->pdf->getK();
1332
1333            // calculate the size from the css style
1334            if ($this->parsingCss->value['width'] && $this->parsingCss->value['height']) {
1335                $w = $this->parsingCss->value['width'];
1336                $h = $this->parsingCss->value['height'];
1337            } else if ($this->parsingCss->value['width']) {
1338                $w = $this->parsingCss->value['width'];
1339                $h = $imageHeight*$w/$imageWidth;
1340            } else if ($this->parsingCss->value['height']) {
1341                $h = $this->parsingCss->value['height'];
1342                $w = $imageWidth*$h/$imageHeight;
1343            } else {
1344                // convert px to pt
1345                $w = 72./96.*$imageWidth;
1346                $h = 72./96.*$imageHeight;
1347            }
1348
1349            // are we in a float
1350            $float = $this->parsingCss->getFloat();
1351
1352            // if we are in a float, but if something else if on the line => Break Line
1353            if ($float && $this->_maxH) {
1354                // make the break line (false if we are in "_isForOneLine" mode)
1355                if (!$this->_tag_open_BR(array())) {
1356                    return false;
1357                }
1358            }
1359
1360            // position of the image
1361            $x = $this->pdf->getX();
1362            $y = $this->pdf->getY();
1363
1364            // if the image can not be put on the current line => new line
1365            if (!$float && ($x + $w>$this->pdf->getW() - $this->pdf->getrMargin()) && $this->_maxH) {
1366                if ($this->_isForOneLine) {
1367                    return false;
1368                }
1369
1370                // set the new line
1371                $hnl = max($this->_maxH, $this->parsingCss->getLineHeight());
1372                $this->_setNewLine($hnl);
1373
1374                // get the new position
1375                $x = $this->pdf->getX();
1376                $y = $this->pdf->getY();
1377            }
1378
1379            // if the image can not be put on the current page
1380            if (($y + $h>$this->pdf->getH() - $this->pdf->getbMargin()) && !$this->_isInOverflow) {
1381                // new page
1382                $this->_setNewPage();
1383
1384                // get the new position
1385                $x = $this->pdf->getX();
1386                $y = $this->pdf->getY();
1387            }
1388
1389            // correction for display the image of a list
1390            $hT = 0.80*$this->parsingCss->value['font-size'];
1391            if ($subLi && $h<$hT) {
1392                $y+=($hT-$h);
1393            }
1394
1395            // add the margin top
1396            $yc = $y-$this->parsingCss->value['margin']['t'];
1397
1398            // get the width and the position of the parent
1399            $old = $this->parsingCss->getOldValues();
1400            if ( $old['width']) {
1401                $parentWidth = $old['width'];
1402                $parentX = $x;
1403            } else {
1404                $parentWidth = $this->pdf->getW() - $this->pdf->getlMargin() - $this->pdf->getrMargin();
1405                $parentX = $this->pdf->getlMargin();
1406            }
1407
1408            // if we are in a gloat => adapt the parent position and width
1409            if ($float) {
1410                list($lx, $rx) = $this->_getMargins($yc);
1411                $parentX = $lx;
1412                $parentWidth = $rx-$lx;
1413            }
1414
1415            // calculate the position of the image, if align to the right
1416            if ($parentWidth>$w && $float!='left') {
1417                if ($float=='right' || $this->parsingCss->value['text-align']=='li_right')    $x = $parentX + $parentWidth - $w-$this->parsingCss->value['margin']['r']-$this->parsingCss->value['margin']['l'];
1418            }
1419
1420            // display the image
1421            if (!$this->_subPart && !$this->_isSubPart) {
1422                if ($src) {
1423                    $this->pdf->Image($src, $x, $y, $w, $h, '', $this->_isInLink);
1424                } else {
1425                    // rectangle if the image can not be loaded
1426                    $this->pdf->setFillColorArray(array(240, 220, 220));
1427                    $this->pdf->Rect($x, $y, $w, $h, 'F');
1428                }
1429            }
1430
1431            // apply the margins
1432            $x-= $this->parsingCss->value['margin']['l'];
1433            $y-= $this->parsingCss->value['margin']['t'];
1434            $w+= $this->parsingCss->value['margin']['l'] + $this->parsingCss->value['margin']['r'];
1435            $h+= $this->parsingCss->value['margin']['t'] + $this->parsingCss->value['margin']['b'];
1436
1437            if ($float=='left') {
1438                // save the current max
1439                $this->_maxX = max($this->_maxX, $x+$w);
1440                $this->_maxY = max($this->_maxY, $y+$h);
1441
1442                // add the image to the margins
1443                $this->_addMargins($float, $x, $y, $x+$w, $y+$h);
1444
1445                // get the new position
1446                list($lx, $rx) = $this->_getMargins($yc);
1447                $this->pdf->setXY($lx, $yc);
1448            } else if ($float=='right') {
1449                // save the current max. We don't save the X because it is not the real max of the line
1450                $this->_maxY = max($this->_maxY, $y+$h);
1451
1452                // add the image to the margins
1453                $this->_addMargins($float, $x, $y, $x+$w, $y+$h);
1454
1455                // get the new position
1456                list($lx, $rx) = $this->_getMargins($yc);
1457                $this->pdf->setXY($lx, $yc);
1458            } else {
1459                // set the new position at the end of the image
1460                $this->pdf->setX($x+$w);
1461
1462                // save the current max
1463                $this->_maxX = max($this->_maxX, $x+$w);
1464                $this->_maxY = max($this->_maxY, $y+$h);
1465                $this->_maxH = max($this->_maxH, $h);
1466            }
1467
1468            return true;
1469        }
1470
1471        /**
1472         * draw a rectangle
1473         *
1474         * @access protected
1475         * @param  float $x
1476         * @param  float $y
1477         * @param  float $w
1478         * @param  float $h
1479         * @param  array $border
1480         * @param  float $padding - internal marge of the rectanble => not used, but...
1481         * @param  float $margin  - external marge of the rectanble
1482         * @param  array $background
1483         * @return boolean
1484         */
1485        protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $background)
1486        {
1487            // if we are in a subpart or if height is null => return false
1488            if ($this->_subPart || $this->_isSubPart || $h===null) return false;
1489
1490            // add the margin
1491            $x+= $margin;
1492            $y+= $margin;
1493            $w-= $margin*2;
1494            $h-= $margin*2;
1495
1496            // get the radius of the border
1497            $outTL = $border['radius']['tl'];
1498            $outTR = $border['radius']['tr'];
1499            $outBR = $border['radius']['br'];
1500            $outBL = $border['radius']['bl'];
1501
1502            // prepare the out radius
1503            $outTL = ($outTL[0] && $outTL[1]) ? $outTL : null;
1504            $outTR = ($outTR[0] && $outTR[1]) ? $outTR : null;
1505            $outBR = ($outBR[0] && $outBR[1]) ? $outBR : null;
1506            $outBL = ($outBL[0] && $outBL[1]) ? $outBL : null;
1507
1508            // prepare the in radius
1509            $inTL = $outTL;
1510            $inTR = $outTR;
1511            $inBR = $outBR;
1512            $inBL = $outBL;
1513
1514            if (is_array($inTL)) {
1515                $inTL[0]-= $border['l']['width'];
1516                $inTL[1]-= $border['t']['width'];
1517            }
1518            if (is_array($inTR)) {
1519                $inTR[0]-= $border['r']['width'];
1520                $inTR[1]-= $border['t']['width'];
1521            }
1522            if (is_array($inBR)) {
1523                $inBR[0]-= $border['r']['width'];
1524                $inBR[1]-= $border['b']['width'];
1525            }
1526            if (is_array($inBL)) {
1527                $inBL[0]-= $border['l']['width'];
1528                $inBL[1]-= $border['b']['width'];
1529            }
1530
1531            if ($inTL[0]<=0 || $inTL[1]<=0) $inTL = null;
1532            if ($inTR[0]<=0 || $inTR[1]<=0) $inTR = null;
1533            if ($inBR[0]<=0 || $inBR[1]<=0) $inBR = null;
1534            if ($inBL[0]<=0 || $inBL[1]<=0) $inBL = null;
1535
1536            // prepare the background color
1537            $pdfStyle = '';
1538            if ($background['color']) {
1539                $this->pdf->setFillColorArray($background['color']);
1540                $pdfStyle.= 'F';
1541            }
1542
1543            // if we have a background to fill => fill it with a path (because of the radius)
1544            if ($pdfStyle) {
1545                $this->pdf->clippingPathStart($x, $y, $w, $h, $outTL, $outTR, $outBL, $outBR);
1546                $this->pdf->Rect($x, $y, $w, $h, $pdfStyle);
1547                $this->pdf->clippingPathStop();
1548            }
1549
1550            // prepare the background image
1551            if ($background['image']) {
1552                $iName      = $background['image'];
1553                $iPosition  = $background['position']!==null ? $background['position'] : array(0, 0);
1554                $iRepeat    = $background['repeat']!==null   ? $background['repeat']   : array(true, true);
1555
1556                // size of the background without the borders
1557                $bX = $x;
1558                $bY = $y;
1559                $bW = $w;
1560                $bH = $h;
1561
1562                if ($border['b']['width']) {
1563                    $bH-= $border['b']['width'];
1564                }
1565                if ($border['l']['width']) {
1566                    $bW-= $border['l']['width'];
1567                    $bX+= $border['l']['width'];
1568                }
1569                if ($border['t']['width']) {
1570                    $bH-= $border['t']['width'];
1571                    $bY+= $border['t']['width'];
1572                }
1573                if ($border['r']['width']) {
1574                    $bW-= $border['r']['width'];
1575                }
1576
1577                // get the size of the image
1578                // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini
1579                $imageInfos=@getimagesize($iName);
1580
1581                // if the image can not be loaded
1582                if (count($imageInfos)<2) {
1583                    if ($this->_testIsImage) {
1584                        throw new HTML2PDF_exception(6, $iName);
1585                    }
1586                } else {
1587                    // convert the size of the image from pixel to the unit of the PDF
1588                    $imageWidth    = 72./96.*$imageInfos[0]/$this->pdf->getK();
1589                    $imageHeight    = 72./96.*$imageInfos[1]/$this->pdf->getK();
1590
1591                    // prepare the position of the backgroung
1592                    if ($iRepeat[0]) $iPosition[0] = $bX;
1593                    else if (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[0], $match)) $iPosition[0] = $bX + $match[1]*($bW-$imageWidth)/100;
1594                    else $iPosition[0] = $bX+$iPosition[0];
1595
1596                    if ($iRepeat[1]) $iPosition[1] = $bY;
1597                    else if (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[1], $match)) $iPosition[1] = $bY + $match[1]*($bH-$imageHeight)/100;
1598                    else $iPosition[1] = $bY+$iPosition[1];
1599
1600                    $imageXmin = $bX;
1601                    $imageXmax = $bX+$bW;
1602                    $imageYmin = $bY;
1603                    $imageYmax = $bY+$bH;
1604
1605                    if (!$iRepeat[0] && !$iRepeat[1]) {
1606                        $imageXmin =     $iPosition[0]; $imageXmax =     $iPosition[0]+$imageWidth;
1607                        $imageYmin =     $iPosition[1]; $imageYmax =     $iPosition[1]+$imageHeight;
1608                    } else if ($iRepeat[0] && !$iRepeat[1]) {
1609                        $imageYmin =     $iPosition[1]; $imageYmax =     $iPosition[1]+$imageHeight;
1610                    } else if (!$iRepeat[0] && $iRepeat[1]) {
1611                        $imageXmin =     $iPosition[0]; $imageXmax =     $iPosition[0]+$imageWidth;
1612                    }
1613
1614                    // build the path to display the image (because of radius)
1615                    $this->pdf->clippingPathStart($bX, $bY, $bW, $bH, $inTL, $inTR, $inBL, $inBR);
1616
1617                    // repeat the image
1618                    for ($iY=$imageYmin; $iY<$imageYmax; $iY+=$imageHeight) {
1619                        for ($iX=$imageXmin; $iX<$imageXmax; $iX+=$imageWidth) {
1620                            $cX = null;
1621                            $cY = null;
1622                            $cW = $imageWidth;
1623                            $cH = $imageHeight;
1624                            if ($imageYmax-$iY<$imageHeight) {
1625                                $cX = $iX;
1626                                $cY = $iY;
1627                                $cH = $imageYmax-$iY;
1628                            }
1629                            if ($imageXmax-$iX<$imageWidth) {
1630                                $cX = $iX;
1631                                $cY = $iY;
1632                                $cW = $imageXmax-$iX;
1633                            }
1634
1635                            $this->pdf->Image($iName, $iX, $iY, $imageWidth, $imageHeight, '', '');
1636                        }
1637                    }
1638
1639                    // end of the path
1640                    $this->pdf->clippingPathStop();
1641                }
1642            }
1643
1644            // adding some loose (0.01mm)
1645            $loose = 0.01;
1646            $x-= $loose;
1647            $y-= $loose;
1648            $w+= 2.*$loose;
1649            $h+= 2.*$loose;
1650            if ($border['l']['width']) $border['l']['width']+= 2.*$loose;
1651            if ($border['t']['width']) $border['t']['width']+= 2.*$loose;
1652            if ($border['r']['width']) $border['r']['width']+= 2.*$loose;
1653            if ($border['b']['width']) $border['b']['width']+= 2.*$loose;
1654
1655            // prepare the test on borders
1656            $testBl = ($border['l']['width'] && $border['l']['color'][0]!==null);
1657            $testBt = ($border['t']['width'] && $border['t']['color'][0]!==null);
1658            $testBr = ($border['r']['width'] && $border['r']['color'][0]!==null);
1659            $testBb = ($border['b']['width'] && $border['b']['color'][0]!==null);
1660
1661            // draw the radius bottom-left
1662            if (is_array($outBL) && ($testBb || $testBl)) {
1663                if ($inBL) {
1664                    $courbe = array();
1665                    $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h;
1666                    $courbe[] = $x;                        $courbe[] = $y+$h-$outBL[1];
1667                    $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h-$border['b']['width'];
1668                    $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$h-$outBL[1];
1669                    $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h-$outBL[1];
1670                } else {
1671                    $courbe = array();
1672                    $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h;
1673                    $courbe[] = $x;                        $courbe[] = $y+$h-$outBL[1];
1674                    $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$h-$border['b']['width'];
1675                    $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h-$outBL[1];
1676                }
1677                $this->_drawCurve($courbe, $border['l']['color']);
1678            }
1679
1680            // draw the radius left-top
1681            if (is_array($outTL) && ($testBt || $testBl)) {
1682                if ($inTL) {
1683                    $courbe = array();
1684                    $courbe[] = $x;                        $courbe[] = $y+$outTL[1];
1685                    $courbe[] = $x+$outTL[0];              $courbe[] = $y;
1686                    $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$outTL[1];
1687                    $courbe[] = $x+$outTL[0];              $courbe[] = $y+$border['t']['width'];
1688                    $courbe[] = $x+$outTL[0];              $courbe[] = $y+$outTL[1];
1689                } else {
1690                    $courbe = array();
1691                    $courbe[] = $x;                        $courbe[] = $y+$outTL[1];
1692                    $courbe[] = $x+$outTL[0];              $courbe[] = $y;
1693                    $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$border['t']['width'];
1694                    $courbe[] = $x+$outTL[0];              $courbe[] = $y+$outTL[1];
1695                }
1696                $this->_drawCurve($courbe, $border['t']['color']);
1697            }
1698
1699            // draw the radius top-right
1700            if (is_array($outTR) && ($testBt || $testBr)) {
1701                if ($inTR) {
1702                    $courbe = array();
1703                    $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y;
1704                    $courbe[] = $x+$w;                       $courbe[] = $y+$outTR[1];
1705                    $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y+$border['t']['width'];
1706                    $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$outTR[1];
1707                    $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y+$outTR[1];
1708                } else {
1709                    $courbe = array();
1710                    $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y;
1711                    $courbe[] = $x+$w;                       $courbe[] = $y+$outTR[1];
1712                    $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$border['t']['width'];
1713                    $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y+$outTR[1];
1714                }
1715                $this->_drawCurve($courbe, $border['r']['color']);
1716            }
1717
1718            // draw the radius right-bottom
1719            if (is_array($outBR) && ($testBb || $testBr)) {
1720                if ($inBR) {
1721                    $courbe = array();
1722                    $courbe[] = $x+$w;                       $courbe[] = $y+$h-$outBR[1];
1723                    $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h;
1724                    $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$h-$outBR[1];
1725                    $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h-$border['b']['width'];
1726                    $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h-$outBR[1];
1727                } else {
1728                    $courbe = array();
1729                    $courbe[] = $x+$w;                       $courbe[] = $y+$h-$outBR[1];
1730                    $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h;
1731                    $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$h-$border['b']['width'];
1732                    $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h-$outBR[1];
1733                }
1734                $this->_drawCurve($courbe, $border['b']['color']);
1735            }
1736
1737            // draw the left border
1738            if ($testBl) {
1739                $pt = array();
1740                $pt[] = $x;                       $pt[] = $y+$h;
1741                $pt[] = $x;                       $pt[] = $y+$h-$border['b']['width'];
1742                $pt[] = $x;                       $pt[] = $y+$border['t']['width'];
1743                $pt[] = $x;                       $pt[] = $y;
1744                $pt[] = $x+$border['l']['width']; $pt[] = $y+$border['t']['width'];
1745                $pt[] = $x+$border['l']['width']; $pt[] = $y+$h-$border['b']['width'];
1746
1747                $bord = 3;
1748                if (is_array($outBL)) {
1749                    $bord-=1;
1750                    $pt[3] -= $outBL[1] - $border['b']['width'];
1751                    if ($inBL) $pt[11]-= $inBL[1];
1752                    unset($pt[0]);unset($pt[1]);
1753                }
1754                if (is_array($outTL)) {
1755                    $bord-=2;
1756                    $pt[5] += $outTL[1]-$border['t']['width'];
1757                    if ($inTL) $pt[9] += $inTL[1];
1758                    unset($pt[6]);unset($pt[7]);
1759                }
1760
1761                $pt = array_values($pt);
1762                $this->_drawLine($pt, $border['l']['color'], $border['l']['type'], $border['l']['width'], $bord);
1763            }
1764
1765            // draw the top border
1766            if ($testBt) {
1767                $pt = array();
1768                $pt[] = $x;                          $pt[] = $y;
1769                $pt[] = $x+$border['l']['width'];    $pt[] = $y;
1770                $pt[] = $x+$w-$border['r']['width']; $pt[] = $y;
1771                $pt[] = $x+$w;                       $pt[] = $y;
1772                $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$border['t']['width'];
1773                $pt[] = $x+$border['l']['width'];    $pt[] = $y+$border['t']['width'];
1774
1775                $bord = 3;
1776                if (is_array($outTL)) {
1777                    $bord-=1;
1778                    $pt[2] += $outTL[0] - $border['l']['width'];
1779                    if ($inTL) $pt[10]+= $inTL[0];
1780                    unset($pt[0]);unset($pt[1]);
1781                }
1782                if (is_array($outTR)) {
1783                    $bord-=2;
1784                    $pt[4] -= $outTR[0] - $border['r']['width'];
1785                    if ($inTR) $pt[8] -= $inTR[0];
1786                    unset($pt[6]);unset($pt[7]);
1787                }
1788
1789                $pt = array_values($pt);
1790                $this->_drawLine($pt, $border['t']['color'], $border['t']['type'], $border['t']['width'], $bord);
1791            }
1792
1793            // draw the right border
1794            if ($testBr) {
1795                $pt = array();
1796                $pt[] = $x+$w;                       $pt[] = $y;
1797                $pt[] = $x+$w;                       $pt[] = $y+$border['t']['width'];
1798                $pt[] = $x+$w;                       $pt[] = $y+$h-$border['b']['width'];
1799                $pt[] = $x+$w;                       $pt[] = $y+$h;
1800                $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$h-$border['b']['width'];
1801                $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$border['t']['width'];
1802
1803                $bord = 3;
1804                if (is_array($outTR)) {
1805                    $bord-=1;
1806                    $pt[3] += $outTR[1] - $border['t']['width'];
1807                    if ($inTR) $pt[11]+= $inTR[1];
1808                    unset($pt[0]);unset($pt[1]);
1809                }
1810                if (is_array($outBR)) {
1811                    $bord-=2;
1812                    $pt[5] -= $outBR[1] - $border['b']['width'];
1813                    if ($inBR) $pt[9] -= $inBR[1];
1814                    unset($pt[6]);unset($pt[7]);
1815                }
1816
1817                $pt = array_values($pt);
1818                $this->_drawLine($pt, $border['r']['color'], $border['r']['type'], $border['r']['width'], $bord);
1819            }
1820
1821            // draw the bottom border
1822            if ($testBb) {
1823                $pt = array();
1824                $pt[] = $x+$w;                       $pt[] = $y+$h;
1825                $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$h;
1826                $pt[] = $x+$border['l']['width'];    $pt[] = $y+$h;
1827                $pt[] = $x;                          $pt[] = $y+$h;
1828                $pt[] = $x+$border['l']['width'];    $pt[] = $y+$h-$border['b']['width'];
1829                $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$h-$border['b']['width'];
1830
1831                $bord = 3;
1832                if (is_array($outBL)) {
1833                    $bord-=2;
1834                    $pt[4] += $outBL[0] - $border['l']['width'];
1835                    if ($inBL) $pt[8] += $inBL[0];
1836                    unset($pt[6]);unset($pt[7]);
1837                }
1838                if (is_array($outBR)) {
1839                    $bord-=1;
1840                    $pt[2] -= $outBR[0] - $border['r']['width'];
1841                    if ($inBR) $pt[10]-= $inBR[0];
1842                    unset($pt[0]);unset($pt[1]);
1843
1844                }
1845
1846                $pt = array_values($pt);
1847                $this->_drawLine($pt, $border['b']['color'], $border['b']['type'], $border['b']['width'], $bord);
1848            }
1849
1850            if ($background['color']) {
1851                $this->pdf->setFillColorArray($background['color']);
1852            }
1853
1854            return true;
1855        }
1856
1857        /**
1858         * draw a curve (for border radius)
1859         *
1860         * @access protected
1861         * @param  array $pt
1862         * @param  array $color
1863         */
1864        protected function _drawCurve($pt, $color)
1865        {
1866            $this->pdf->setFillColorArray($color);
1867
1868            if (count($pt)==10)
1869                $this->pdf->drawCurve($pt[0], $pt[1], $pt[2], $pt[3], $pt[4], $pt[5], $pt[6], $pt[7], $pt[8], $pt[9]);
1870            else
1871                $this->pdf->drawCorner($pt[0], $pt[1], $pt[2], $pt[3], $pt[4], $pt[5], $pt[6], $pt[7]);
1872        }
1873
1874        /**
1875         * draw a ligne with a specific type, and specific start and end for radius
1876         *
1877         * @access protected
1878         * @param  array   $pt
1879         * @param  float   $color
1880         * @param  string  $type (dashed, dotted, double, solid)
1881         * @param  float   $width
1882         * @param  integer $radius (binary from 0 to 3 with 1=>start with a radius, 2=>end with a radius)
1883         */
1884        protected function _drawLine($pt, $color, $type, $width, $radius=3)
1885        {
1886            // set the fill color
1887            $this->pdf->setFillColorArray($color);
1888
1889            // if dashed or dotted
1890            if ($type=='dashed' || $type=='dotted') {
1891
1892                // clean the end of the line, if radius
1893                if ($radius==1) {
1894                    $tmp = array(); $tmp[]=$pt[0]; $tmp[]=$pt[1]; $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[8]; $tmp[]=$pt[9];
1895                    $this->pdf->Polygon($tmp, 'F');
1896
1897                    $tmp = array(); $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[4]; $tmp[]=$pt[5]; $tmp[]=$pt[6]; $tmp[]=$pt[7]; $tmp[]=$pt[8]; $tmp[]=$pt[9];
1898                    $pt = $tmp;
1899                } else if ($radius==2) {
1900                    $tmp = array(); $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[4]; $tmp[]=$pt[5]; $tmp[]=$pt[6]; $tmp[]=$pt[7];
1901                    $this->pdf->Polygon($tmp, 'F');
1902
1903                    $tmp = array(); $tmp[]=$pt[0]; $tmp[]=$pt[1]; $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[6]; $tmp[]=$pt[7]; $tmp[]=$pt[8]; $tmp[]=$pt[9];
1904                    $pt = $tmp;
1905                } else if ($radius==3) {
1906                    $tmp = array(); $tmp[]=$pt[0]; $tmp[]=$pt[1]; $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[10]; $tmp[]=$pt[11];
1907                    $this->pdf->Polygon($tmp, 'F');
1908
1909                    $tmp = array(); $tmp[]=$pt[4]; $tmp[]=$pt[5]; $tmp[]=$pt[6]; $tmp[]=$pt[7]; $tmp[]=$pt[8]; $tmp[]=$pt[9];
1910                    $this->pdf->Polygon($tmp, 'F');
1911
1912                    $tmp = array(); $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[4]; $tmp[]=$pt[5]; $tmp[]=$pt[8]; $tmp[]=$pt[9]; $tmp[]=$pt[10]; $tmp[]=$pt[11];
1913                    $pt = $tmp;
1914                }
1915
1916                // horisontal or vertical line
1917                if ($pt[2]==$pt[0]) {
1918                    $l = abs(($pt[3]-$pt[1])*0.5);
1919                    $px = 0;
1920                    $py = $width;
1921                    $x1 = $pt[0]; $y1 = ($pt[3]+$pt[1])*0.5;
1922                    $x2 = $pt[6]; $y2 = ($pt[7]+$pt[5])*0.5;
1923                } else {
1924                    $l = abs(($pt[2]-$pt[0])*0.5);
1925                    $px = $width;
1926                    $py = 0;
1927                    $x1 = ($pt[2]+$pt[0])*0.5; $y1 = $pt[1];
1928                    $x2 = ($pt[6]+$pt[4])*0.5; $y2 = $pt[7];
1929                }
1930
1931                // if dashed : 3x bigger than dotted
1932                if ($type=='dashed') {
1933                    $px = $px*3.;
1934                    $py = $py*3.;
1935                }
1936                $mode = ($l/($px+$py)<.5);
1937
1938                // display the dotted/dashed line
1939                for ($i=0; $l-($px+$py)*($i-0.5)>0; $i++) {
1940                    if (($i%2)==$mode) {
1941                        $j = $i-0.5;
1942                        $lx1 = $px*($j);   if ($lx1<-$l) $lx1 =-$l;
1943                        $ly1 = $py*($j);   if ($ly1<-$l) $ly1 =-$l;
1944                        $lx2 = $px*($j+1); if ($lx2>$l)  $lx2 = $l;
1945                        $ly2 = $py*($j+1); if ($ly2>$l)  $ly2 = $l;
1946
1947                        $tmp = array();
1948                        $tmp[] = $x1+$lx1; $tmp[] = $y1+$ly1;
1949                        $tmp[] = $x1+$lx2; $tmp[] = $y1+$ly2;
1950                        $tmp[] = $x2+$lx2; $tmp[] = $y2+$ly2;
1951                        $tmp[] = $x2+$lx1; $tmp[] = $y2+$ly1;
1952                        $this->pdf->Polygon($tmp, 'F');
1953
1954                        if ($j>0) {
1955                            $tmp = array();
1956                            $tmp[] = $x1-$lx1; $tmp[] = $y1-$ly1;
1957                            $tmp[] = $x1-$lx2; $tmp[] = $y1-$ly2;
1958                            $tmp[] = $x2-$lx2; $tmp[] = $y2-$ly2;
1959                            $tmp[] = $x2-$lx1; $tmp[] = $y2-$ly1;
1960                            $this->pdf->Polygon($tmp, 'F');
1961                        }
1962                    }
1963                }
1964            } else if ($type=='double') {
1965
1966                // if double, 2 lines : 0=>1/3 and 2/3=>1
1967                $pt1 = $pt;
1968                $pt2 = $pt;
1969
1970                if (count($pt)==12) {
1971                    // line 1
1972                    $pt1[0] = ($pt[0]-$pt[10])*0.33 + $pt[10];
1973                    $pt1[1] = ($pt[1]-$pt[11])*0.33 + $pt[11];
1974                    $pt1[2] = ($pt[2]-$pt[10])*0.33 + $pt[10];
1975                    $pt1[3] = ($pt[3]-$pt[11])*0.33 + $pt[11];
1976                    $pt1[4] = ($pt[4]-$pt[8])*0.33 + $pt[8];
1977                    $pt1[5] = ($pt[5]-$pt[9])*0.33 + $pt[9];
1978                    $pt1[6] = ($pt[6]-$pt[8])*0.33 + $pt[8];
1979                    $pt1[7] = ($pt[7]-$pt[9])*0.33 + $pt[9];
1980                    $pt2[10]= ($pt[10]-$pt[0])*0.33 + $pt[0];
1981                    $pt2[11]= ($pt[11]-$pt[1])*0.33 + $pt[1];
1982
1983                    // line 2
1984                    $pt2[2] = ($pt[2] -$pt[0])*0.33 + $pt[0];
1985                    $pt2[3] = ($pt[3] -$pt[1])*0.33 + $pt[1];
1986                    $pt2[4] = ($pt[4] -$pt[6])*0.33 + $pt[6];
1987                    $pt2[5] = ($pt[5] -$pt[7])*0.33 + $pt[7];
1988                    $pt2[8] = ($pt[8] -$pt[6])*0.33 + $pt[6];
1989                    $pt2[9] = ($pt[9] -$pt[7])*0.33 + $pt[7];
1990                } else {
1991                    // line 1
1992                    $pt1[0] = ($pt[0]-$pt[6])*0.33 + $pt[6];
1993                    $pt1[1] = ($pt[1]-$pt[7])*0.33 + $pt[7];
1994                    $pt1[2] = ($pt[2]-$pt[4])*0.33 + $pt[4];
1995                    $pt1[3] = ($pt[3]-$pt[5])*0.33 + $pt[5];
1996
1997                    // line 2
1998                    $pt2[6] = ($pt[6]-$pt[0])*0.33 + $pt[0];
1999                    $pt2[7] = ($pt[7]-$pt[1])*0.33 + $pt[1];
2000                    $pt2[4] = ($pt[4]-$pt[2])*0.33 + $pt[2];
2001                    $pt2[5] = ($pt[5]-$pt[3])*0.33 + $pt[3];
2002                }
2003                $this->pdf->Polygon($pt1, 'F');
2004                $this->pdf->Polygon($pt2, 'F');
2005            } else if ($type=='solid') {
2006                // solid line : draw directly the polygon
2007                $this->pdf->Polygon($pt, 'F');
2008            }
2009        }
2010
2011        /**
2012         * prepare a transform matrix, only for drawing a SVG graphic
2013         *
2014         * @access protected
2015         * @param  string $transform
2016         * @return array  $matrix
2017         */
2018        protected function _prepareTransform($transform)
2019        {
2020            // it can not be  empty
2021            if (!$transform) return null;
2022
2023            // sctions must be like scale(...)
2024            if (!preg_match_all('/([a-z]+)\(([^\)]*)\)/isU', $transform, $match)) return null;
2025
2026            // prepare the list of the actions
2027            $actions = array();
2028
2029            // for actions
2030            for ($k=0; $k<count($match[0]); $k++) {
2031
2032                // get the name of the action
2033                $name = strtolower($match[1][$k]);
2034
2035                // get the parameters of the action
2036                $val = explode(',', trim($match[2][$k]));
2037                foreach ($val as $i => $j) {
2038                    $val[$i] = trim($j);
2039                }
2040
2041                // prepare the matrix, depending on the action
2042                switch($name)
2043                {
2044                    case 'scale':
2045                        if (!isset($val[0])) $val[0] = 1.;      else $val[0] = 1.*$val[0];
2046                        if (!isset($val[1])) $val[1] = $val[0]; else $val[1] = 1.*$val[1];
2047                        $actions[] = array($val[0],0,0,$val[1],0,0);
2048                        break;
2049
2050                    case 'translate':
2051                        if (!isset($val[0])) $val[0] = 0.; else $val[0] = $this->parsingCss->ConvertToMM($val[0], $this->_isInDraw['w']);
2052                        if (!isset($val[1])) $val[1] = 0.; else $val[1] = $this->parsingCss->ConvertToMM($val[1], $this->_isInDraw['h']);
2053                        $actions[] = array(1,0,0,1,$val[0],$val[1]);
2054                        break;
2055
2056                    case 'rotate':
2057                        if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*M_PI/180.;
2058                        if (!isset($val[1])) $val[1] = 0.; else $val[1] = $this->parsingCss->ConvertToMM($val[1], $this->_isInDraw['w']);
2059                        if (!isset($val[2])) $val[2] = 0.; else $val[2] = $this->parsingCss->ConvertToMM($val[2], $this->_isInDraw['h']);
2060                        if ($val[1] || $val[2]) $actions[] = array(1,0,0,1,-$val[1],-$val[2]);
2061                        $actions[] = array(cos($val[0]),sin($val[0]),-sin($val[0]),cos($val[0]),0,0);
2062                        if ($val[1] || $val[2]) $actions[] = array(1,0,0,1,$val[1],$val[2]);
2063                        break;
2064
2065                    case 'skewx':
2066                        if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*M_PI/180.;
2067                        $actions[] = array(1,0,tan($val[0]),1,0,0);
2068                        break;
2069
2070                    case 'skewy':
2071                        if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*M_PI/180.;
2072                        $actions[] = array(1,tan($val[0]),0,1,0,0);
2073                        break;
2074                    case 'matrix':
2075                        if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*1.;
2076                        if (!isset($val[1])) $val[1] = 0.; else $val[1] = $val[1]*1.;
2077                        if (!isset($val[2])) $val[2] = 0.; else $val[2] = $val[2]*1.;
2078                        if (!isset($val[3])) $val[3] = 0.; else $val[3] = $val[3]*1.;
2079                        if (!isset($val[4])) $val[4] = 0.; else $val[4] = $this->parsingCss->ConvertToMM($val[4], $this->_isInDraw['w']);
2080                        if (!isset($val[5])) $val[5] = 0.; else $val[5] = $this->parsingCss->ConvertToMM($val[5], $this->_isInDraw['h']);
2081                        $actions[] =$val;
2082                        break;
2083                }
2084            }
2085
2086            // if ther is no actions => return
2087            if (!$actions) return null;
2088
2089            // get the first matrix
2090            $m = $actions[0]; unset($actions[0]);
2091
2092            // foreach matrix => multiply to the last matrix
2093            foreach ($actions as $n) {
2094                $m = array(
2095                    $m[0]*$n[0]+$m[2]*$n[1],
2096                    $m[1]*$n[0]+$m[3]*$n[1],
2097                    $m[0]*$n[2]+$m[2]*$n[3],
2098                    $m[1]*$n[2]+$m[3]*$n[3],
2099                    $m[0]*$n[4]+$m[2]*$n[5]+$m[4],
2100                    $m[1]*$n[4]+$m[3]*$n[5]+$m[5]
2101                );
2102            }
2103
2104            // return the matrix
2105            return $m;
2106        }
2107
2108        /**
2109         * @access protected
2110         * @param  &array $cases
2111         * @param  &array $corr
2112         */
2113        protected function _calculateTableCellSize(&$cases, &$corr)
2114        {
2115            if (!isset($corr[0])) return true;
2116
2117            // for each cell without colspan, we get the max width for each column
2118            $sw = array();
2119            for ($x=0; $x<count($corr[0]); $x++) {
2120                $m=0;
2121                for ($y=0; $y<count($corr); $y++) {
2122                    if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][2]==1) {
2123                        $m = max($m, $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w']);
2124                    }
2125                }
2126                $sw[$x] = $m;
2127            }
2128
2129            // for each cell with colspan, we adapt the width of each column
2130            for ($x=0; $x<count($corr[0]); $x++) {
2131                for ($y=0; $y<count($corr); $y++) {
2132                    if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][2]>1) {
2133
2134                        // sum the max width of each column in colspan
2135                        $s = 0; for ($i=0; $i<$corr[$y][$x][2]; $i++) $s+= $sw[$x+$i];
2136
2137                        // if the max width is < the width of the cell with colspan => we adapt the width of each max width
2138                        if ($s>0 && $s<$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w']) {
2139                            for ($i=0; $i<$corr[$y][$x][2]; $i++) {
2140                                $sw[$x+$i] = $sw[$x+$i]/$s*$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'];
2141                            }
2142                        }
2143                    }
2144                }
2145            }
2146
2147            // set the new width, for each cell
2148            for ($x=0; $x<count($corr[0]); $x++) {
2149                for ($y=0; $y<count($corr); $y++) {
2150                    if (isset($corr[$y][$x]) && is_array($corr[$y][$x])) {
2151                        // without colspan
2152                        if ($corr[$y][$x][2]==1) {
2153                            $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'] = $sw[$x];
2154                        // with colspan
2155                        } else {
2156                            $s = 0;
2157                            for ($i=0; $i<$corr[$y][$x][2]; $i++) {
2158                                $s+= $sw[$x+$i];
2159                            }
2160                            $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'] = $s;
2161                        }
2162                    }
2163                }
2164            }
2165
2166            // for each cell without rowspan, we get the max height for each line
2167            $sh = array();
2168            for ($y=0; $y<count($corr); $y++) {
2169                $m=0;
2170                for ($x=0; $x<count($corr[0]); $x++) {
2171                    if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][3]==1) {
2172                        $m = max($m, $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h']);
2173                    }
2174                }
2175                $sh[$y] = $m;
2176            }
2177
2178            // for each cell with rowspan, we adapt the height of each line
2179            for ($y=0; $y<count($corr); $y++) {
2180                for ($x=0; $x<count($corr[0]); $x++) {
2181                    if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][3]>1) {
2182
2183                        // sum the max height of each line in rowspan
2184                        $s = 0;
2185                        for ($i=0; $i<$corr[$y][$x][3]; $i++) {
2186                            $s+= isset($sh[$y+$i]) ? $sh[$y+$i] : 0;
2187                        }
2188
2189                        // if the max height is < the height of the cell with rowspan => we adapt the height of each max height
2190                        if ($s>0 && $s<$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h']) {
2191                            for ($i=0; $i<$corr[$y][$x][3]; $i++) {
2192                                $sh[$y+$i] = $sh[$y+$i]/$s*$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h'];
2193                            }
2194                        }
2195                    }
2196                }
2197            }
2198
2199            // set the new height, for each cell
2200            for ($y=0; $y<count($corr); $y++) {
2201                for ($x=0; $x<count($corr[0]); $x++) {
2202                    if (isset($corr[$y][$x]) && is_array($corr[$y][$x])) {
2203                        // without rowspan
2204                        if ($corr[$y][$x][3]==1) {
2205                            $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h'] = $sh[$y];
2206                        // with rowspan
2207                        } else {
2208                            $s = 0;
2209                            for ($i=0; $i<$corr[$y][$x][3]; $i++) {
2210                                $s+= $sh[$y+$i];
2211                            }
2212                            $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h'] = $s;
2213
2214                            for ($j=1; $j<$corr[$y][$x][3]; $j++) {
2215                                $tx = $x+1;
2216                                $ty = $y+$j;
2217                                for (true; isset($corr[$ty][$tx]) && !is_array($corr[$ty][$tx]); $tx++);
2218                                if (isset($corr[$ty][$tx])) {
2219                                    $cases[$corr[$ty][$tx][1]][$corr[$ty][$tx][0]]['dw']+= $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'];
2220                                }
2221                            }
2222                        }
2223                    }
2224                }
2225            }
2226        }
2227
2228        /**
2229         * tag : PAGE
2230         * mode : OPEN
2231         *
2232         * @param  array $param
2233         * @return boolean
2234         */
2235        protected function _tag_open_PAGE($param)
2236        {
2237            if ($this->_isForOneLine) return false;
2238            if ($this->_debugActif) $this->_DEBUG_add('PAGE '.($this->_page+1), true);
2239
2240            $newPageSet= (!isset($param['pageset']) || $param['pageset']!='old');
2241
2242            $resetPageNumber = (isset($param['pagegroup']) && $param['pagegroup']=='new');
2243           
2244            if (array_key_exists('hideheader', $param) && $param['hideheader']!='false' && !empty($param['hideheader'])) {
2245                $this->_hideHeader = (array) array_merge($this->_hideHeader, split(',', $param['hideheader']));
2246            }
2247
2248            $this->_maxH = 0;
2249
2250            // if new page set asked
2251            if ($newPageSet) {
2252                $this->_subHEADER = array();
2253                $this->_subFOOTER = array();
2254
2255                // orientation
2256                $orientation = '';
2257                if (isset($param['orientation'])) {
2258                    $param['orientation'] = strtolower($param['orientation']);
2259                    if ($param['orientation']=='p')         $orientation = 'P';
2260                    if ($param['orientation']=='portrait')  $orientation = 'P';
2261
2262                    if ($param['orientation']=='l')         $orientation = 'L';
2263                    if ($param['orientation']=='paysage')   $orientation = 'L';
2264                    if ($param['orientation']=='landscape') $orientation = 'L';
2265                }
2266
2267                // format
2268                $format = null;
2269                if (isset($param['format'])) {
2270                    $format = strtolower($param['format']);
2271                    if (preg_match('/^([0-9]+)x([0-9]+)$/isU', $format, $match)) {
2272                        $format = array(intval($match[1]), intval($match[2]));
2273                    }
2274                }
2275
2276                // background
2277                $background = array();
2278                if (isset($param['backimg'])) {
2279                    $background['img']    = isset($param['backimg'])  ? $param['backimg']  : '';       // src of the image
2280                    $background['posX']   = isset($param['backimgx']) ? $param['backimgx'] : 'center'; // horizontale position of the image
2281                    $background['posY']   = isset($param['backimgy']) ? $param['backimgy'] : 'middle'; // vertical position of the image
2282                    $background['width']  = isset($param['backimgw']) ? $param['backimgw'] : '100%';   // width of the image (100% = page width)
2283
2284                    // convert the src of the image, if parameters
2285                    $background['img'] = str_replace('&amp;', '&', $background['img']);
2286
2287                    // convert the positions
2288                    if ($background['posX']=='left')    $background['posX'] = '0%';
2289                    if ($background['posX']=='center')  $background['posX'] = '50%';
2290                    if ($background['posX']=='right')   $background['posX'] = '100%';
2291                    if ($background['posY']=='top')     $background['posY'] = '0%';
2292                    if ($background['posY']=='middle')  $background['posY'] = '50%';
2293                    if ($background['posY']=='bottom')  $background['posY'] = '100%';
2294
2295                    if ($background['img']) {
2296                        // get the size of the image
2297                        // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini
2298                        $infos=@getimagesize($background['img']);
2299                        if (count($infos)>1) {
2300                            $imageWidth = $this->parsingCss->ConvertToMM($background['width'], $this->pdf->getW());
2301                            $imageHeight = $imageWidth*$infos[1]/$infos[0];
2302
2303                            $background['width'] = $imageWidth;
2304                            $background['posX']  = $this->parsingCss->ConvertToMM($background['posX'], $this->pdf->getW() - $imageWidth);
2305                            $background['posY']  = $this->parsingCss->ConvertToMM($background['posY'], $this->pdf->getH() - $imageHeight);
2306                        } else {
2307                            $background = array();
2308                        }
2309                    } else {
2310                        $background = array();
2311                    }
2312                }
2313
2314                // margins of the page
2315                $background['top']    = isset($param['backtop'])    ? $param['backtop']    : '0';
2316                $background['bottom'] = isset($param['backbottom']) ? $param['backbottom'] : '0';
2317                $background['left']   = isset($param['backleft'])   ? $param['backleft']   : '0';
2318                $background['right']  = isset($param['backright'])  ? $param['backright']  : '0';
2319
2320                // if no unit => mm
2321                if (preg_match('/^([0-9]*)$/isU', $background['top']))    $background['top']    .= 'mm';
2322                if (preg_match('/^([0-9]*)$/isU', $background['bottom'])) $background['bottom'] .= 'mm';
2323                if (preg_match('/^([0-9]*)$/isU', $background['left']))   $background['left']   .= 'mm';
2324                if (preg_match('/^([0-9]*)$/isU', $background['right']))  $background['right']  .= 'mm';
2325
2326                // convert to mm
2327                $background['top']    = $this->parsingCss->ConvertToMM($background['top'], $this->pdf->getH());
2328                $background['bottom'] = $this->parsingCss->ConvertToMM($background['bottom'], $this->pdf->getH());
2329                $background['left']   = $this->parsingCss->ConvertToMM($background['left'], $this->pdf->getW());
2330                $background['right']  = $this->parsingCss->ConvertToMM($background['right'], $this->pdf->getW());
2331
2332                // get the background color
2333                $res = false;
2334                $background['color']    = isset($param['backcolor'])    ? $this->parsingCss->convertToColor($param['backcolor'], $res) : null;
2335                if (!$res) $background['color'] = null;
2336
2337                $this->parsingCss->save();
2338                $this->parsingCss->analyse('PAGE', $param);
2339                $this->parsingCss->setPosition();
2340                $this->parsingCss->fontSet();
2341
2342                // new page
2343                $this->_setNewPage($format, $orientation, $background, null, $resetPageNumber);
2344
2345                // automatic footer
2346                if (isset($param['footer'])) {
2347                    $lst = explode(';', $param['footer']);
2348                    foreach ($lst as $key => $val) $lst[$key] = trim(strtolower($val));
2349                    $page    = in_array('page', $lst);
2350                    $date    = in_array('date', $lst);
2351                    $hour    = in_array('heure', $lst);
2352                    $form    = in_array('form', $lst);
2353                } else {
2354                    $page    = null;
2355                    $date    = null;
2356                    $hour    = null;
2357                    $form    = null;
2358                }
2359                $this->pdf->SetMyFooter($page, $date, $hour, $form);
2360            // else => we use the last page set used
2361            } else {
2362                $this->parsingCss->save();
2363                $this->parsingCss->analyse('PAGE', $param);
2364                $this->parsingCss->setPosition();
2365                $this->parsingCss->fontSet();
2366
2367                $this->_setNewPage(null, null, null, null, $resetPageNumber);
2368            }
2369
2370            return true;
2371        }
2372
2373        /**
2374         * tag : PAGE
2375         * mode : CLOSE
2376         *
2377         * @param  array $param
2378         * @return boolean
2379         */
2380        protected function _tag_close_PAGE($param)
2381        {
2382            if ($this->_isForOneLine) return false;
2383
2384            $this->_maxH = 0;
2385
2386            $this->parsingCss->load();
2387            $this->parsingCss->fontSet();
2388
2389            if ($this->_debugActif) $this->_DEBUG_add('PAGE '.$this->_page, false);
2390
2391            return true;
2392        }
2393
2394        /**
2395         * tag : PAGE_HEADER
2396         * mode : OPEN
2397         *
2398         * @param  array $param
2399         * @return boolean
2400         */
2401        protected function _tag_open_PAGE_HEADER($param)
2402        {
2403            if ($this->_isForOneLine) return false;
2404
2405            $this->_subHEADER = array();
2406            for ($this->_parsePos; $this->_parsePos<count($this->parsingHtml->code); $this->_parsePos++) {
2407                $action = $this->parsingHtml->code[$this->_parsePos];
2408                if ($action['name']=='page_header') $action['name']='page_header_sub';
2409                $this->_subHEADER[] = $action;
2410                if (strtolower($action['name'])=='page_header_sub' && $action['close']) break;
2411            }
2412
2413            $this->_setPageHeader();
2414
2415            return true;
2416        }
2417
2418        /**
2419         * tag : PAGE_FOOTER
2420         * mode : OPEN
2421         *
2422         * @param  array $param
2423         * @return boolean
2424         */
2425        protected function _tag_open_PAGE_FOOTER($param)
2426        {
2427            if ($this->_isForOneLine) return false;
2428
2429            $this->_subFOOTER = array();
2430            for ($this->_parsePos; $this->_parsePos<count($this->parsingHtml->code); $this->_parsePos++) {
2431                $action = $this->parsingHtml->code[$this->_parsePos];
2432                if ($action['name']=='page_footer') $action['name']='page_footer_sub';
2433                $this->_subFOOTER[] = $action;
2434                if (strtolower($action['name'])=='page_footer_sub' && $action['close']) break;
2435            }
2436
2437            $this->_setPageFooter();
2438
2439            return true;
2440        }
2441
2442        /**
2443         * It is not a real tag. Does not use it directly
2444         *
2445         * @param  array $param
2446         * @return boolean
2447         */
2448        protected function _tag_open_PAGE_HEADER_SUB($param)
2449        {
2450            if ($this->_isForOneLine) return false;
2451
2452            // save the current stat
2453            $this->_subSTATES = array();
2454            $this->_subSTATES['x']  = $this->pdf->getX();
2455            $this->_subSTATES['y']  = $this->pdf->getY();
2456            $this->_subSTATES['s']  = $this->parsingCss->value;
2457            $this->_subSTATES['t']  = $this->parsingCss->table;
2458            $this->_subSTATES['ml'] = $this->_margeLeft;
2459            $this->_subSTATES['mr'] = $this->_margeRight;
2460            $this->_subSTATES['mt'] = $this->_margeTop;
2461            $this->_subSTATES['mb'] = $this->_margeBottom;
2462            $this->_subSTATES['mp'] = $this->_pageMarges;
2463
2464            // new stat for the header
2465            $this->_pageMarges = array();
2466            $this->_margeLeft    = $this->_defaultLeft;
2467            $this->_margeRight   = $this->_defaultRight;
2468            $this->_margeTop     = $this->_defaultTop;
2469            $this->_margeBottom  = $this->_defaultBottom;
2470            $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2471            $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2472            $this->pdf->setXY($this->_defaultLeft, $this->_defaultTop);
2473
2474            $this->parsingCss->initStyle();
2475            $this->parsingCss->resetStyle();
2476            $this->parsingCss->value['width'] = $this->pdf->getW() - $this->_defaultLeft - $this->_defaultRight;
2477            $this->parsingCss->table = array();
2478
2479            $this->parsingCss->save();
2480            $this->parsingCss->analyse('page_header_sub', $param);
2481            $this->parsingCss->setPosition();
2482            $this->parsingCss->fontSet();
2483            $this->_setNewPositionForNewLine();
2484            return true;
2485        }
2486
2487        /**
2488         * It is not a real tag. Does not use it directly
2489         *
2490         * @param  array $param
2491         * @return boolean
2492         */
2493        protected function _tag_close_PAGE_HEADER_SUB($param)
2494        {
2495            if ($this->_isForOneLine) return false;
2496
2497            $this->parsingCss->load();
2498
2499            // restore the stat
2500            $this->parsingCss->value = $this->_subSTATES['s'];
2501            $this->parsingCss->table = $this->_subSTATES['t'];
2502            $this->_pageMarges       = $this->_subSTATES['mp'];
2503            $this->_margeLeft        = $this->_subSTATES['ml'];
2504            $this->_margeRight       = $this->_subSTATES['mr'];
2505            $this->_margeTop         = $this->_subSTATES['mt'];
2506            $this->_margeBottom      = $this->_subSTATES['mb'];
2507            $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2508            $this->pdf->setbMargin($this->_margeBottom);
2509            $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2510            $this->pdf->setXY($this->_subSTATES['x'], $this->_subSTATES['y']);
2511
2512            $this->parsingCss->fontSet();
2513            $this->_maxH = 0;
2514
2515            return true;
2516        }
2517
2518        /**
2519         * It is not a real tag. Does not use it directly
2520         *
2521         * @param  array $param
2522         * @return boolean
2523         */
2524        protected function _tag_open_PAGE_FOOTER_SUB($param)
2525        {
2526            if ($this->_isForOneLine) return false;
2527
2528            // save the current stat
2529            $this->_subSTATES = array();
2530            $this->_subSTATES['x']    = $this->pdf->getX();
2531            $this->_subSTATES['y']    = $this->pdf->getY();
2532            $this->_subSTATES['s']    = $this->parsingCss->value;
2533            $this->_subSTATES['t']    = $this->parsingCss->table;
2534            $this->_subSTATES['ml']    = $this->_margeLeft;
2535            $this->_subSTATES['mr']    = $this->_margeRight;
2536            $this->_subSTATES['mt']    = $this->_margeTop;
2537            $this->_subSTATES['mb']    = $this->_margeBottom;
2538            $this->_subSTATES['mp']    = $this->_pageMarges;
2539
2540            // new stat for the footer
2541            $this->_pageMarges  = array();
2542            $this->_margeLeft   = $this->_defaultLeft;
2543            $this->_margeRight  = $this->_defaultRight;
2544            $this->_margeTop    = $this->_defaultTop;
2545            $this->_margeBottom = $this->_defaultBottom;
2546            $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2547            $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2548            $this->pdf->setXY($this->_defaultLeft, $this->_defaultTop);
2549
2550            $this->parsingCss->initStyle();
2551            $this->parsingCss->resetStyle();
2552            $this->parsingCss->value['width']    = $this->pdf->getW() - $this->_defaultLeft - $this->_defaultRight;
2553            $this->parsingCss->table                = array();
2554
2555            // we create a sub HTML2PFDF, and we execute on it the content of the footer, to get the height of it
2556            $sub = null;
2557            $this->_createSubHTML($sub);
2558            $sub->parsingHtml->code = $this->parsingHtml->getLevel($this->_parsePos);
2559            $sub->_makeHTMLcode();
2560            $this->pdf->setY($this->pdf->getH() - $sub->_maxY - $this->_defaultBottom - 0.01);
2561            $this->_destroySubHTML($sub);
2562
2563            $this->parsingCss->save();
2564            $this->parsingCss->analyse('page_footer_sub', $param);
2565            $this->parsingCss->setPosition();
2566            $this->parsingCss->fontSet();
2567            $this->_setNewPositionForNewLine();
2568
2569            return true;
2570        }
2571
2572        /**
2573         * It is not a real tag. Does not use it directly
2574         *
2575         * @param  array $param
2576         * @return boolean
2577         */
2578        protected function _tag_close_PAGE_FOOTER_SUB($param)
2579        {
2580            if ($this->_isForOneLine) return false;
2581
2582            $this->parsingCss->load();
2583
2584            $this->parsingCss->value                = $this->_subSTATES['s'];
2585            $this->parsingCss->table                = $this->_subSTATES['t'];
2586            $this->_pageMarges                 = $this->_subSTATES['mp'];
2587            $this->_margeLeft                = $this->_subSTATES['ml'];
2588            $this->_margeRight                = $this->_subSTATES['mr'];
2589            $this->_margeTop                    = $this->_subSTATES['mt'];
2590            $this->_margeBottom                = $this->_subSTATES['mb'];
2591            $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2592            $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2593            $this->pdf->setXY($this->_subSTATES['x'], $this->_subSTATES['y']);
2594
2595            $this->parsingCss->fontSet();
2596            $this->_maxH = 0;
2597
2598            return true;
2599        }
2600
2601        /**
2602         * tag : NOBREAK
2603         * mode : OPEN
2604         *
2605         * @param  array $param
2606         * @return boolean
2607         */
2608        protected function _tag_open_NOBREAK($param)
2609        {
2610            if ($this->_isForOneLine) return false;
2611
2612            $this->_maxH = 0;
2613
2614            // create a sub HTML2PDF to execute the content of the tag, to get the dimensions
2615            $sub = null;
2616            $this->_createSubHTML($sub);
2617            $sub->parsingHtml->code = $this->parsingHtml->getLevel($this->_parsePos);
2618            $sub->_makeHTMLcode();
2619            $y = $this->pdf->getY();
2620
2621            // if the content does not fit on the page => new page
2622            if (
2623                $sub->_maxY < ($this->pdf->getH() - $this->pdf->gettMargin()-$this->pdf->getbMargin()) &&
2624                $y + $sub->_maxY>=($this->pdf->getH() - $this->pdf->getbMargin())
2625            ) {
2626                $this->_setNewPage();
2627            }
2628
2629            // destroy the sub HTML2PDF
2630            $this->_destroySubHTML($sub);
2631
2632            return true;
2633        }
2634
2635
2636        /**
2637         * tag : NOBREAK
2638         * mode : CLOSE
2639         *
2640         * @param  array $param
2641         * @return boolean
2642         */
2643        protected function _tag_close_NOBREAK($param)
2644        {
2645            if ($this->_isForOneLine) return false;
2646
2647            $this->_maxH = 0;
2648
2649            return true;
2650        }
2651
2652        /**
2653         * tag : DIV
2654         * mode : OPEN
2655         *
2656         * @param  array $param
2657         * @param  string $other name of tag that used the div tag
2658         * @return boolean
2659         */
2660        protected function _tag_open_DIV($param, $other = 'div')
2661        {
2662            if ($this->_isForOneLine) return false;
2663            if ($this->_debugActif) $this->_DEBUG_add(strtoupper($other), true);
2664
2665            $this->parsingCss->save();
2666            $this->parsingCss->analyse($other, $param);
2667            $this->parsingCss->fontSet();
2668
2669            // for fieldset and legend
2670            if (in_array($other, array('fieldset', 'legend'))) {
2671                if (isset($param['moveTop']))  $this->parsingCss->value['margin']['t']    += $param['moveTop'];
2672                if (isset($param['moveLeft'])) $this->parsingCss->value['margin']['l']    += $param['moveLeft'];
2673                if (isset($param['moveDown'])) $this->parsingCss->value['margin']['b']    += $param['moveDown'];
2674            }
2675
2676            $alignObject = null;
2677            if ($this->parsingCss->value['margin-auto']) $alignObject = 'center';
2678
2679            $marge = array();
2680            $marge['l'] = $this->parsingCss->value['border']['l']['width'] + $this->parsingCss->value['padding']['l']+0.03;
2681            $marge['r'] = $this->parsingCss->value['border']['r']['width'] + $this->parsingCss->value['padding']['r']+0.03;
2682            $marge['t'] = $this->parsingCss->value['border']['t']['width'] + $this->parsingCss->value['padding']['t']+0.03;
2683            $marge['b'] = $this->parsingCss->value['border']['b']['width'] + $this->parsingCss->value['padding']['b']+0.03;
2684
2685            // extract the content of the div
2686            $level = $this->parsingHtml->getLevel($this->_parsePos);
2687
2688            // create a sub HTML2PDF to get the dimensions of the content of the div
2689            $w = 0; $h = 0;
2690            if (count($level)) {
2691                $sub = null;
2692                $this->_createSubHTML($sub);
2693                $sub->parsingHtml->code = $level;
2694                $sub->_makeHTMLcode();
2695                $w = $sub->_maxX;
2696                $h = $sub->_maxY;
2697                $this->_destroySubHTML($sub);
2698            }
2699            $wReel = $w;
2700            $hReel = $h;
2701
2702            $w+= $marge['l']+$marge['r']+0.001;
2703            $h+= $marge['t']+$marge['b']+0.001;
2704
2705            if ($this->parsingCss->value['overflow']=='hidden') {
2706                $overW = max($w, $this->parsingCss->value['width']);
2707                $overH = max($h, $this->parsingCss->value['height']);
2708                $overflow = true;
2709                $this->parsingCss->value['old_maxX'] = $this->_maxX;
2710                $this->parsingCss->value['old_maxY'] = $this->_maxY;
2711                $this->parsingCss->value['old_maxH'] = $this->_maxH;
2712                $this->parsingCss->value['old_overflow'] = $this->_isInOverflow;
2713                $this->_isInOverflow = true;
2714            } else {
2715                $overW = null;
2716                $overH = null;
2717                $overflow = false;
2718                $this->parsingCss->value['width']    = max($w, $this->parsingCss->value['width']);
2719                $this->parsingCss->value['height']    = max($h, $this->parsingCss->value['height']);
2720            }
2721
2722            switch($this->parsingCss->value['rotate'])
2723            {
2724                case 90:
2725                    $tmp = $overH; $overH = $overW; $overW = $tmp;
2726                    $tmp = $hReel; $hReel = $wReel; $wReel = $tmp;
2727                    unset($tmp);
2728                    $w = $this->parsingCss->value['height'];
2729                    $h = $this->parsingCss->value['width'];
2730                    $tX =-$h;
2731                    $tY = 0;
2732                    break;
2733
2734                case 180:
2735                    $w = $this->parsingCss->value['width'];
2736                    $h = $this->parsingCss->value['height'];
2737                    $tX = -$w;
2738                    $tY = -$h;
2739                    break;
2740
2741                case 270:
2742                    $tmp = $overH; $overH = $overW; $overW = $tmp;
2743                    $tmp = $hReel; $hReel = $wReel; $wReel = $tmp;
2744                    unset($tmp);
2745                    $w = $this->parsingCss->value['height'];
2746                    $h = $this->parsingCss->value['width'];
2747                    $tX = 0;
2748                    $tY =-$w;
2749                    break;
2750
2751                default:
2752                    $w = $this->parsingCss->value['width'];
2753                    $h = $this->parsingCss->value['height'];
2754                    $tX = 0;
2755                    $tY = 0;
2756                    break;
2757            }
2758
2759            if (!$this->parsingCss->value['position']) {
2760                if (
2761                    $w < ($this->pdf->getW() - $this->pdf->getlMargin()-$this->pdf->getrMargin()) &&
2762                    $this->pdf->getX() + $w>=($this->pdf->getW() - $this->pdf->getrMargin())
2763                    )
2764                    $this->_tag_open_BR(array());
2765
2766                if (
2767                        ($h < ($this->pdf->getH() - $this->pdf->gettMargin()-$this->pdf->getbMargin())) &&
2768                        ($this->pdf->getY() + $h>=($this->pdf->getH() - $this->pdf->getbMargin())) &&
2769                        !$this->_isInOverflow
2770                    )
2771                    $this->_setNewPage();
2772
2773                $old = $this->parsingCss->getOldValues();
2774                $parentWidth = $old['width'] ? $old['width'] : $this->pdf->getW() - $this->pdf->getlMargin() - $this->pdf->getrMargin();
2775
2776                if ($parentWidth>$w) {
2777                    if ($alignObject=='center')        $this->pdf->setX($this->pdf->getX() + ($parentWidth-$w)*0.5);
2778                    else if ($alignObject=='right')    $this->pdf->setX($this->pdf->getX() + $parentWidth-$w);
2779                }
2780
2781                $this->parsingCss->setPosition();
2782            } else {
2783                $old = $this->parsingCss->getOldValues();
2784                $parentWidth = $old['width'] ? $old['width'] : $this->pdf->getW() - $this->pdf->getlMargin() - $this->pdf->getrMargin();
2785
2786                if ($parentWidth>$w) {
2787                    if ($alignObject=='center')        $this->pdf->setX($this->pdf->getX() + ($parentWidth-$w)*0.5);
2788                    else if ($alignObject=='right')    $this->pdf->setX($this->pdf->getX() + $parentWidth-$w);
2789                }
2790
2791                $this->parsingCss->setPosition();
2792                $this->_saveMax();
2793                $this->_maxX = 0;
2794                $this->_maxY = 0;
2795                $this->_maxH = 0;
2796                $this->_maxE = 0;
2797            }
2798
2799            if ($this->parsingCss->value['rotate']) {
2800                $this->pdf->startTransform();
2801                $this->pdf->setRotation($this->parsingCss->value['rotate']);
2802                $this->pdf->setTranslate($tX, $tY);
2803            }
2804
2805            $this->_drawRectangle(
2806                $this->parsingCss->value['x'],
2807                $this->parsingCss->value['y'],
2808                $this->parsingCss->value['width'],
2809                $this->parsingCss->value['height'],
2810                $this->parsingCss->value['border'],
2811                $this->parsingCss->value['padding'],
2812                0,
2813                $this->parsingCss->value['background']
2814            );
2815
2816            $marge = array();
2817            $marge['l'] = $this->parsingCss->value['border']['l']['width'] + $this->parsingCss->value['padding']['l']+0.03;
2818            $marge['r'] = $this->parsingCss->value['border']['r']['width'] + $this->parsingCss->value['padding']['r']+0.03;
2819            $marge['t'] = $this->parsingCss->value['border']['t']['width'] + $this->parsingCss->value['padding']['t']+0.03;
2820            $marge['b'] = $this->parsingCss->value['border']['b']['width'] + $this->parsingCss->value['padding']['b']+0.03;
2821
2822            $this->parsingCss->value['width'] -= $marge['l']+$marge['r'];
2823            $this->parsingCss->value['height']-= $marge['t']+$marge['b'];
2824
2825            $xCorr = 0;
2826            $yCorr = 0;
2827            if (!$this->_subPart && !$this->_isSubPart) {
2828                switch($this->parsingCss->value['text-align'])
2829                {
2830                    case 'right':
2831                        $xCorr = ($this->parsingCss->value['width']-$wReel);
2832                        break;
2833                    case 'center':
2834                        $xCorr = ($this->parsingCss->value['width']-$wReel)*0.5;
2835                        break;
2836                }
2837                if ($xCorr>0) $xCorr=0;
2838                switch($this->parsingCss->value['vertical-align'])
2839                {
2840                    case 'bottom':
2841                        $yCorr = ($this->parsingCss->value['height']-$hReel);
2842                        break;
2843                    case 'middle':
2844                        $yCorr = ($this->parsingCss->value['height']-$hReel)*0.5;
2845                        break;
2846                }
2847            }
2848
2849            if ($overflow) {
2850                $overW-= $marge['l']+$marge['r'];
2851                $overH-= $marge['t']+$marge['b'];
2852                $this->pdf->clippingPathStart(
2853                    $this->parsingCss->value['x']+$marge['l'],
2854                    $this->parsingCss->value['y']+$marge['t'],
2855                    $this->parsingCss->value['width'],
2856                    $this->parsingCss->value['height']
2857                );
2858
2859                $this->parsingCss->value['x']+= $xCorr;
2860
2861                // marges from the dimension of the content
2862                $mL = $this->parsingCss->value['x']+$marge['l'];
2863                $mR = $this->pdf->getW() - $mL - $overW;
2864            } else {
2865                // marges from the dimension of the div
2866                $mL = $this->parsingCss->value['x']+$marge['l'];
2867                $mR = $this->pdf->getW() - $mL - $this->parsingCss->value['width'];
2868            }
2869
2870            $x = $this->parsingCss->value['x']+$marge['l'];
2871            $y = $this->parsingCss->value['y']+$marge['t']+$yCorr;
2872            $this->_saveMargin($mL, 0, $mR);
2873            $this->pdf->setXY($x, $y);
2874
2875            $this->_setNewPositionForNewLine();
2876
2877            return true;
2878        }
2879
2880        /**
2881         * tag : BLOCKQUOTE
2882         * mode : OPEN
2883         *
2884         * @param  array $param
2885         * @return boolean
2886         */
2887        protected function _tag_open_BLOCKQUOTE($param)
2888        {
2889            return $this->_tag_open_DIV($param, 'blockquote');
2890        }
2891
2892        /**
2893         * tag : LEGEND
2894         * mode : OPEN
2895         *
2896         * @param  array $param
2897         * @return boolean
2898         */
2899        protected function _tag_open_LEGEND($param)
2900        {
2901            return $this->_tag_open_DIV($param, 'legend');
2902        }
2903
2904        /**
2905         * tag : FIELDSET
2906         * mode : OPEN
2907         *
2908         * @author Pavel Kochman
2909         * @param  array $param
2910         * @return boolean
2911         */
2912        protected function _tag_open_FIELDSET($param)
2913        {
2914
2915            $this->parsingCss->save();
2916            $this->parsingCss->analyse('fieldset', $param);
2917
2918            // get height of LEGEND element and make fieldset corrections
2919            for ($tempPos = $this->_parsePos + 1; $tempPos<count($this->parsingHtml->code); $tempPos++) {
2920                $action = $this->parsingHtml->code[$tempPos];
2921                if ($action['name'] == 'fieldset') break;
2922                if ($action['name'] == 'legend' && !$action['close']) {
2923                    $legendOpenPos = $tempPos;
2924
2925                    $sub = null;
2926                    $this->_createSubHTML($sub);
2927                    $sub->parsingHtml->code = $this->parsingHtml->getLevel($tempPos - 1);
2928
2929                    $res = null;
2930                    for ($sub->_parsePos = 0; $sub->_parsePos<count($sub->parsingHtml->code); $sub->_parsePos++) {
2931                        $action = $sub->parsingHtml->code[$sub->_parsePos];
2932                        $sub->_executeAction($action);
2933
2934                        if ($action['name'] == 'legend' && $action['close'])
2935                            break;
2936                    }
2937
2938                    $legendH = $sub->_maxY;
2939                    $this->_destroySubHTML($sub);
2940
2941                    $move = $this->parsingCss->value['padding']['t'] + $this->parsingCss->value['border']['t']['width'] + 0.03;
2942
2943                    $param['moveTop'] = $legendH / 2;
2944
2945                    $this->parsingHtml->code[$legendOpenPos]['param']['moveTop'] = - ($legendH / 2 + $move);
2946                    $this->parsingHtml->code[$legendOpenPos]['param']['moveLeft'] = 2 - $this->parsingCss->value['border']['l']['width'] - $this->parsingCss->value['padding']['l'];
2947                    $this->parsingHtml->code[$legendOpenPos]['param']['moveDown'] = $move;
2948                    break;
2949                }
2950            }
2951            $this->parsingCss->load();
2952
2953            return $this->_tag_open_DIV($param, 'fieldset');
2954        }
2955
2956        /**
2957         * tag : DIV
2958         * mode : CLOSE
2959         *
2960         * @param  array $param
2961         * @param  string $other name of tag that used the div tag
2962         * @return boolean
2963         */
2964        protected function _tag_close_DIV($param, $other='div')
2965        {
2966            if ($this->_isForOneLine) return false;
2967
2968            if ($this->parsingCss->value['overflow']=='hidden') {
2969                $this->_maxX = $this->parsingCss->value['old_maxX'];
2970                $this->_maxY = $this->parsingCss->value['old_maxY'];
2971                $this->_maxH = $this->parsingCss->value['old_maxH'];
2972                $this->_isInOverflow = $this->parsingCss->value['old_overflow'];
2973                $this->pdf->clippingPathStop();
2974            }
2975
2976            if ($this->parsingCss->value['rotate'])
2977                $this->pdf->stopTransform();
2978
2979            $marge = array();
2980            $marge['l'] = $this->parsingCss->value['border']['l']['width'] + $this->parsingCss->value['padding']['l']+0.03;
2981            $marge['r'] = $this->parsingCss->value['border']['r']['width'] + $this->parsingCss->value['padding']['r']+0.03;
2982            $marge['t'] = $this->parsingCss->value['border']['t']['width'] + $this->parsingCss->value['padding']['t']+0.03;
2983            $marge['b'] = $this->parsingCss->value['border']['b']['width'] + $this->parsingCss->value['padding']['b']+0.03;
2984
2985            $x = $this->parsingCss->value['x'];
2986            $y = $this->parsingCss->value['y'];
2987            $w = $this->parsingCss->value['width']+$marge['l']+$marge['r']+$this->parsingCss->value['margin']['r'];
2988            $h = $this->parsingCss->value['height']+$marge['t']+$marge['b']+$this->parsingCss->value['margin']['b'];
2989
2990            switch($this->parsingCss->value['rotate'])
2991            {
2992                case 90:
2993                    $t = $w; $w = $h; $h = $t;
2994                    break;
2995
2996                case 270:
2997                    $t = $w; $w = $h; $h = $t;
2998                    break;
2999
3000                default:
3001                    break;
3002            }
3003
3004
3005            if ($this->parsingCss->value['position']!='absolute') {
3006                $this->pdf->setXY($x+$w, $y);
3007
3008                $this->_maxX = max($this->_maxX, $x+$w);
3009                $this->_maxY = max($this->_maxY, $y+$h);
3010                $this->_maxH = max($this->_maxH, $h);
3011            } else {
3012                $this->pdf->setXY($this->parsingCss->value['xc'], $this->parsingCss->value['yc']);
3013
3014                $this->_loadMax();
3015            }
3016
3017            $block = ($this->parsingCss->value['display']!='inline' && $this->parsingCss->value['position']!='absolute');
3018
3019            $this->parsingCss->load();
3020            $this->parsingCss->fontSet();
3021            $this->_loadMargin();
3022
3023            if ($block) $this->_tag_open_BR(array());
3024            if ($this->_debugActif) $this->_DEBUG_add(strtoupper($other), false);
3025
3026            return true;
3027        }
3028
3029        /**
3030         * tag : BLOCKQUOTE
3031         * mode : CLOSE
3032         *
3033         * @param  array $param
3034         * @return boolean
3035         */
3036        protected function _tag_close_BLOCKQUOTE($param)
3037        {
3038            return $this->_tag_close_DIV($param, 'blockquote');
3039        }
3040
3041        /**
3042         * tag : FIELDSET
3043         * mode : CLOSE
3044         *
3045         * @param  array $param
3046         * @return boolean
3047         */
3048        protected function _tag_close_FIELDSET($param)
3049        {
3050            return $this->_tag_close_DIV($param, 'fieldset');
3051        }
3052
3053        /**
3054         * tag : LEGEND
3055         * mode : CLOSE
3056         *
3057         * @param  array $param
3058         * @return boolean
3059         */
3060        protected function _tag_close_LEGEND($param)
3061        {
3062            return $this->_tag_close_DIV($param, 'legend');
3063        }
3064
3065        /**
3066         * tag : BARCODE
3067         * mode : OPEN
3068         *
3069         * @param  array $param
3070         * @return boolean
3071         */
3072        protected function _tag_open_BARCODE($param)
3073        {
3074            // for  compatibility with old versions < 3.29
3075            $lstBarcode = array();
3076            $lstBarcode['UPC_A']  =    'UPCA';
3077            $lstBarcode['CODE39'] =    'C39';
3078
3079            if (!isset($param['type']))     $param['type'] = 'C39';
3080            if (!isset($param['value']))    $param['value']    = 0;
3081            if (!isset($param['label']))    $param['label']    = 'label';
3082            if (!isset($param['style']['color'])) $param['style']['color'] = '#000000';
3083
3084            if ($this->_testIsDeprecated && (isset($param['bar_h']) || isset($param['bar_w'])))
3085                throw new HTML2PDF_exception(9, array('BARCODE', 'bar_h, bar_w'));
3086
3087            $param['type'] = strtoupper($param['type']);
3088            if (isset($lstBarcode[$param['type']])) $param['type'] = $lstBarcode[$param['type']];
3089
3090            $this->parsingCss->save();
3091            $this->parsingCss->analyse('barcode', $param);
3092            $this->parsingCss->setPosition();
3093            $this->parsingCss->fontSet();
3094
3095            $x = $this->pdf->getX();
3096            $y = $this->pdf->getY();
3097            $w = $this->parsingCss->value['width'];    if (!$w) $w = $this->parsingCss->ConvertToMM('50mm');
3098            $h = $this->parsingCss->value['height'];    if (!$h) $h = $this->parsingCss->ConvertToMM('10mm');
3099            $txt = ($param['label']!=='none' ? $this->parsingCss->value['font-size'] : false);
3100            $c = $this->parsingCss->value['color'];
3101            $infos = $this->pdf->myBarcode($param['value'], $param['type'], $x, $y, $w, $h, $txt, $c);
3102
3103            $this->_maxX = max($this->_maxX, $x+$infos[0]);
3104            $this->_maxY = max($this->_maxY, $y+$infos[1]);
3105            $this->_maxH = max($this->_maxH, $infos[1]);
3106            $this->_maxE++;
3107
3108            $this->pdf->setXY($x+$infos[0], $y);
3109
3110            $this->parsingCss->load();
3111            $this->parsingCss->fontSet();
3112
3113            return true;
3114        }
3115
3116        /**
3117         * tag : BARCODE
3118         * mode : CLOSE
3119         *
3120         * @param  array $param
3121         * @return boolean
3122         */
3123        protected function _tag_close_BARCODE($param)
3124        {
3125            // there is nothing to do here
3126
3127            return true;
3128        }
3129
3130        /**
3131         * tag : QRCODE
3132         * mode : OPEN
3133         *
3134         * @param  array $param
3135         * @return boolean
3136         */
3137        protected function _tag_open_QRCODE($param)
3138        {
3139            if ($this->_testIsDeprecated && (isset($param['size']) || isset($param['noborder'])))
3140                throw new HTML2PDF_exception(9, array('QRCODE', 'size, noborder'));
3141
3142            if ($this->_debugActif) $this->_DEBUG_add('QRCODE');
3143
3144            if (!isset($param['value']))                     $param['value'] = '';
3145            if (!isset($param['ec']))                        $param['ec'] = 'H';
3146            if (!isset($param['style']['color']))            $param['style']['color'] = '#000000';
3147            if (!isset($param['style']['background-color'])) $param['style']['background-color'] = '#FFFFFF';
3148            if (isset($param['style']['border'])) {
3149                $borders = $param['style']['border']!='none';
3150                unset($param['style']['border']);
3151            } else {
3152                $borders = true;
3153            }
3154
3155            if ($param['value']==='') return true;
3156            if (!in_array($param['ec'], array('L', 'M', 'Q', 'H'))) $param['ec'] = 'H';
3157
3158            $this->parsingCss->save();
3159            $this->parsingCss->analyse('qrcode', $param);
3160            $this->parsingCss->setPosition();
3161            $this->parsingCss->fontSet();
3162
3163            $x = $this->pdf->getX();
3164            $y = $this->pdf->getY();
3165            $w = $this->parsingCss->value['width'];
3166            $h = $this->parsingCss->value['height'];
3167            $size = max($w, $h); if (!$size) $size = $this->parsingCss->ConvertToMM('50mm');
3168
3169            $style = array(
3170                    'fgcolor' => $this->parsingCss->value['color'],
3171                    'bgcolor' => $this->parsingCss->value['background']['color'],
3172                );
3173
3174            if ($borders) {
3175                $style['border'] = true;
3176                $style['padding'] = 'auto';
3177            } else {
3178                $style['border'] = false;
3179                $style['padding'] = 0;
3180            }
3181
3182            if (!$this->_subPart && !$this->_isSubPart) {
3183                $this->pdf->write2DBarcode($param['value'], 'QRCODE,'.$param['ec'], $x, $y, $size, $size, $style);
3184            }
3185
3186            $this->_maxX = max($this->_maxX, $x+$size);
3187            $this->_maxY = max($this->_maxY, $y+$size);
3188            $this->_maxH = max($this->_maxH, $size);
3189            $this->_maxE++;
3190
3191            $this->pdf->setX($x+$size);
3192
3193            $this->parsingCss->load();
3194            $this->parsingCss->fontSet();
3195
3196            return true;
3197        }
3198
3199        /**
3200         * tag : QRCODE
3201         * mode : CLOSE
3202         *
3203         * @param  array $param
3204         * @return boolean
3205         */
3206        protected function _tag_close_QRCODE($param)
3207        {
3208            // there is nothing to do here
3209
3210            return true;
3211        }
3212
3213        /**
3214         * tag : BOOKMARK
3215         * mode : OPEN
3216         *
3217         * @param  array $param
3218         * @return boolean
3219         */
3220        protected function _tag_open_BOOKMARK($param)
3221        {
3222            $titre = isset($param['title']) ? trim($param['title']) : '';
3223            $level = isset($param['level']) ? floor($param['level']) : 0;
3224
3225            if ($level<0) $level = 0;
3226            if ($titre) $this->pdf->Bookmark($titre, $level, -1);
3227
3228            return true;
3229        }
3230
3231        /**
3232         * tag : BOOKMARK
3233         * mode : CLOSE
3234         *
3235         * @param  array $param
3236         * @return boolean
3237         */
3238        protected function _tag_close_BOOKMARK($param)
3239        {
3240            // there is nothing to do here
3241
3242            return true;
3243        }
3244
3245        /**
3246         * this is not a real TAG, it is just to write texts
3247         *
3248         * @param  array $param
3249         * @return boolean
3250         */
3251        protected function _tag_open_WRITE($param)
3252        {
3253            $fill = ($this->parsingCss->value['background']['color']!==null && $this->parsingCss->value['background']['image']===null);
3254            if (in_array($this->parsingCss->value['id_tag'], array('fieldset', 'legend', 'div', 'table', 'tr', 'td', 'th'))) {
3255                $fill = false;
3256            }
3257
3258            // get the text to write
3259            $txt = $param['txt'];
3260
3261            if ($this->_isAfterFloat) {
3262                $txt = ltrim($txt);
3263                $this->_isAfterFloat = false;
3264            }
3265
3266            $txt = str_replace('[[page_nb]]', $this->pdf->getMyAliasNbPages(), $txt);
3267            $txt = str_replace('[[page_cu]]', $this->pdf->getMyNumPage($this->_page), $txt);
3268
3269            if ($this->parsingCss->value['text-transform']!='none') {
3270                if ($this->parsingCss->value['text-transform']=='capitalize')
3271                    $txt = mb_convert_case($txt, MB_CASE_TITLE, $this->_encoding);
3272                else if ($this->parsingCss->value['text-transform']=='uppercase')
3273                    $txt = mb_convert_case($txt, MB_CASE_UPPER, $this->_encoding);
3274                else if ($this->parsingCss->value['text-transform']=='lowercase')
3275                    $txt = mb_convert_case($txt, MB_CASE_LOWER, $this->_encoding);
3276            }
3277
3278            // size of the text
3279            $h  = 1.08*$this->parsingCss->value['font-size'];
3280            $dh = $h*$this->parsingCss->value['mini-decal'];
3281            $lh = $this->parsingCss->getLineHeight();
3282
3283            // identify the align
3284            $align = 'L';
3285            if ($this->parsingCss->value['text-align']=='li_right') {
3286                $w = $this->parsingCss->value['width'];
3287                $align = 'R';
3288            }
3289
3290            // calculate the width of each words, and of all the sentence
3291            $w = 0;
3292            $words = explode(' ', $txt);
3293            foreach ($words as $k => $word) {
3294                $words[$k] = array($word, $this->pdf->GetStringWidth($word));
3295                $w+= $words[$k][1];
3296            }
3297            $space = $this->pdf->GetStringWidth(' ');
3298            $w+= $space*(count($words)-1);
3299
3300            // position in the text
3301            $currPos = 0;
3302
3303            // the bigger width of the text, after automatic break line
3304            $maxX = 0;
3305
3306            // position of the text
3307            $x = $this->pdf->getX();
3308            $y = $this->pdf->getY();
3309            $dy = $this->_getElementY($lh);
3310
3311            // margins
3312            list($left, $right) = $this->_getMargins($y);
3313
3314            // number of lines after automatic break line
3315            $nb = 0;
3316
3317            // while we have words, and the text does not fit on the line => we cut the sentence
3318            while ($x+$w>$right && $x<$right+$space && count($words)) {
3319                // adding words 1 by 1 to fit on the line
3320                $i=0;
3321                $old = array('', 0);
3322                $str = $words[0];
3323                $add = false;
3324                while (($x+$str[1])<$right) {
3325                    $i++;
3326                    $add = true;
3327
3328                    array_shift($words);
3329                    $old = $str;
3330
3331                    if (!count($words)) break;
3332                    $str[0].= ' '.$words[0][0];
3333                    $str[1]+= $space+$words[0][1];
3334                }
3335                $str = $old;
3336
3337                // if  nothing fit on the line, and if the first word does not fit on the line => the word is too long, we put it
3338                if ($i==0 && (($left+$words[0][1])>=$right)) {
3339                    $str = $words[0];
3340                    array_shift($words);
3341                    $i++;
3342                    $add = true;
3343                }
3344                $currPos+= ($currPos ? 1 : 0)+strlen($str[0]);
3345
3346                // write the extract sentence that fit on the page
3347                $wc = ($align=='L' ? $str[1] : $this->parsingCss->value['width']);
3348                if ($right - $left<$wc) $wc = $right - $left;
3349
3350                if (strlen($str[0])) {
3351                    $this->pdf->setXY($this->pdf->getX(), $y+$dh+$dy);
3352                    $this->pdf->Cell($wc, $h, $str[0], 0, 0, $align, $fill, $this->_isInLink);
3353                    $this->pdf->setXY($this->pdf->getX(), $y);
3354                }
3355                $this->_maxH = max($this->_maxH, $lh);
3356
3357                // max width
3358                $maxX = max($maxX, $this->pdf->getX());
3359
3360                // new position and new width for the "while"
3361                $w-= $str[1];
3362                $y = $this->pdf->getY();
3363                $x = $this->pdf->getX();
3364                $dy = $this->_getElementY($lh);
3365
3366                // if we have again words to write
3367                if (count($words)) {
3368                    // remove the space at the end
3369                    if ($add) $w-= $space;
3370
3371                    // if we don't add any word, and if the first word is empty => useless space to skip
3372                    if (!$add && $words[0][0]==='') {
3373                        array_shift($words);
3374                    }
3375
3376                    // if it is just to calculate for one line => adding the number of words
3377                    if ($this->_isForOneLine) {
3378                        $this->_maxE+= $i;
3379                        $this->_maxX = max($this->_maxX, $maxX);
3380                        return null;
3381                    }
3382
3383                    // automatic line break
3384                    $this->_tag_open_BR(array('style' => ''), $currPos);
3385
3386                    // new position
3387                    $y = $this->pdf->getY();
3388                    $x = $this->pdf->getX();
3389                    $dy = $this->_getElementY($lh);
3390
3391                    // if the next line does  not fit on the page => new page
3392                    if ($y + $h>=$this->pdf->getH() - $this->pdf->getbMargin()) {
3393                        if (!$this->_isInOverflow && !$this->_isInFooter) {
3394                            $this->_setNewPage(null, '', null, $currPos);
3395                            $y = $this->pdf->getY();
3396                            $x = $this->pdf->getX();
3397                            $dy = $this->_getElementY($lh);
3398                        }
3399                    }
3400
3401                    // if more than 10000 line => error
3402                    $nb++;
3403                    if ($nb>10000) {
3404                        $txt = ''; foreach ($words as $k => $word) $txt.= ($k ? ' ' : '').$word[0];
3405                        throw new HTML2PDF_exception(2, array($txt, $right-$left, $w));
3406                    }
3407
3408                    // new margins for the new line
3409                    list($left, $right) = $this->_getMargins($y);
3410                }
3411            }
3412
3413            // if we have words after automatic cut, it is because they fit on the line => we write the text
3414            if (count($words)) {
3415                $txt = ''; foreach ($words as $k => $word) $txt.= ($k ? ' ' : '').$word[0];
3416                $w+= $this->pdf->getWordSpacing()*(count($words));
3417                $this->pdf->setXY($this->pdf->getX(), $y+$dh+$dy);
3418                $this->pdf->Cell(($align=='L' ? $w : $this->parsingCss->value['width']), $h, $txt, 0, 0, $align, $fill, $this->_isInLink);
3419                $this->pdf->setXY($this->pdf->getX(), $y);
3420                $this->_maxH = max($this->_maxH, $lh);
3421                $this->_maxE+= count($words);
3422            }
3423
3424            $maxX = max($maxX, $this->pdf->getX());
3425            $maxY = $this->pdf->getY()+$h;
3426
3427            $this->_maxX = max($this->_maxX, $maxX);
3428            $this->_maxY = max($this->_maxY, $maxY);
3429
3430            return true;
3431        }
3432
3433        /**
3434         * tag : BR
3435         * mode : OPEN
3436         *
3437         * @param  array   $param
3438         * @param  integer $curr real position in the html parseur (if break line in the write of a text)
3439         * @return boolean
3440         */
3441        protected function _tag_open_BR($param, $curr = null)
3442        {
3443            if ($this->_isForOneLine) return false;
3444
3445            $h = max($this->_maxH, $this->parsingCss->getLineHeight());
3446
3447            if ($this->_maxH==0) $this->_maxY = max($this->_maxY, $this->pdf->getY()+$h);
3448
3449            $this->_makeBreakLine($h, $curr);
3450
3451            $this->_maxH = 0;
3452            $this->_maxE = 0;
3453
3454            return true;
3455        }
3456
3457        /**
3458         * tag : HR
3459         * mode : OPEN
3460         *
3461         * @param  array $param
3462         * @return boolean
3463         */
3464        protected function _tag_open_HR($param)
3465        {
3466            if ($this->_isForOneLine) return false;
3467            $oldAlign = $this->parsingCss->value['text-align'];
3468            $this->parsingCss->value['text-align'] = 'left';
3469
3470            if ($this->_maxH) $this->_tag_open_BR($param);
3471
3472            $fontSize = $this->parsingCss->value['font-size'];
3473            $this->parsingCss->value['font-size']=$fontSize*0.5; $this->_tag_open_BR($param);
3474            $this->parsingCss->value['font-size']=0;
3475
3476            $param['style']['width'] = '100%';
3477
3478            $this->parsingCss->save();
3479            $this->parsingCss->value['height']=$this->parsingCss->ConvertToMM('1mm');
3480
3481            $this->parsingCss->analyse('hr', $param);
3482            $this->parsingCss->setPosition();
3483            $this->parsingCss->fontSet();
3484
3485            $h = $this->parsingCss->value['height'];
3486            if ($h)    $h-= $this->parsingCss->value['border']['t']['width']+$this->parsingCss->value['border']['b']['width'];
3487            if ($h<=0) $h = $this->parsingCss->value['border']['t']['width']+$this->parsingCss->value['border']['b']['width'];
3488
3489            $this->_drawRectangle($this->pdf->getX(), $this->pdf->getY(), $this->parsingCss->value['width'], $h, $this->parsingCss->value['border'], 0, 0, $this->parsingCss->value['background']);
3490            $this->_maxH = $h;
3491
3492            $this->parsingCss->load();
3493            $this->parsingCss->fontSet();
3494
3495            $this->_tag_open_BR($param);
3496
3497            $this->parsingCss->value['font-size']=$fontSize*0.5; $this->_tag_open_BR($param);
3498            $this->parsingCss->value['font-size']=$fontSize;
3499
3500            $this->parsingCss->value['text-align'] = $oldAlign;
3501            $this->_setNewPositionForNewLine();
3502
3503            return true;
3504        }
3505
3506        /**
3507         * tag : B
3508         * mode : OPEN
3509         *
3510         * @param  array $param
3511         * @param  string $other
3512         * @return boolean
3513         */
3514        protected function _tag_open_B($param, $other = 'b')
3515        {
3516            $this->parsingCss->save();
3517            $this->parsingCss->value['font-bold'] = true;
3518            $this->parsingCss->analyse($other, $param);
3519            $this->parsingCss->setPosition();
3520            $this->parsingCss->fontSet();
3521
3522            return true;
3523        }
3524
3525        /**
3526         * tag : STRONG
3527         * mode : OPEN
3528         *
3529         * @param  array $param
3530         * @return boolean
3531         */
3532        protected function _tag_open_STRONG($param)
3533        {
3534            return $this->_tag_open_B($param, 'strong');
3535        }
3536
3537        /**
3538         * tag : B
3539         * mode : CLOSE
3540         *
3541         * @param    array $param
3542         * @return boolean
3543         */
3544        protected function _tag_close_B($param)
3545        {
3546            $this->parsingCss->load();
3547            $this->parsingCss->fontSet();
3548
3549            return true;
3550        }
3551
3552        /**
3553         * tag : STRONG
3554         * mode : CLOSE
3555         *
3556         * @param  array $param
3557         * @return boolean
3558         */
3559        protected function _tag_close_STRONG($param)
3560        {
3561            return $this->_tag_close_B($param);
3562        }
3563
3564        /**
3565         * tag : I
3566         * mode : OPEN
3567         *
3568         * @param  array $param
3569         * @param  string $other
3570         * @return boolean
3571         */
3572        protected function _tag_open_I($param, $other = 'i')
3573        {
3574            $this->parsingCss->save();
3575            $this->parsingCss->value['font-italic'] = true;
3576            $this->parsingCss->analyse($other, $param);
3577            $this->parsingCss->setPosition();
3578            $this->parsingCss->fontSet();
3579
3580            return true;
3581        }
3582
3583        /**
3584         * tag : ADDRESS
3585         * mode : OPEN
3586         *
3587         * @param  array $param
3588         * @return boolean
3589         */
3590        protected function _tag_open_ADDRESS($param)
3591        {
3592            return $this->_tag_open_I($param, 'address');
3593        }
3594
3595        /**
3596         * tag : CITE
3597         * mode : OPEN
3598         *
3599         * @param  array $param
3600         * @return boolean
3601         */
3602        protected function _tag_open_CITE($param)
3603        {
3604            return $this->_tag_open_I($param, 'cite');
3605        }
3606
3607        /**
3608         * tag : EM
3609         * mode : OPEN
3610         *
3611         * @param  array $param
3612         * @return boolean
3613         */
3614        protected function _tag_open_EM($param)
3615        {
3616            return $this->_tag_open_I($param, 'em');
3617        }
3618
3619        /**
3620         * tag : SAMP
3621         * mode : OPEN
3622         *
3623         * @param  array $param
3624         * @return boolean
3625         */
3626        protected function _tag_open_SAMP($param)
3627        {
3628            return $this->_tag_open_I($param, 'samp');
3629        }
3630
3631        /**
3632         * tag : I
3633         * mode : CLOSE
3634         *
3635         * @param  array $param
3636         * @return boolean
3637         */
3638        protected function _tag_close_I($param)
3639        {
3640            $this->parsingCss->load();
3641            $this->parsingCss->fontSet();
3642
3643            return true;
3644        }
3645
3646        /**
3647         * tag : ADDRESS
3648         * mode : CLOSE
3649         *
3650         * @param  array $param
3651         * @return boolean
3652         */
3653        protected function _tag_close_ADDRESS($param)
3654        {
3655            return $this->_tag_close_I($param);
3656        }
3657
3658        /**
3659         * tag : CITE
3660         * mode : CLOSE
3661         *
3662         * @param  array $param
3663         * @return boolean
3664         */
3665        protected function _tag_close_CITE($param)
3666        {
3667            return $this->_tag_close_I($param);
3668        }
3669
3670        /**
3671         * tag : EM
3672         * mode : CLOSE
3673         *
3674         * @param  array $param
3675         * @return boolean
3676         */
3677        protected function _tag_close_EM($param)
3678        {
3679            return $this->_tag_close_I($param);
3680        }
3681
3682        /**
3683         * tag : SAMP
3684         * mode : CLOSE
3685         *
3686         * @param  array $param
3687         * @return boolean
3688         */
3689        protected function _tag_close_SAMP($param)
3690        {
3691            return $this->_tag_close_I($param);
3692        }
3693
3694        /**
3695         * tag : S
3696         * mode : OPEN
3697         *
3698         * @param  array $param
3699         * @param  string $other
3700         * @return boolean
3701         */
3702        protected function _tag_open_S($param, $other = 's')
3703        {
3704            $this->parsingCss->save();
3705            $this->parsingCss->value['font-linethrough'] = true;
3706            $this->parsingCss->analyse($other, $param);
3707            $this->parsingCss->setPosition();
3708            $this->parsingCss->fontSet();
3709
3710            return true;
3711        }
3712
3713        /**
3714         * tag : DEL
3715         * mode : OPEN
3716         *
3717         * @param  array $param
3718         * @return boolean
3719         */
3720        protected function _tag_open_DEL($param)
3721        {
3722            return $this->_tag_open_S($param, 'del');
3723        }
3724
3725        /**
3726         * tag : S
3727         * mode : CLOSE
3728         *
3729         * @param    array $param
3730         * @return boolean
3731         */
3732        protected function _tag_close_S($param)
3733        {
3734            $this->parsingCss->load();
3735            $this->parsingCss->fontSet();
3736
3737            return true;
3738        }
3739
3740        /**
3741         * tag : DEL
3742         * mode : CLOSE
3743         *
3744         * @param  array $param
3745         * @return boolean
3746         */
3747        protected function _tag_close_DEL($param)
3748        {
3749            return $this->_tag_close_S($param);
3750        }
3751
3752        /**
3753         * tag : U
3754         * mode : OPEN
3755         *
3756         * @param  array $param
3757         * @param  string $other
3758         * @return boolean
3759         */
3760        protected function _tag_open_U($param, $other='u')
3761        {
3762            $this->parsingCss->save();
3763            $this->parsingCss->value['font-underline'] = true;
3764            $this->parsingCss->analyse($other, $param);
3765            $this->parsingCss->setPosition();
3766            $this->parsingCss->fontSet();
3767
3768            return true;
3769        }
3770
3771        /**
3772         * tag : INS
3773         * mode : OPEN
3774         *
3775         * @param  array $param
3776         * @return boolean
3777         */
3778        protected function _tag_open_INS($param)
3779        {
3780            return $this->_tag_open_U($param, 'ins');
3781        }
3782
3783        /**
3784         * tag : U
3785         * mode : CLOSE
3786         *
3787         * @param    array $param
3788         * @return boolean
3789         */
3790        protected function _tag_close_U($param)
3791        {
3792            $this->parsingCss->load();
3793            $this->parsingCss->fontSet();
3794
3795            return true;
3796        }
3797
3798        /**
3799         * tag : INS
3800         * mode : CLOSE
3801         *
3802         * @param  array $param
3803         * @return boolean
3804         */
3805        protected function _tag_close_INS($param)
3806        {
3807            return $this->_tag_close_U($param);
3808        }
3809
3810        /**
3811         * tag : A
3812         * mode : OPEN
3813         *
3814         * @param  array $param
3815         * @return boolean
3816         */
3817        protected function _tag_open_A($param)
3818        {
3819            $this->_isInLink = str_replace('&amp;', '&', isset($param['href']) ? $param['href'] : '');
3820
3821            if (isset($param['name'])) {
3822                $name =     $param['name'];
3823                if (!isset($this->_lstAnchor[$name])) $this->_lstAnchor[$name] = array($this->pdf->AddLink(), false);
3824
3825                if (!$this->_lstAnchor[$name][1]) {
3826                    $this->_lstAnchor[$name][1] = true;
3827                    $this->pdf->SetLink($this->_lstAnchor[$name][0], -1, -1);
3828                }
3829            }
3830
3831            if (preg_match('/^#([^#]+)$/isU', $this->_isInLink, $match)) {
3832                $name = $match[1];
3833                if (!isset($this->_lstAnchor[$name])) $this->_lstAnchor[$name] = array($this->pdf->AddLink(), false);
3834
3835                $this->_isInLink = $this->_lstAnchor[$name][0];
3836            }
3837
3838            $this->parsingCss->save();
3839            $this->parsingCss->value['font-underline'] = true;
3840            $this->parsingCss->value['color'] = array(20, 20, 250);
3841            $this->parsingCss->analyse('a', $param);
3842            $this->parsingCss->setPosition();
3843            $this->parsingCss->fontSet();
3844
3845            return true;
3846        }
3847
3848        /**
3849         * tag : A
3850         * mode : CLOSE
3851         *
3852         * @param  array $param
3853         * @return boolean
3854         */
3855        protected function _tag_close_A($param)
3856        {
3857            $this->_isInLink    = '';
3858            $this->parsingCss->load();
3859            $this->parsingCss->fontSet();
3860
3861            return true;
3862        }
3863
3864        /**
3865         * tag : H1
3866         * mode : OPEN
3867         *
3868         * @param  array $param
3869         * @param  string $other
3870         * @return boolean
3871         */
3872        protected function _tag_open_H1($param, $other = 'h1')
3873        {
3874            if ($this->_isForOneLine) return false;
3875
3876            if ($this->_maxH) $this->_tag_open_BR(array());
3877            $this->parsingCss->save();
3878            $this->parsingCss->value['font-bold'] = true;
3879
3880            $size = array('h1' => '28px', 'h2' => '24px', 'h3' => '20px', 'h4' => '16px', 'h5' => '12px', 'h6' => '9px');
3881            $this->parsingCss->value['margin']['l'] = 0;
3882            $this->parsingCss->value['margin']['r'] = 0;
3883            $this->parsingCss->value['margin']['t'] = $this->parsingCss->ConvertToMM('16px');
3884            $this->parsingCss->value['margin']['b'] = $this->parsingCss->ConvertToMM('16px');
3885            $this->parsingCss->value['font-size'] = $this->parsingCss->ConvertToMM($size[$other]);
3886
3887            $this->parsingCss->analyse($other, $param);
3888            $this->parsingCss->setPosition();
3889            $this->parsingCss->fontSet();
3890            $this->_setNewPositionForNewLine();
3891
3892            return true;
3893        }
3894
3895        /**
3896         * tag : H2
3897         * mode : OPEN
3898         *
3899         * @param  array $param
3900         * @return boolean
3901         */
3902        protected function _tag_open_H2($param)
3903        {
3904            return $this->_tag_open_H1($param, 'h2');
3905        }
3906
3907        /**
3908         * tag : H3
3909         * mode : OPEN
3910         *
3911         * @param  array $param
3912         * @return boolean
3913         */
3914        protected function _tag_open_H3($param)
3915        {
3916            return $this->_tag_open_H1($param, 'h3');
3917        }
3918
3919        /**
3920         * tag : H4
3921         * mode : OPEN
3922         *
3923         * @param  array $param
3924         * @return boolean
3925         */
3926        protected function _tag_open_H4($param)
3927        {
3928            return $this->_tag_open_H1($param, 'h4');
3929        }
3930
3931        /**
3932         * tag : H5
3933         * mode : OPEN
3934         *
3935         * @param  array $param
3936         * @return boolean
3937         */
3938        protected function _tag_open_H5($param)
3939        {
3940            return $this->_tag_open_H1($param, 'h5');
3941        }
3942
3943        /**
3944         * tag : H6
3945         * mode : OPEN
3946         *
3947         * @param  array $param
3948         * @return boolean
3949         */
3950        protected function _tag_open_H6($param)
3951        {
3952            return $this->_tag_open_H1($param, 'h6');
3953        }
3954
3955        /**
3956         * tag : H1
3957         * mode : CLOSE
3958         *
3959         * @param  array $param
3960         * @return boolean
3961         */
3962        protected function _tag_close_H1($param)
3963        {
3964            if ($this->_isForOneLine) return false;
3965
3966            $this->_maxH+= $this->parsingCss->value['margin']['b'];
3967            $h = max($this->_maxH, $this->parsingCss->getLineHeight());
3968
3969            $this->parsingCss->load();
3970            $this->parsingCss->fontSet();
3971
3972            $this->_makeBreakLine($h);
3973            $this->_maxH = 0;
3974
3975            $this->_maxY = max($this->_maxY, $this->pdf->getY());
3976
3977            return true;
3978        }
3979
3980        /**
3981         * tag : H2
3982         * mode : CLOSE
3983         *
3984         * @param  array $param
3985         * @return boolean
3986         */
3987        protected function _tag_close_H2($param)
3988        {
3989            return $this->_tag_close_H1($param);
3990        }
3991
3992        /**
3993         * tag : H3
3994         * mode : CLOSE
3995         *
3996         * @param  array $param
3997         * @return boolean
3998         */
3999        protected function _tag_close_H3($param)
4000        {
4001            return $this->_tag_close_H1($param);
4002        }
4003
4004        /**
4005         * tag : H4
4006         * mode : CLOSE
4007         *
4008         * @param  array $param
4009         * @return boolean
4010         */
4011        protected function _tag_close_H4($param)
4012        {
4013            return $this->_tag_close_H1($param);
4014        }
4015
4016        /**
4017         * tag : H5
4018         * mode : CLOSE
4019         *
4020         * @param  array $param
4021         * @return boolean
4022         */
4023        protected function _tag_close_H5($param)
4024        {
4025            return $this->_tag_close_H1($param);
4026        }
4027
4028        /**
4029         * tag : H6
4030         * mode : CLOSE
4031         *
4032         * @param  array $param
4033         * @return boolean
4034         */
4035        protected function _tag_close_H6($param)
4036        {
4037            return $this->_tag_close_H1($param);
4038        }
4039
4040        /**
4041         * tag : SPAN
4042         * mode : OPEN
4043         *
4044         * @param  array $param
4045         * @param  string $other
4046         * @return boolean
4047         */
4048        protected function _tag_open_SPAN($param, $other = 'span')
4049        {
4050            $this->parsingCss->save();
4051            $this->parsingCss->analyse($other, $param);
4052            $this->parsingCss->setPosition();
4053            $this->parsingCss->fontSet();
4054
4055            return true;
4056        }
4057
4058        /**
4059         * tag : FONT
4060         * mode : OPEN
4061         *
4062         * @param  array $param
4063         * @return boolean
4064         */
4065        protected function _tag_open_FONT($param)
4066        {
4067            return $this->_tag_open_SPAN($param, 'font');
4068        }
4069
4070        /**
4071         * tag : LABEL
4072         * mode : OPEN
4073         *
4074         * @param  array $param
4075         * @return boolean
4076         */
4077        protected function _tag_open_LABEL($param)
4078        {
4079            return $this->_tag_open_SPAN($param, 'label');
4080        }
4081
4082        /**
4083         * tag : SPAN
4084         * mode : CLOSE
4085         *
4086         * @param  array $param
4087         * @return boolean
4088         */
4089        protected function _tag_close_SPAN($param)
4090        {
4091            $this->parsingCss->restorePosition();
4092            $this->parsingCss->load();
4093            $this->parsingCss->fontSet();
4094
4095            return true;
4096        }
4097
4098        /**
4099         * tag : FONT
4100         * mode : CLOSE
4101         *
4102         * @param  array $param
4103         * @return boolean
4104         */
4105        protected function _tag_close_FONT($param)
4106        {
4107            return $this->_tag_close_SPAN($param);
4108        }
4109
4110        /**
4111         * tag : LABEL
4112         * mode : CLOSE
4113         *
4114         * @param  array $param
4115         * @return boolean
4116         */
4117        protected function _tag_close_LABEL($param)
4118        {
4119            return $this->_tag_close_SPAN($param);
4120        }
4121
4122        /**
4123         * tag : P
4124         * mode : OPEN
4125         *
4126         * @param    array $param
4127         * @return boolean
4128         */
4129        protected function _tag_open_P($param)
4130        {
4131            if ($this->_isForOneLine) return false;
4132
4133            if (!in_array($this->_previousCall, array('_tag_close_P', '_tag_close_UL'))) {
4134                if ($this->_maxH) $this->_tag_open_BR(array());
4135            }
4136
4137            $this->parsingCss->save();
4138            $this->parsingCss->analyse('p', $param);
4139            $this->parsingCss->setPosition();
4140            $this->parsingCss->fontSet();
4141
4142             // cancel the effects of the setPosition
4143            $this->pdf->setXY($this->pdf->getX()-$this->parsingCss->value['margin']['l'], $this->pdf->getY()-$this->parsingCss->value['margin']['t']);
4144
4145            list($mL, $mR) = $this->_getMargins($this->pdf->getY());
4146            $mR = $this->pdf->getW()-$mR;
4147            $mL+= $this->parsingCss->value['margin']['l']+$this->parsingCss->value['padding']['l'];
4148            $mR+= $this->parsingCss->value['margin']['r']+$this->parsingCss->value['padding']['r'];
4149            $this->_saveMargin($mL, 0, $mR);
4150
4151            if ($this->parsingCss->value['text-indent']>0) {
4152                $y = $this->pdf->getY()+$this->parsingCss->value['margin']['t']+$this->parsingCss->value['padding']['t'];
4153                $this->_pageMarges[floor($y*100)] = array($mL+$this->parsingCss->value['text-indent'], $this->pdf->getW()-$mR);
4154                $y+= $this->parsingCss->getLineHeight()*0.1;
4155                $this->_pageMarges[floor($y*100)] = array($mL, $this->pdf->getW()-$mR);
4156            }
4157            $this->_makeBreakLine($this->parsingCss->value['margin']['t']+$this->parsingCss->value['padding']['t']);
4158            $this->_isInParagraph = array($mL, $mR);
4159            return true;
4160        }
4161
4162        /**
4163         * tag : P
4164         * mode : CLOSE
4165         *
4166         * @param  array $param
4167         * @return boolean
4168         */
4169        protected function _tag_close_P($param)
4170        {
4171            if ($this->_isForOneLine) return false;
4172
4173            if ($this->_maxH) $this->_tag_open_BR(array());
4174            $this->_isInParagraph = false;
4175            $this->_loadMargin();
4176            $h = $this->parsingCss->value['margin']['b']+$this->parsingCss->value['padding']['b'];
4177
4178            $this->parsingCss->load();
4179            $this->parsingCss->fontSet();
4180            $this->_makeBreakLine($h);
4181
4182            return true;
4183        }
4184
4185        /**
4186         * tag : PRE
4187         * mode : OPEN
4188         *
4189         * @param  array $param
4190         * @param  string $other
4191         * @return boolean
4192         */
4193        protected function _tag_open_PRE($param, $other = 'pre')
4194        {
4195            if ($other=='pre' && $this->_maxH) $this->_tag_open_BR(array());
4196
4197            $this->parsingCss->save();
4198            $this->parsingCss->value['font-family'] = 'courier';
4199            $this->parsingCss->analyse($other, $param);
4200            $this->parsingCss->setPosition();
4201            $this->parsingCss->fontSet();
4202
4203            if ($other=='pre') return $this->_tag_open_DIV($param, $other);
4204
4205            return true;
4206        }
4207
4208        /**
4209         * tag : CODE
4210         * mode : OPEN
4211         *
4212         * @param  array $param
4213         * @param  string $other
4214         * @return boolean
4215         */
4216        protected function _tag_open_CODE($param)
4217        {
4218            return $this->_tag_open_PRE($param, 'code');
4219        }
4220
4221        /**
4222         * tag : PRE
4223         * mode : CLOSE
4224         *
4225         * @param  array $param
4226         * @param  string $other
4227         * @return boolean
4228         */
4229        protected function _tag_close_PRE($param, $other = 'pre')
4230        {
4231            if ($other=='pre') {
4232                if ($this->_isForOneLine) return false;
4233
4234                $this->_tag_close_DIV($param, $other);
4235                $this->_tag_open_BR(array());
4236            }
4237            $this->parsingCss->load();
4238            $this->parsingCss->fontSet();
4239
4240            return true;
4241        }
4242
4243        /**
4244         * tag : CODE
4245         * mode : CLOSE
4246         *
4247         * @param  array $param
4248         * @return boolean
4249         */
4250        protected function _tag_close_CODE($param)
4251        {
4252            return $this->_tag_close_PRE($param, 'code');
4253        }
4254
4255        /**
4256         * tag : BIG
4257         * mode : OPEN
4258         *
4259         * @param    array $param
4260         * @return boolean
4261         */
4262        protected function _tag_open_BIG($param)
4263        {
4264            $this->parsingCss->save();
4265            $this->parsingCss->value['mini-decal']-= $this->parsingCss->value['mini-size']*0.12;
4266            $this->parsingCss->value['mini-size'] *= 1.2;
4267            $this->parsingCss->analyse('big', $param);
4268            $this->parsingCss->setPosition();
4269            $this->parsingCss->fontSet();
4270            return true;
4271        }
4272
4273        /**
4274         * tag : BIG
4275         * mode : CLOSE
4276         *
4277         * @param    array $param
4278         * @return boolean
4279         */
4280        protected function _tag_close_BIG($param)
4281        {
4282            $this->parsingCss->load();
4283            $this->parsingCss->fontSet();
4284
4285            return true;
4286        }
4287
4288        /**
4289         * tag : SMALL
4290         * mode : OPEN
4291         *
4292         * @param    array $param
4293         * @return boolean
4294         */
4295        protected function _tag_open_SMALL($param)
4296        {
4297            $this->parsingCss->save();
4298            $this->parsingCss->value['mini-decal']+= $this->parsingCss->value['mini-size']*0.05;
4299            $this->parsingCss->value['mini-size'] *= 0.82;
4300            $this->parsingCss->analyse('small', $param);
4301            $this->parsingCss->setPosition();
4302            $this->parsingCss->fontSet();
4303            return true;
4304        }
4305
4306        /**
4307         * tag : SMALL
4308         * mode : CLOSE
4309         *
4310         * @param    array $param
4311         * @return boolean
4312         */
4313        protected function _tag_close_SMALL($param)
4314        {
4315            $this->parsingCss->load();
4316            $this->parsingCss->fontSet();
4317
4318            return true;
4319        }
4320
4321        /**
4322         * tag : SUP
4323         * mode : OPEN
4324         *
4325         * @param    array $param
4326         * @return boolean
4327         */
4328        protected function _tag_open_SUP($param)
4329        {
4330            $this->parsingCss->save();
4331            $this->parsingCss->value['mini-decal']-= $this->parsingCss->value['mini-size']*0.15;
4332            $this->parsingCss->value['mini-size'] *= 0.75;
4333            $this->parsingCss->analyse('sup', $param