[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/se3master/var/www/se3/html2pdf/ -> html2pdf.class.php (source)

   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.03
  10   */
  11  
  12  if (!defined('__CLASS_HTML2PDF__')) {
  13  
  14      define('__CLASS_HTML2PDF__', '4.03');
  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  
  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              $oldParsePos = $this->_parsePos;
 812              $oldParseCode = $this->parsingHtml->code;
 813  
 814              $this->_parsePos = 0;
 815              $this->parsingHtml->code = $this->_subHEADER;
 816              $this->_makeHTMLcode();
 817  
 818              $this->_parsePos = $oldParsePos;
 819              $this->parsingHtml->code = $oldParseCode;
 820          }
 821  
 822          /**
 823           * draw the PDF footer with the HTML in page_footer
 824           *
 825           * @access protected
 826           */
 827          protected function _setPageFooter()
 828          {
 829              if (!count($this->_subFOOTER)) return false;
 830  
 831              $oldParsePos = $this->_parsePos;
 832              $oldParseCode = $this->parsingHtml->code;
 833  
 834              $this->_parsePos = 0;
 835              $this->parsingHtml->code = $this->_subFOOTER;
 836              $this->_isInFooter = true;
 837              $this->_makeHTMLcode();
 838              $this->_isInFooter = false;
 839  
 840              $this->_parsePos = $oldParsePos;
 841              $this->parsingHtml->code = $oldParseCode;
 842          }
 843  
 844          /**
 845           * new line, with a specific height
 846           *
 847           * @access protected
 848           * @param float   $h
 849           * @param integer $curr real current position in the text, if new line in the write of a text
 850           */
 851          protected function _setNewLine($h, $curr = null)
 852          {
 853              $this->pdf->Ln($h);
 854              $this->_setNewPositionForNewLine($curr);
 855          }
 856  
 857          /**
 858           * calculate the start position of the next line,  depending on the text-align
 859           *
 860           * @access protected
 861           * @param  integer $curr real current position in the text, if new line in the write of a text
 862           */
 863          protected function _setNewPositionForNewLine($curr = null)
 864          {
 865              // get the margins for the current line
 866              list($lx, $rx) = $this->_getMargins($this->pdf->getY());
 867              $this->pdf->setX($lx);
 868              $wMax = $rx-$lx;
 869              $this->_currentH = 0;
 870  
 871              // if subPart => return because align left
 872              if ($this->_subPart || $this->_isSubPart || $this->_isForOneLine) {
 873                  $this->pdf->setWordSpacing(0);
 874                  return null;
 875              }
 876  
 877              // create the sub object
 878              $sub = null;
 879              $this->_createSubHTML($sub);
 880              $sub->_saveMargin(0, 0, $sub->pdf->getW()-$wMax);
 881              $sub->_isForOneLine = true;
 882              $sub->_parsePos = $this->_parsePos;
 883              $sub->parsingHtml->code = $this->parsingHtml->code;
 884  
 885              // if $curr => adapt the current position of the parsing
 886              if ($curr!==null && $sub->parsingHtml->code[$this->_parsePos]['name']=='write') {
 887                  $txt = $sub->parsingHtml->code[$this->_parsePos]['param']['txt'];
 888                  $txt = str_replace('[[page_cu]]', $sub->pdf->getMyNumPage($this->_page), $txt);
 889                  $sub->parsingHtml->code[$this->_parsePos]['param']['txt'] = substr($txt, $curr+1);
 890              } else
 891                  $sub->_parsePos++;
 892  
 893              // for each element of the parsing => load the action
 894              $res = null;
 895              for ($sub->_parsePos; $sub->_parsePos<count($sub->parsingHtml->code); $sub->_parsePos++) {
 896                  $action = $sub->parsingHtml->code[$sub->_parsePos];
 897                  $res = $sub->_executeAction($action);
 898                  if (!$res) break;
 899              }
 900  
 901              $w = $sub->_maxX; // max width
 902              $h = $sub->_maxH; // max height
 903              $e = ($res===null ? $sub->_maxE : 0); // maxnumber of elemets on the line
 904  
 905              // destroy the sub HTML
 906              $this->_destroySubHTML($sub);
 907  
 908              // adapt the start of the line, depending on the text-align
 909              if ($this->parsingCss->value['text-align']=='center')
 910                  $this->pdf->setX(($rx+$this->pdf->getX()-$w)*0.5-0.01);
 911              else if ($this->parsingCss->value['text-align']=='right')
 912                  $this->pdf->setX($rx-$w-0.01);
 913              else
 914                  $this->pdf->setX($lx);
 915  
 916              // set the height of the line
 917              $this->_currentH = $h;
 918  
 919              // if justify => set the word spacing
 920              if ($this->parsingCss->value['text-align']=='justify' && $e>1) {
 921                  $this->pdf->setWordSpacing(($wMax-$w)/($e-1));
 922              } else {
 923                  $this->pdf->setWordSpacing(0);
 924              }
 925          }
 926  
 927          /**
 928           * prepare HTML2PDF::$_subobj (used for create the sub HTML2PDF objects
 929           *
 930           * @access protected
 931           */
 932          protected function _prepareSubObj()
 933          {
 934              $pdf = null;
 935  
 936              // create the sub object
 937              HTML2PDF::$_subobj = new HTML2PDF(
 938                                          $this->_orientation,
 939                                          $this->_format,
 940                                          $this->_langue,
 941                                          $this->_unicode,
 942                                          $this->_encoding,
 943                                          array($this->_defaultLeft,$this->_defaultTop,$this->_defaultRight,$this->_defaultBottom)
 944                                      );
 945  
 946              // init
 947              HTML2PDF::$_subobj->setTestTdInOnePage($this->_testTdInOnepage);
 948              HTML2PDF::$_subobj->setTestIsImage($this->_testIsImage);
 949              HTML2PDF::$_subobj->setTestIsDeprecated($this->_testIsDeprecated);
 950              HTML2PDF::$_subobj->setDefaultFont($this->_defaultFont);
 951              HTML2PDF::$_subobj->parsingCss->css            = &$this->parsingCss->css;
 952              HTML2PDF::$_subobj->parsingCss->cssKeys        = &$this->parsingCss->cssKeys;
 953  
 954              // clone font from the original PDF
 955              HTML2PDF::$_subobj->pdf->cloneFontFrom($this->pdf);
 956  
 957              // remove the link to the parent
 958              HTML2PDF::$_subobj->parsingCss->setPdfParent($pdf);
 959          }
 960  
 961          /**
 962           * create a sub HTML2PDF, to calculate the multi-tables
 963           *
 964           * @access protected
 965           * @param  &HTML2PDF $subHtml sub HTML2PDF to create
 966           * @param  integer   $cellmargin if in a TD : cellmargin of this td
 967           */
 968          protected function _createSubHTML(&$subHtml, $cellmargin=0)
 969          {
 970              // prepare the subObject, if never prepare before
 971              if (HTML2PDF::$_subobj===null) {
 972                  $this->_prepareSubObj();
 973              }
 974  
 975              // calculate the width to use
 976              if ($this->parsingCss->value['width']) {
 977                  $marge = $cellmargin*2;
 978                  $marge+= $this->parsingCss->value['padding']['l'] + $this->parsingCss->value['padding']['r'];
 979                  $marge+= $this->parsingCss->value['border']['l']['width'] + $this->parsingCss->value['border']['r']['width'];
 980                  $marge = $this->pdf->getW() - $this->parsingCss->value['width'] + $marge;
 981              } else {
 982                  $marge = $this->_margeLeft+$this->_margeRight;
 983              }
 984  
 985              // BUGFIX : we have to call the method, because of a bug in php 5.1.6
 986              HTML2PDF::$_subobj->pdf->getPage();
 987  
 988              // clone the sub oject
 989              $subHtml = clone HTML2PDF::$_subobj;
 990              $subHtml->parsingCss->table = $this->parsingCss->table;
 991              $subHtml->parsingCss->value = $this->parsingCss->value;
 992              $subHtml->initSubHtml(
 993                  $this->_format,
 994                  $this->_orientation,
 995                  $marge,
 996                  $this->_page,
 997                  $this->_defList,
 998                  $this->pdf->getMyLastPageGroup(),
 999                  $this->pdf->getMyLastPageGroupNb()
1000              );
1001          }
1002  
1003          /**
1004           * destroy a subHTML2PDF
1005           *
1006           * @access protected
1007           */
1008          protected function _destroySubHTML(&$subHtml)
1009          {
1010              unset($subHtml);
1011              $subHtml = null;
1012          }
1013  
1014          /**
1015           * Convert a arabic number in roman number
1016           *
1017           * @access protected
1018           * @param  integer $nbArabic
1019           * @return string  $nbRoman
1020           */
1021          protected function _listeArab2Rom($nbArabic)
1022          {
1023              $nbBaseTen    = array('I','X','C','M');
1024              $nbBaseFive    = array('V','L','D');
1025              $nbRoman    = '';
1026  
1027              if ($nbArabic<1)    return $nbArabic;
1028              if ($nbArabic>3999) return $nbArabic;
1029  
1030              for ($i=3; $i>=0 ; $i--) {
1031                  $chiffre=floor($nbArabic/pow(10, $i));
1032                  if ($chiffre>=1) {
1033                      $nbArabic=$nbArabic-$chiffre*pow(10, $i);
1034                      if ($chiffre<=3) {
1035                          for ($j=$chiffre; $j>=1; $j--) {
1036                              $nbRoman=$nbRoman.$nbBaseTen[$i];
1037                          }
1038                      } else if ($chiffre==9) {
1039                          $nbRoman=$nbRoman.$nbBaseTen[$i].$nbBaseTen[$i+1];
1040                      } else if ($chiffre==4) {
1041                      $nbRoman=$nbRoman.$nbBaseTen[$i].$nbBaseFive[$i];
1042                      } else {
1043                          $nbRoman=$nbRoman.$nbBaseFive[$i];
1044                          for ($j=$chiffre-5; $j>=1; $j--) {
1045                              $nbRoman=$nbRoman.$nbBaseTen[$i];
1046                          }
1047                      }
1048                  }
1049              }
1050              return $nbRoman;
1051          }
1052  
1053          /**
1054           * add a LI to the current level
1055           *
1056           * @access protected
1057           */
1058          protected function _listeAddLi()
1059          {
1060              $this->_defList[count($this->_defList)-1]['nb']++;
1061          }
1062  
1063          /**
1064           * get the width to use for the column of the list
1065           *
1066           * @access protected
1067           * @return string $width
1068           */
1069          protected function _listeGetWidth()
1070          {
1071              return '7mm';
1072          }
1073  
1074          /**
1075           * get the padding to use for the column of the list
1076           *
1077           * @access protected
1078           * @return string $padding
1079           */
1080          protected function _listeGetPadding()
1081          {
1082              return '1mm';
1083          }
1084  
1085          /**
1086           * get the information of the li on the current level
1087           *
1088           * @access protected
1089           * @return array(fontName, small size, string)
1090           */
1091          protected function _listeGetLi()
1092          {
1093              $im = $this->_defList[count($this->_defList)-1]['img'];
1094              $st = $this->_defList[count($this->_defList)-1]['style'];
1095              $nb = $this->_defList[count($this->_defList)-1]['nb'];
1096              $up = (substr($st, 0, 6)=='upper-');
1097  
1098              if ($im) return array(false, false, $im);
1099  
1100              switch($st)
1101              {
1102                  case 'none':
1103                      return array('helvetica', true, ' ');
1104  
1105                  case 'upper-alpha':
1106                  case 'lower-alpha':
1107                      $str = '';
1108                      while ($nb>26) {
1109                          $str = chr(96+$nb%26).$str;
1110                          $nb = floor($nb/26);
1111                      }
1112                      $str = chr(96+$nb).$str;
1113  
1114                      return array('helvetica', false, ($up ? strtoupper($str) : $str).'.');
1115  
1116                  case 'upper-roman':
1117                  case 'lower-roman':
1118                      $str = $this->_listeArab2Rom($nb);
1119  
1120                      return array('helvetica', false, ($up ? strtoupper($str) : $str).'.');
1121  
1122                  case 'decimal':
1123                      return array('helvetica', false, $nb.'.');
1124  
1125                  case 'square':
1126                      return array('zapfdingbats', true, chr(110));
1127  
1128                  case 'circle':
1129                      return array('zapfdingbats', true, chr(109));
1130  
1131                  case 'disc':
1132                  default:
1133                      return array('zapfdingbats', true, chr(108));
1134              }
1135          }
1136  
1137          /**
1138           * add a level to the list
1139           *
1140           * @access protected
1141           * @param  string $type  : ul, ol
1142           * @param  string $style : lower-alpha, ...
1143           * @param  string $img
1144           */
1145          protected function _listeAddLevel($type = 'ul', $style = '', $img = null)
1146          {
1147              // get the url of the image, if we want to use a image
1148              if ($img) {
1149                  if (preg_match('/^url\(([^)]+)\)$/isU', trim($img), $match)) {
1150                      $img = $match[1];
1151                  } else {
1152                      $img = null;
1153                  }
1154              } else {
1155                  $img = null;
1156              }
1157  
1158              // prepare the datas
1159              if (!in_array($type, array('ul', 'ol'))) $type = 'ul';
1160              if (!in_array($style, array('lower-alpha', 'upper-alpha', 'upper-roman', 'lower-roman', 'decimal', 'square', 'circle', 'disc', 'none'))) $style = '';
1161  
1162              if (!$style) {
1163                  if ($type=='ul')    $style = 'disc';
1164                  else                $style = 'decimal';
1165              }
1166  
1167              // add the new level
1168              $this->_defList[count($this->_defList)] = array('style' => $style, 'nb' => 0, 'img' => $img);
1169          }
1170  
1171          /**
1172           * remove a level to the list
1173           *
1174           * @access protected
1175           */
1176          protected function _listeDelLevel()
1177          {
1178              if (count($this->_defList)) {
1179                  unset($this->_defList[count($this->_defList)-1]);
1180                  $this->_defList = array_values($this->_defList);
1181              }
1182          }
1183  
1184          /**
1185           * execute the actions to convert the html
1186           *
1187           * @access protected
1188           */
1189          protected function _makeHTMLcode()
1190          {
1191              // foreach elements of the parsing
1192              for ($this->_parsePos=0; $this->_parsePos<count($this->parsingHtml->code); $this->_parsePos++) {
1193  
1194                  // get the action to do
1195                  $action = $this->parsingHtml->code[$this->_parsePos];
1196  
1197                  // if it is a opening of table / ul / ol
1198                  if (in_array($action['name'], array('table', 'ul', 'ol')) && !$action['close']) {
1199  
1200                      //  we will work as a sub HTML to calculate the size of the element
1201                      $this->_subPart = true;
1202  
1203                      // get the name of the opening tag
1204                      $tagOpen = $action['name'];
1205  
1206                      // save the actual pos on the parsing
1207                      $this->_tempPos = $this->_parsePos;
1208  
1209                      // foreach elements, while we are in the opened tag
1210                      while (isset($this->parsingHtml->code[$this->_tempPos]) && !($this->parsingHtml->code[$this->_tempPos]['name']==$tagOpen && $this->parsingHtml->code[$this->_tempPos]['close'])) {
1211                          // make the action
1212                          $this->_executeAction($this->parsingHtml->code[$this->_tempPos]);
1213                          $this->_tempPos++;
1214                      }
1215  
1216                      // execute the closure of the tag
1217                      if (isset($this->parsingHtml->code[$this->_tempPos])) {
1218                          $this->_executeAction($this->parsingHtml->code[$this->_tempPos]);
1219                      }
1220  
1221                      // end of the sub part
1222                      $this->_subPart = false;
1223                  }
1224  
1225                  // execute the action
1226                  $this->_executeAction($action);
1227              }
1228          }
1229  
1230          /**
1231           * execute the action from the parsing
1232           *
1233           * @access protected
1234           * @param  array $action
1235           */
1236          protected function _executeAction($action)
1237          {
1238              // name of the action
1239              $fnc = ($action['close'] ? '_tag_close_' : '_tag_open_').strtoupper($action['name']);
1240  
1241              // parameters of the action
1242              $param = $action['param'];
1243  
1244              // if it the first action of the first page, and if it is not a open tag of PAGE => create the new page
1245              if ($fnc!='_tag_open_PAGE' && $this->_firstPage) {
1246                  $this->_setNewPage();
1247              }
1248  
1249              // the action must exist
1250              if (!is_callable(array(&$this, $fnc))) {
1251                  throw new HTML2PDF_exception(1, strtoupper($action['name']), $this->parsingHtml->getHtmlErrorCode($action['html_pos']));
1252              }
1253  
1254              // lauch the action
1255              $res = $this->{$fnc}($param);
1256  
1257              // save the name of the action
1258              $this->_previousCall = $fnc;
1259  
1260              // return the result
1261              return $res;
1262          }
1263  
1264          /**
1265           * get the position of the element on the current line, depending on his height
1266           *
1267           * @access protected
1268           * @param  float $h
1269           * @return float
1270           */
1271          protected function _getElementY($h)
1272          {
1273              if ($this->_subPart || $this->_isSubPart || !$this->_currentH || $this->_currentH<$h)
1274                  return 0;
1275  
1276              return ($this->_currentH-$h)*0.8;
1277          }
1278  
1279          /**
1280           * make a break line
1281           *
1282           * @access protected
1283           * @param  float $h current line height
1284           * @param  integer $curr real current position in the text, if new line in the write of a text
1285           */
1286          protected function _makeBreakLine($h, $curr = null)
1287          {
1288              if ($h) {
1289                  if (($this->pdf->getY()+$h<$this->pdf->getH() - $this->pdf->getbMargin()) || $this->_isInOverflow || $this->_isInFooter)
1290                      $this->_setNewLine($h, $curr);
1291                  else
1292                      $this->_setNewPage(null, '', null, $curr);
1293              } else {
1294                  $this->_setNewPositionForNewLine($curr);
1295              }
1296  
1297              $this->_maxH = 0;
1298              $this->_maxE = 0;
1299          }
1300  
1301          /**
1302           * display a image
1303           *
1304           * @access protected
1305           * @param  string $src
1306           * @param  boolean $subLi if true=image of a list
1307           * @return boolean depending on "isForOneLine"
1308           */
1309          protected function _drawImage($src, $subLi=false)
1310          {
1311              // get the size of the image
1312              // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini
1313              $infos=@getimagesize($src);
1314  
1315              // if the image does not exist, or can not be loaded
1316              if (count($infos)<2) {
1317                  // if the test is activ => exception
1318                  if ($this->_testIsImage) {
1319                      throw new HTML2PDF_exception(6, $src);
1320                  }
1321  
1322                  // else, display a gray rectangle
1323                  $src = null;
1324                  $infos = array(16, 16);
1325              }
1326  
1327              // convert the size of the image in the unit of the PDF
1328              $imageWidth = $infos[0]/$this->pdf->getK();
1329              $imageHeight = $infos[1]/$this->pdf->getK();
1330  
1331              // calculate the size from the css style
1332              if ($this->parsingCss->value['width'] && $this->parsingCss->value['height']) {
1333                  $w = $this->parsingCss->value['width'];
1334                  $h = $this->parsingCss->value['height'];
1335              } else if ($this->parsingCss->value['width']) {
1336                  $w = $this->parsingCss->value['width'];
1337                  $h = $imageHeight*$w/$imageWidth;
1338              } else if ($this->parsingCss->value['height']) {
1339                  $h = $this->parsingCss->value['height'];
1340                  $w = $imageWidth*$h/$imageHeight;
1341              } else {
1342                  // convert px to pt
1343                  $w = 72./96.*$imageWidth;
1344                  $h = 72./96.*$imageHeight;
1345              }
1346  
1347              // are we in a float
1348              $float = $this->parsingCss->getFloat();
1349  
1350              // if we are in a float, but if something else if on the line => Break Line
1351              if ($float && $this->_maxH) {
1352                  // make the break line (false if we are in "_isForOneLine" mode)
1353                  if (!$this->_tag_open_BR(array())) {
1354                      return false;
1355                  }
1356              }
1357  
1358              // position of the image
1359              $x = $this->pdf->getX();
1360              $y = $this->pdf->getY();
1361  
1362              // if the image can not be put on the current line => new line
1363              if (!$float && ($x + $w>$this->pdf->getW() - $this->pdf->getrMargin()) && $this->_maxH) {
1364                  if ($this->_isForOneLine) {
1365                      return false;
1366                  }
1367  
1368                  // set the new line
1369                  $hnl = max($this->_maxH, $this->parsingCss->getLineHeight());
1370                  $this->_setNewLine($hnl);
1371  
1372                  // get the new position
1373                  $x = $this->pdf->getX();
1374                  $y = $this->pdf->getY();
1375              }
1376  
1377              // if the image can not be put on the current page
1378              if (($y + $h>$this->pdf->getH() - $this->pdf->getbMargin()) && !$this->_isInOverflow) {
1379                  // new page
1380                  $this->_setNewPage();
1381  
1382                  // get the new position
1383                  $x = $this->pdf->getX();
1384                  $y = $this->pdf->getY();
1385              }
1386  
1387              // correction for display the image of a list
1388              $hT = 0.80*$this->parsingCss->value['font-size'];
1389              if ($subLi && $h<$hT) {
1390                  $y+=($hT-$h);
1391              }
1392  
1393              // add the margin top
1394              $yc = $y-$this->parsingCss->value['margin']['t'];
1395  
1396              // get the width and the position of the parent
1397              $old = $this->parsingCss->getOldValues();
1398              if ( $old['width']) {
1399                  $parentWidth = $old['width'];
1400                  $parentX = $x;
1401              } else {
1402                  $parentWidth = $this->pdf->getW() - $this->pdf->getlMargin() - $this->pdf->getrMargin();
1403                  $parentX = $this->pdf->getlMargin();
1404              }
1405  
1406              // if we are in a gloat => adapt the parent position and width
1407              if ($float) {
1408                  list($lx, $rx) = $this->_getMargins($yc);
1409                  $parentX = $lx;
1410                  $parentWidth = $rx-$lx;
1411              }
1412  
1413              // calculate the position of the image, if align to the right
1414              if ($parentWidth>$w && $float!='left') {
1415                  if ($float=='right' || $this->parsingCss->value['text-align']=='li_right')    $x = $parentX + $parentWidth - $w-$this->parsingCss->value['margin']['r']-$this->parsingCss->value['margin']['l'];
1416              }
1417  
1418              // display the image
1419              if (!$this->_subPart && !$this->_isSubPart) {
1420                  if ($src) {
1421                      $this->pdf->Image($src, $x, $y, $w, $h, '', $this->_isInLink);
1422                  } else {
1423                      // rectangle if the image can not be loaded
1424                      $this->pdf->setFillColorArray(array(240, 220, 220));
1425                      $this->pdf->Rect($x, $y, $w, $h, 'F');
1426                  }
1427              }
1428  
1429              // apply the margins
1430              $x-= $this->parsingCss->value['margin']['l'];
1431              $y-= $this->parsingCss->value['margin']['t'];
1432              $w+= $this->parsingCss->value['margin']['l'] + $this->parsingCss->value['margin']['r'];
1433              $h+= $this->parsingCss->value['margin']['t'] + $this->parsingCss->value['margin']['b'];
1434  
1435              if ($float=='left') {
1436                  // save the current max
1437                  $this->_maxX = max($this->_maxX, $x+$w);
1438                  $this->_maxY = max($this->_maxY, $y+$h);
1439  
1440                  // add the image to the margins
1441                  $this->_addMargins($float, $x, $y, $x+$w, $y+$h);
1442  
1443                  // get the new position
1444                  list($lx, $rx) = $this->_getMargins($yc);
1445                  $this->pdf->setXY($lx, $yc);
1446              } else if ($float=='right') {
1447                  // save the current max. We don't save the X because it is not the real max of the line
1448                  $this->_maxY = max($this->_maxY, $y+$h);
1449  
1450                  // add the image to the margins
1451                  $this->_addMargins($float, $x, $y, $x+$w, $y+$h);
1452  
1453                  // get the new position
1454                  list($lx, $rx) = $this->_getMargins($yc);
1455                  $this->pdf->setXY($lx, $yc);
1456              } else {
1457                  // set the new position at the end of the image
1458                  $this->pdf->setX($x+$w);
1459  
1460                  // save the current max
1461                  $this->_maxX = max($this->_maxX, $x+$w);
1462                  $this->_maxY = max($this->_maxY, $y+$h);
1463                  $this->_maxH = max($this->_maxH, $h);
1464              }
1465  
1466              return true;
1467          }
1468  
1469          /**
1470           * draw a rectangle
1471           *
1472           * @access protected
1473           * @param  float $x
1474           * @param  float $y
1475           * @param  float $w
1476           * @param  float $h
1477           * @param  array $border
1478           * @param  float $padding - internal marge of the rectanble => not used, but...
1479           * @param  float $margin  - external marge of the rectanble
1480           * @param  array $background
1481           * @return boolean
1482           */
1483          protected function _drawRectangle($x, $y, $w, $h, $border, $padding, $margin, $background)
1484          {
1485              // if we are in a subpart or if height is null => return false
1486              if ($this->_subPart || $this->_isSubPart || $h===null) return false;
1487  
1488              // add the margin
1489              $x+= $margin;
1490              $y+= $margin;
1491              $w-= $margin*2;
1492              $h-= $margin*2;
1493  
1494              // get the radius of the border
1495              $outTL = $border['radius']['tl'];
1496              $outTR = $border['radius']['tr'];
1497              $outBR = $border['radius']['br'];
1498              $outBL = $border['radius']['bl'];
1499  
1500              // prepare the out radius
1501              $outTL = ($outTL[0] && $outTL[1]) ? $outTL : null;
1502              $outTR = ($outTR[0] && $outTR[1]) ? $outTR : null;
1503              $outBR = ($outBR[0] && $outBR[1]) ? $outBR : null;
1504              $outBL = ($outBL[0] && $outBL[1]) ? $outBL : null;
1505  
1506              // prepare the in radius
1507              $inTL = $outTL;
1508              $inTR = $outTR;
1509              $inBR = $outBR;
1510              $inBL = $outBL;
1511  
1512              if (is_array($inTL)) {
1513                  $inTL[0]-= $border['l']['width'];
1514                  $inTL[1]-= $border['t']['width'];
1515              }
1516              if (is_array($inTR)) {
1517                  $inTR[0]-= $border['r']['width'];
1518                  $inTR[1]-= $border['t']['width'];
1519              }
1520              if (is_array($inBR)) {
1521                  $inBR[0]-= $border['r']['width'];
1522                  $inBR[1]-= $border['b']['width'];
1523              }
1524              if (is_array($inBL)) {
1525                  $inBL[0]-= $border['l']['width'];
1526                  $inBL[1]-= $border['b']['width'];
1527              }
1528  
1529              if ($inTL[0]<=0 || $inTL[1]<=0) $inTL = null;
1530              if ($inTR[0]<=0 || $inTR[1]<=0) $inTR = null;
1531              if ($inBR[0]<=0 || $inBR[1]<=0) $inBR = null;
1532              if ($inBL[0]<=0 || $inBL[1]<=0) $inBL = null;
1533  
1534              // prepare the background color
1535              $pdfStyle = '';
1536              if ($background['color']) {
1537                  $this->pdf->setFillColorArray($background['color']);
1538                  $pdfStyle.= 'F';
1539              }
1540  
1541              // if we have a background to fill => fill it with a path (because of the radius)
1542              if ($pdfStyle) {
1543                  $this->pdf->clippingPathStart($x, $y, $w, $h, $outTL, $outTR, $outBL, $outBR);
1544                  $this->pdf->Rect($x, $y, $w, $h, $pdfStyle);
1545                  $this->pdf->clippingPathStop();
1546              }
1547  
1548              // prepare the background image
1549              if ($background['image']) {
1550                  $iName      = $background['image'];
1551                  $iPosition  = $background['position']!==null ? $background['position'] : array(0, 0);
1552                  $iRepeat    = $background['repeat']!==null   ? $background['repeat']   : array(true, true);
1553  
1554                  // size of the background without the borders
1555                  $bX = $x;
1556                  $bY = $y;
1557                  $bW = $w;
1558                  $bH = $h;
1559  
1560                  if ($border['b']['width']) {
1561                      $bH-= $border['b']['width'];
1562                  }
1563                  if ($border['l']['width']) {
1564                      $bW-= $border['l']['width'];
1565                      $bX+= $border['l']['width'];
1566                  }
1567                  if ($border['t']['width']) {
1568                      $bH-= $border['t']['width'];
1569                      $bY+= $border['t']['width'];
1570                  }
1571                  if ($border['r']['width']) {
1572                      $bW-= $border['r']['width'];
1573                  }
1574  
1575                  // get the size of the image
1576                  // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini
1577                  $imageInfos=@getimagesize($iName);
1578  
1579                  // if the image can not be loaded
1580                  if (count($imageInfos)<2) {
1581                      if ($this->_testIsImage) {
1582                          throw new HTML2PDF_exception(6, $iName);
1583                      }
1584                  } else {
1585                      // convert the size of the image from pixel to the unit of the PDF
1586                      $imageWidth    = 72./96.*$imageInfos[0]/$this->pdf->getK();
1587                      $imageHeight    = 72./96.*$imageInfos[1]/$this->pdf->getK();
1588  
1589                      // prepare the position of the backgroung
1590                      if ($iRepeat[0]) $iPosition[0] = $bX;
1591                      else if (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[0], $match)) $iPosition[0] = $bX + $match[1]*($bW-$imageWidth)/100;
1592                      else $iPosition[0] = $bX+$iPosition[0];
1593  
1594                      if ($iRepeat[1]) $iPosition[1] = $bY;
1595                      else if (preg_match('/^([-]?[0-9\.]+)%/isU', $iPosition[1], $match)) $iPosition[1] = $bY + $match[1]*($bH-$imageHeight)/100;
1596                      else $iPosition[1] = $bY+$iPosition[1];
1597  
1598                      $imageXmin = $bX;
1599                      $imageXmax = $bX+$bW;
1600                      $imageYmin = $bY;
1601                      $imageYmax = $bY+$bH;
1602  
1603                      if (!$iRepeat[0] && !$iRepeat[1]) {
1604                          $imageXmin =     $iPosition[0]; $imageXmax =     $iPosition[0]+$imageWidth;
1605                          $imageYmin =     $iPosition[1]; $imageYmax =     $iPosition[1]+$imageHeight;
1606                      } else if ($iRepeat[0] && !$iRepeat[1]) {
1607                          $imageYmin =     $iPosition[1]; $imageYmax =     $iPosition[1]+$imageHeight;
1608                      } else if (!$iRepeat[0] && $iRepeat[1]) {
1609                          $imageXmin =     $iPosition[0]; $imageXmax =     $iPosition[0]+$imageWidth;
1610                      }
1611  
1612                      // build the path to display the image (because of radius)
1613                      $this->pdf->clippingPathStart($bX, $bY, $bW, $bH, $inTL, $inTR, $inBL, $inBR);
1614  
1615                      // repeat the image
1616                      for ($iY=$imageYmin; $iY<$imageYmax; $iY+=$imageHeight) {
1617                          for ($iX=$imageXmin; $iX<$imageXmax; $iX+=$imageWidth) {
1618                              $cX = null;
1619                              $cY = null;
1620                              $cW = $imageWidth;
1621                              $cH = $imageHeight;
1622                              if ($imageYmax-$iY<$imageHeight) {
1623                                  $cX = $iX;
1624                                  $cY = $iY;
1625                                  $cH = $imageYmax-$iY;
1626                              }
1627                              if ($imageXmax-$iX<$imageWidth) {
1628                                  $cX = $iX;
1629                                  $cY = $iY;
1630                                  $cW = $imageXmax-$iX;
1631                              }
1632  
1633                              $this->pdf->Image($iName, $iX, $iY, $imageWidth, $imageHeight, '', '');
1634                          }
1635                      }
1636  
1637                      // end of the path
1638                      $this->pdf->clippingPathStop();
1639                  }
1640              }
1641  
1642              // adding some loose (0.01mm)
1643              $loose = 0.01;
1644              $x-= $loose;
1645              $y-= $loose;
1646              $w+= 2.*$loose;
1647              $h+= 2.*$loose;
1648              if ($border['l']['width']) $border['l']['width']+= 2.*$loose;
1649              if ($border['t']['width']) $border['t']['width']+= 2.*$loose;
1650              if ($border['r']['width']) $border['r']['width']+= 2.*$loose;
1651              if ($border['b']['width']) $border['b']['width']+= 2.*$loose;
1652  
1653              // prepare the test on borders
1654              $testBl = ($border['l']['width'] && $border['l']['color'][0]!==null);
1655              $testBt = ($border['t']['width'] && $border['t']['color'][0]!==null);
1656              $testBr = ($border['r']['width'] && $border['r']['color'][0]!==null);
1657              $testBb = ($border['b']['width'] && $border['b']['color'][0]!==null);
1658  
1659              // draw the radius bottom-left
1660              if (is_array($outBL) && ($testBb || $testBl)) {
1661                  if ($inBL) {
1662                      $courbe = array();
1663                      $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h;
1664                      $courbe[] = $x;                        $courbe[] = $y+$h-$outBL[1];
1665                      $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h-$border['b']['width'];
1666                      $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$h-$outBL[1];
1667                      $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h-$outBL[1];
1668                  } else {
1669                      $courbe = array();
1670                      $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h;
1671                      $courbe[] = $x;                        $courbe[] = $y+$h-$outBL[1];
1672                      $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$h-$border['b']['width'];
1673                      $courbe[] = $x+$outBL[0];              $courbe[] = $y+$h-$outBL[1];
1674                  }
1675                  $this->_drawCurve($courbe, $border['l']['color']);
1676              }
1677  
1678              // draw the radius left-top
1679              if (is_array($outTL) && ($testBt || $testBl)) {
1680                  if ($inTL) {
1681                      $courbe = array();
1682                      $courbe[] = $x;                        $courbe[] = $y+$outTL[1];
1683                      $courbe[] = $x+$outTL[0];              $courbe[] = $y;
1684                      $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$outTL[1];
1685                      $courbe[] = $x+$outTL[0];              $courbe[] = $y+$border['t']['width'];
1686                      $courbe[] = $x+$outTL[0];              $courbe[] = $y+$outTL[1];
1687                  } else {
1688                      $courbe = array();
1689                      $courbe[] = $x;                        $courbe[] = $y+$outTL[1];
1690                      $courbe[] = $x+$outTL[0];              $courbe[] = $y;
1691                      $courbe[] = $x+$border['l']['width'];  $courbe[] = $y+$border['t']['width'];
1692                      $courbe[] = $x+$outTL[0];              $courbe[] = $y+$outTL[1];
1693                  }
1694                  $this->_drawCurve($courbe, $border['t']['color']);
1695              }
1696  
1697              // draw the radius top-right
1698              if (is_array($outTR) && ($testBt || $testBr)) {
1699                  if ($inTR) {
1700                      $courbe = array();
1701                      $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y;
1702                      $courbe[] = $x+$w;                       $courbe[] = $y+$outTR[1];
1703                      $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y+$border['t']['width'];
1704                      $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$outTR[1];
1705                      $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y+$outTR[1];
1706                  } else {
1707                      $courbe = array();
1708                      $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y;
1709                      $courbe[] = $x+$w;                       $courbe[] = $y+$outTR[1];
1710                      $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$border['t']['width'];
1711                      $courbe[] = $x+$w-$outTR[0];             $courbe[] = $y+$outTR[1];
1712                  }
1713                  $this->_drawCurve($courbe, $border['r']['color']);
1714              }
1715  
1716              // draw the radius right-bottom
1717              if (is_array($outBR) && ($testBb || $testBr)) {
1718                  if ($inBR) {
1719                      $courbe = array();
1720                      $courbe[] = $x+$w;                       $courbe[] = $y+$h-$outBR[1];
1721                      $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h;
1722                      $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$h-$outBR[1];
1723                      $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h-$border['b']['width'];
1724                      $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h-$outBR[1];
1725                  } else {
1726                      $courbe = array();
1727                      $courbe[] = $x+$w;                       $courbe[] = $y+$h-$outBR[1];
1728                      $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h;
1729                      $courbe[] = $x+$w-$border['r']['width']; $courbe[] = $y+$h-$border['b']['width'];
1730                      $courbe[] = $x+$w-$outBR[0];             $courbe[] = $y+$h-$outBR[1];
1731                  }
1732                  $this->_drawCurve($courbe, $border['b']['color']);
1733              }
1734  
1735              // draw the left border
1736              if ($testBl) {
1737                  $pt = array();
1738                  $pt[] = $x;                       $pt[] = $y+$h;
1739                  $pt[] = $x;                       $pt[] = $y+$h-$border['b']['width'];
1740                  $pt[] = $x;                       $pt[] = $y+$border['t']['width'];
1741                  $pt[] = $x;                       $pt[] = $y;
1742                  $pt[] = $x+$border['l']['width']; $pt[] = $y+$border['t']['width'];
1743                  $pt[] = $x+$border['l']['width']; $pt[] = $y+$h-$border['b']['width'];
1744  
1745                  $bord = 3;
1746                  if (is_array($outBL)) {
1747                      $bord-=1;
1748                      $pt[3] -= $outBL[1] - $border['b']['width'];
1749                      if ($inBL) $pt[11]-= $inBL[1];
1750                      unset($pt[0]);unset($pt[1]);
1751                  }
1752                  if (is_array($outTL)) {
1753                      $bord-=2;
1754                      $pt[5] += $outTL[1]-$border['t']['width'];
1755                      if ($inTL) $pt[9] += $inTL[1];
1756                      unset($pt[6]);unset($pt[7]);
1757                  }
1758  
1759                  $pt = array_values($pt);
1760                  $this->_drawLine($pt, $border['l']['color'], $border['l']['type'], $border['l']['width'], $bord);
1761              }
1762  
1763              // draw the top border
1764              if ($testBt) {
1765                  $pt = array();
1766                  $pt[] = $x;                          $pt[] = $y;
1767                  $pt[] = $x+$border['l']['width'];    $pt[] = $y;
1768                  $pt[] = $x+$w-$border['r']['width']; $pt[] = $y;
1769                  $pt[] = $x+$w;                       $pt[] = $y;
1770                  $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$border['t']['width'];
1771                  $pt[] = $x+$border['l']['width'];    $pt[] = $y+$border['t']['width'];
1772  
1773                  $bord = 3;
1774                  if (is_array($outTL)) {
1775                      $bord-=1;
1776                      $pt[2] += $outTL[0] - $border['l']['width'];
1777                      if ($inTL) $pt[10]+= $inTL[0];
1778                      unset($pt[0]);unset($pt[1]);
1779                  }
1780                  if (is_array($outTR)) {
1781                      $bord-=2;
1782                      $pt[4] -= $outTR[0] - $border['r']['width'];
1783                      if ($inTR) $pt[8] -= $inTR[0];
1784                      unset($pt[6]);unset($pt[7]);
1785                  }
1786  
1787                  $pt = array_values($pt);
1788                  $this->_drawLine($pt, $border['t']['color'], $border['t']['type'], $border['t']['width'], $bord);
1789              }
1790  
1791              // draw the right border
1792              if ($testBr) {
1793                  $pt = array();
1794                  $pt[] = $x+$w;                       $pt[] = $y;
1795                  $pt[] = $x+$w;                       $pt[] = $y+$border['t']['width'];
1796                  $pt[] = $x+$w;                       $pt[] = $y+$h-$border['b']['width'];
1797                  $pt[] = $x+$w;                       $pt[] = $y+$h;
1798                  $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$h-$border['b']['width'];
1799                  $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$border['t']['width'];
1800  
1801                  $bord = 3;
1802                  if (is_array($outTR)) {
1803                      $bord-=1;
1804                      $pt[3] += $outTR[1] - $border['t']['width'];
1805                      if ($inTR) $pt[11]+= $inTR[1];
1806                      unset($pt[0]);unset($pt[1]);
1807                  }
1808                  if (is_array($outBR)) {
1809                      $bord-=2;
1810                      $pt[5] -= $outBR[1] - $border['b']['width'];
1811                      if ($inBR) $pt[9] -= $inBR[1];
1812                      unset($pt[6]);unset($pt[7]);
1813                  }
1814  
1815                  $pt = array_values($pt);
1816                  $this->_drawLine($pt, $border['r']['color'], $border['r']['type'], $border['r']['width'], $bord);
1817              }
1818  
1819              // draw the bottom border
1820              if ($testBb) {
1821                  $pt = array();
1822                  $pt[] = $x+$w;                       $pt[] = $y+$h;
1823                  $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$h;
1824                  $pt[] = $x+$border['l']['width'];    $pt[] = $y+$h;
1825                  $pt[] = $x;                          $pt[] = $y+$h;
1826                  $pt[] = $x+$border['l']['width'];    $pt[] = $y+$h-$border['b']['width'];
1827                  $pt[] = $x+$w-$border['r']['width']; $pt[] = $y+$h-$border['b']['width'];
1828  
1829                  $bord = 3;
1830                  if (is_array($outBL)) {
1831                      $bord-=2;
1832                      $pt[4] += $outBL[0] - $border['l']['width'];
1833                      if ($inBL) $pt[8] += $inBL[0];
1834                      unset($pt[6]);unset($pt[7]);
1835                  }
1836                  if (is_array($outBR)) {
1837                      $bord-=1;
1838                      $pt[2] -= $outBR[0] - $border['r']['width'];
1839                      if ($inBR) $pt[10]-= $inBR[0];
1840                      unset($pt[0]);unset($pt[1]);
1841  
1842                  }
1843  
1844                  $pt = array_values($pt);
1845                  $this->_drawLine($pt, $border['b']['color'], $border['b']['type'], $border['b']['width'], $bord);
1846              }
1847  
1848              if ($background['color']) {
1849                  $this->pdf->setFillColorArray($background['color']);
1850              }
1851  
1852              return true;
1853          }
1854  
1855          /**
1856           * draw a curve (for border radius)
1857           *
1858           * @access protected
1859           * @param  array $pt
1860           * @param  array $color
1861           */
1862          protected function _drawCurve($pt, $color)
1863          {
1864              $this->pdf->setFillColorArray($color);
1865  
1866              if (count($pt)==10)
1867                  $this->pdf->drawCurve($pt[0], $pt[1], $pt[2], $pt[3], $pt[4], $pt[5], $pt[6], $pt[7], $pt[8], $pt[9]);
1868              else
1869                  $this->pdf->drawCorner($pt[0], $pt[1], $pt[2], $pt[3], $pt[4], $pt[5], $pt[6], $pt[7]);
1870          }
1871  
1872          /**
1873           * draw a ligne with a specific type, and specific start and end for radius
1874           *
1875           * @access protected
1876           * @param  array   $pt
1877           * @param  float   $color
1878           * @param  string  $type (dashed, dotted, double, solid)
1879           * @param  float   $width
1880           * @param  integer $radius (binary from 0 to 3 with 1=>start with a radius, 2=>end with a radius)
1881           */
1882          protected function _drawLine($pt, $color, $type, $width, $radius=3)
1883          {
1884              // set the fill color
1885              $this->pdf->setFillColorArray($color);
1886  
1887              // if dashed or dotted
1888              if ($type=='dashed' || $type=='dotted') {
1889  
1890                  // clean the end of the line, if radius
1891                  if ($radius==1) {
1892                      $tmp = array(); $tmp[]=$pt[0]; $tmp[]=$pt[1]; $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[8]; $tmp[]=$pt[9];
1893                      $this->pdf->Polygon($tmp, 'F');
1894  
1895                      $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];
1896                      $pt = $tmp;
1897                  } else if ($radius==2) {
1898                      $tmp = array(); $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[4]; $tmp[]=$pt[5]; $tmp[]=$pt[6]; $tmp[]=$pt[7];
1899                      $this->pdf->Polygon($tmp, 'F');
1900  
1901                      $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];
1902                      $pt = $tmp;
1903                  } else if ($radius==3) {
1904                      $tmp = array(); $tmp[]=$pt[0]; $tmp[]=$pt[1]; $tmp[]=$pt[2]; $tmp[]=$pt[3]; $tmp[]=$pt[10]; $tmp[]=$pt[11];
1905                      $this->pdf->Polygon($tmp, 'F');
1906  
1907                      $tmp = array(); $tmp[]=$pt[4]; $tmp[]=$pt[5]; $tmp[]=$pt[6]; $tmp[]=$pt[7]; $tmp[]=$pt[8]; $tmp[]=$pt[9];
1908                      $this->pdf->Polygon($tmp, 'F');
1909  
1910                      $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];
1911                      $pt = $tmp;
1912                  }
1913  
1914                  // horisontal or vertical line
1915                  if ($pt[2]==$pt[0]) {
1916                      $l = abs(($pt[3]-$pt[1])*0.5);
1917                      $px = 0;
1918                      $py = $width;
1919                      $x1 = $pt[0]; $y1 = ($pt[3]+$pt[1])*0.5;
1920                      $x2 = $pt[6]; $y2 = ($pt[7]+$pt[5])*0.5;
1921                  } else {
1922                      $l = abs(($pt[2]-$pt[0])*0.5);
1923                      $px = $width;
1924                      $py = 0;
1925                      $x1 = ($pt[2]+$pt[0])*0.5; $y1 = $pt[1];
1926                      $x2 = ($pt[6]+$pt[4])*0.5; $y2 = $pt[7];
1927                  }
1928  
1929                  // if dashed : 3x bigger than dotted
1930                  if ($type=='dashed') {
1931                      $px = $px*3.;
1932                      $py = $py*3.;
1933                  }
1934                  $mode = ($l/($px+$py)<.5);
1935  
1936                  // display the dotted/dashed line
1937                  for ($i=0; $l-($px+$py)*($i-0.5)>0; $i++) {
1938                      if (($i%2)==$mode) {
1939                          $j = $i-0.5;
1940                          $lx1 = $px*($j);   if ($lx1<-$l) $lx1 =-$l;
1941                          $ly1 = $py*($j);   if ($ly1<-$l) $ly1 =-$l;
1942                          $lx2 = $px*($j+1); if ($lx2>$l)  $lx2 = $l;
1943                          $ly2 = $py*($j+1); if ($ly2>$l)  $ly2 = $l;
1944  
1945                          $tmp = array();
1946                          $tmp[] = $x1+$lx1; $tmp[] = $y1+$ly1;
1947                          $tmp[] = $x1+$lx2; $tmp[] = $y1+$ly2;
1948                          $tmp[] = $x2+$lx2; $tmp[] = $y2+$ly2;
1949                          $tmp[] = $x2+$lx1; $tmp[] = $y2+$ly1;
1950                          $this->pdf->Polygon($tmp, 'F');
1951  
1952                          if ($j>0) {
1953                              $tmp = array();
1954                              $tmp[] = $x1-$lx1; $tmp[] = $y1-$ly1;
1955                              $tmp[] = $x1-$lx2; $tmp[] = $y1-$ly2;
1956                              $tmp[] = $x2-$lx2; $tmp[] = $y2-$ly2;
1957                              $tmp[] = $x2-$lx1; $tmp[] = $y2-$ly1;
1958                              $this->pdf->Polygon($tmp, 'F');
1959                          }
1960                      }
1961                  }
1962              } else if ($type=='double') {
1963  
1964                  // if double, 2 lines : 0=>1/3 and 2/3=>1
1965                  $pt1 = $pt;
1966                  $pt2 = $pt;
1967  
1968                  if (count($pt)==12) {
1969                      // line 1
1970                      $pt1[0] = ($pt[0]-$pt[10])*0.33 + $pt[10];
1971                      $pt1[1] = ($pt[1]-$pt[11])*0.33 + $pt[11];
1972                      $pt1[2] = ($pt[2]-$pt[10])*0.33 + $pt[10];
1973                      $pt1[3] = ($pt[3]-$pt[11])*0.33 + $pt[11];
1974                      $pt1[4] = ($pt[4]-$pt[8])*0.33 + $pt[8];
1975                      $pt1[5] = ($pt[5]-$pt[9])*0.33 + $pt[9];
1976                      $pt1[6] = ($pt[6]-$pt[8])*0.33 + $pt[8];
1977                      $pt1[7] = ($pt[7]-$pt[9])*0.33 + $pt[9];
1978                      $pt2[10]= ($pt[10]-$pt[0])*0.33 + $pt[0];
1979                      $pt2[11]= ($pt[11]-$pt[1])*0.33 + $pt[1];
1980  
1981                      // line 2
1982                      $pt2[2] = ($pt[2] -$pt[0])*0.33 + $pt[0];
1983                      $pt2[3] = ($pt[3] -$pt[1])*0.33 + $pt[1];
1984                      $pt2[4] = ($pt[4] -$pt[6])*0.33 + $pt[6];
1985                      $pt2[5] = ($pt[5] -$pt[7])*0.33 + $pt[7];
1986                      $pt2[8] = ($pt[8] -$pt[6])*0.33 + $pt[6];
1987                      $pt2[9] = ($pt[9] -$pt[7])*0.33 + $pt[7];
1988                  } else {
1989                      // line 1
1990                      $pt1[0] = ($pt[0]-$pt[6])*0.33 + $pt[6];
1991                      $pt1[1] = ($pt[1]-$pt[7])*0.33 + $pt[7];
1992                      $pt1[2] = ($pt[2]-$pt[4])*0.33 + $pt[4];
1993                      $pt1[3] = ($pt[3]-$pt[5])*0.33 + $pt[5];
1994  
1995                      // line 2
1996                      $pt2[6] = ($pt[6]-$pt[0])*0.33 + $pt[0];
1997                      $pt2[7] = ($pt[7]-$pt[1])*0.33 + $pt[1];
1998                      $pt2[4] = ($pt[4]-$pt[2])*0.33 + $pt[2];
1999                      $pt2[5] = ($pt[5]-$pt[3])*0.33 + $pt[3];
2000                  }
2001                  $this->pdf->Polygon($pt1, 'F');
2002                  $this->pdf->Polygon($pt2, 'F');
2003              } else if ($type=='solid') {
2004                  // solid line : draw directly the polygon
2005                  $this->pdf->Polygon($pt, 'F');
2006              }
2007          }
2008  
2009          /**
2010           * prepare a transform matrix, only for drawing a SVG graphic
2011           *
2012           * @access protected
2013           * @param  string $transform
2014           * @return array  $matrix
2015           */
2016          protected function _prepareTransform($transform)
2017          {
2018              // it can not be  empty
2019              if (!$transform) return null;
2020  
2021              // sctions must be like scale(...)
2022              if (!preg_match_all('/([a-z]+)\(([^\)]*)\)/isU', $transform, $match)) return null;
2023  
2024              // prepare the list of the actions
2025              $actions = array();
2026  
2027              // for actions
2028              for ($k=0; $k<count($match[0]); $k++) {
2029  
2030                  // get the name of the action
2031                  $name = strtolower($match[1][$k]);
2032  
2033                  // get the parameters of the action
2034                  $val = explode(',', trim($match[2][$k]));
2035                  foreach ($val as $i => $j) {
2036                      $val[$i] = trim($j);
2037                  }
2038  
2039                  // prepare the matrix, depending on the action
2040                  switch($name)
2041                  {
2042                      case 'scale':
2043                          if (!isset($val[0])) $val[0] = 1.;      else $val[0] = 1.*$val[0];
2044                          if (!isset($val[1])) $val[1] = $val[0]; else $val[1] = 1.*$val[1];
2045                          $actions[] = array($val[0],0,0,$val[1],0,0);
2046                          break;
2047  
2048                      case 'translate':
2049                          if (!isset($val[0])) $val[0] = 0.; else $val[0] = $this->parsingCss->ConvertToMM($val[0], $this->_isInDraw['w']);
2050                          if (!isset($val[1])) $val[1] = 0.; else $val[1] = $this->parsingCss->ConvertToMM($val[1], $this->_isInDraw['h']);
2051                          $actions[] = array(1,0,0,1,$val[0],$val[1]);
2052                          break;
2053  
2054                      case 'rotate':
2055                          if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*M_PI/180.;
2056                          if (!isset($val[1])) $val[1] = 0.; else $val[1] = $this->parsingCss->ConvertToMM($val[1], $this->_isInDraw['w']);
2057                          if (!isset($val[2])) $val[2] = 0.; else $val[2] = $this->parsingCss->ConvertToMM($val[2], $this->_isInDraw['h']);
2058                          if ($val[1] || $val[2]) $actions[] = array(1,0,0,1,-$val[1],-$val[2]);
2059                          $actions[] = array(cos($val[0]),sin($val[0]),-sin($val[0]),cos($val[0]),0,0);
2060                          if ($val[1] || $val[2]) $actions[] = array(1,0,0,1,$val[1],$val[2]);
2061                          break;
2062  
2063                      case 'skewx':
2064                          if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*M_PI/180.;
2065                          $actions[] = array(1,0,tan($val[0]),1,0,0);
2066                          break;
2067  
2068                      case 'skewy':
2069                          if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*M_PI/180.;
2070                          $actions[] = array(1,tan($val[0]),0,1,0,0);
2071                          break;
2072                      case 'matrix':
2073                          if (!isset($val[0])) $val[0] = 0.; else $val[0] = $val[0]*1.;
2074                          if (!isset($val[1])) $val[1] = 0.; else $val[1] = $val[1]*1.;
2075                          if (!isset($val[2])) $val[2] = 0.; else $val[2] = $val[2]*1.;
2076                          if (!isset($val[3])) $val[3] = 0.; else $val[3] = $val[3]*1.;
2077                          if (!isset($val[4])) $val[4] = 0.; else $val[4] = $this->parsingCss->ConvertToMM($val[4], $this->_isInDraw['w']);
2078                          if (!isset($val[5])) $val[5] = 0.; else $val[5] = $this->parsingCss->ConvertToMM($val[5], $this->_isInDraw['h']);
2079                          $actions[] =$val;
2080                          break;
2081                  }
2082              }
2083  
2084              // if ther is no actions => return
2085              if (!$actions) return null;
2086  
2087              // get the first matrix
2088              $m = $actions[0]; unset($actions[0]);
2089  
2090              // foreach matrix => multiply to the last matrix
2091              foreach ($actions as $n) {
2092                  $m = array(
2093                      $m[0]*$n[0]+$m[2]*$n[1],
2094                      $m[1]*$n[0]+$m[3]*$n[1],
2095                      $m[0]*$n[2]+$m[2]*$n[3],
2096                      $m[1]*$n[2]+$m[3]*$n[3],
2097                      $m[0]*$n[4]+$m[2]*$n[5]+$m[4],
2098                      $m[1]*$n[4]+$m[3]*$n[5]+$m[5]
2099                  );
2100              }
2101  
2102              // return the matrix
2103              return $m;
2104          }
2105  
2106          /**
2107           * @access protected
2108           * @param  &array $cases
2109           * @param  &array $corr
2110           */
2111          protected function _calculateTableCellSize(&$cases, &$corr)
2112          {
2113              if (!isset($corr[0])) return true;
2114  
2115              // for each cell without colspan, we get the max width for each column
2116              $sw = array();
2117              for ($x=0; $x<count($corr[0]); $x++) {
2118                  $m=0;
2119                  for ($y=0; $y<count($corr); $y++) {
2120                      if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][2]==1) {
2121                          $m = max($m, $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w']);
2122                      }
2123                  }
2124                  $sw[$x] = $m;
2125              }
2126  
2127              // for each cell with colspan, we adapt the width of each column
2128              for ($x=0; $x<count($corr[0]); $x++) {
2129                  for ($y=0; $y<count($corr); $y++) {
2130                      if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][2]>1) {
2131  
2132                          // sum the max width of each column in colspan
2133                          $s = 0; for ($i=0; $i<$corr[$y][$x][2]; $i++) $s+= $sw[$x+$i];
2134  
2135                          // if the max width is < the width of the cell with colspan => we adapt the width of each max width
2136                          if ($s>0 && $s<$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w']) {
2137                              for ($i=0; $i<$corr[$y][$x][2]; $i++) {
2138                                  $sw[$x+$i] = $sw[$x+$i]/$s*$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'];
2139                              }
2140                          }
2141                      }
2142                  }
2143              }
2144  
2145              // set the new width, for each cell
2146              for ($x=0; $x<count($corr[0]); $x++) {
2147                  for ($y=0; $y<count($corr); $y++) {
2148                      if (isset($corr[$y][$x]) && is_array($corr[$y][$x])) {
2149                          // without colspan
2150                          if ($corr[$y][$x][2]==1) {
2151                              $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'] = $sw[$x];
2152                          // with colspan
2153                          } else {
2154                              $s = 0;
2155                              for ($i=0; $i<$corr[$y][$x][2]; $i++) {
2156                                  $s+= $sw[$x+$i];
2157                              }
2158                              $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'] = $s;
2159                          }
2160                      }
2161                  }
2162              }
2163  
2164              // for each cell without rowspan, we get the max height for each line
2165              $sh = array();
2166              for ($y=0; $y<count($corr); $y++) {
2167                  $m=0;
2168                  for ($x=0; $x<count($corr[0]); $x++) {
2169                      if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][3]==1) {
2170                          $m = max($m, $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h']);
2171                      }
2172                  }
2173                  $sh[$y] = $m;
2174              }
2175  
2176              // for each cell with rowspan, we adapt the height of each line
2177              for ($y=0; $y<count($corr); $y++) {
2178                  for ($x=0; $x<count($corr[0]); $x++) {
2179                      if (isset($corr[$y][$x]) && is_array($corr[$y][$x]) && $corr[$y][$x][3]>1) {
2180  
2181                          // sum the max height of each line in rowspan
2182                          $s = 0; for ($i=0; $i<$corr[$y][$x][3]; $i++) $s+= $sh[$y+$i];
2183  
2184                          // if the max height is < the height of the cell with rowspan => we adapt the height of each max height
2185                          if ($s>0 && $s<$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h']) {
2186                              for ($i=0; $i<$corr[$y][$x][3]; $i++) {
2187                                  $sh[$y+$i] = $sh[$y+$i]/$s*$cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h'];
2188                              }
2189                          }
2190                      }
2191                  }
2192              }
2193  
2194              // set the new height, for each cell
2195              for ($y=0; $y<count($corr); $y++) {
2196                  for ($x=0; $x<count($corr[0]); $x++) {
2197                      if (isset($corr[$y][$x]) && is_array($corr[$y][$x])) {
2198                          // without rowspan
2199                          if ($corr[$y][$x][3]==1) {
2200                              $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h'] = $sh[$y];
2201                          // with rowspan
2202                          } else {
2203                              $s = 0;
2204                              for ($i=0; $i<$corr[$y][$x][3]; $i++) {
2205                                  $s+= $sh[$y+$i];
2206                              }
2207                              $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['h'] = $s;
2208  
2209                              for ($j=1; $j<$corr[$y][$x][3]; $j++) {
2210                                  $tx = $x+1;
2211                                  $ty = $y+$j;
2212                                  for (true; isset($corr[$ty][$tx]) && !is_array($corr[$ty][$tx]); $tx++);
2213                                  if (isset($corr[$ty][$tx])) {
2214                                      $cases[$corr[$ty][$tx][1]][$corr[$ty][$tx][0]]['dw']+= $cases[$corr[$y][$x][1]][$corr[$y][$x][0]]['w'];
2215                                  }
2216                              }
2217                          }
2218                      }
2219                  }
2220              }
2221          }
2222  
2223          /**
2224           * tag : PAGE
2225           * mode : OPEN
2226           *
2227           * @param  array $param
2228           * @return boolean
2229           */
2230          protected function _tag_open_PAGE($param)
2231          {
2232              if ($this->_isForOneLine) return false;
2233              if ($this->_debugActif) $this->_DEBUG_add('PAGE '.($this->_page+1), true);
2234  
2235              $newPageSet= (!isset($param['pageset']) || $param['pageset']!='old');
2236  
2237              $resetPageNumber = (isset($param['pagegroup']) && $param['pagegroup']=='new');
2238  
2239              $this->_maxH = 0;
2240  
2241              // if new page set asked
2242              if ($newPageSet) {
2243                  $this->_subHEADER = array();
2244                  $this->_subFOOTER = array();
2245  
2246                  // orientation
2247                  $orientation = '';
2248                  if (isset($param['orientation'])) {
2249                      $param['orientation'] = strtolower($param['orientation']);
2250                      if ($param['orientation']=='p')         $orientation = 'P';
2251                      if ($param['orientation']=='portrait')  $orientation = 'P';
2252  
2253                      if ($param['orientation']=='l')         $orientation = 'L';
2254                      if ($param['orientation']=='paysage')   $orientation = 'L';
2255                      if ($param['orientation']=='landscape') $orientation = 'L';
2256                  }
2257  
2258                  // format
2259                  $format = null;
2260                  if (isset($param['format'])) {
2261                      $format = strtolower($param['format']);
2262                      if (preg_match('/^([0-9]+)x([0-9]+)$/isU', $format, $match)) {
2263                          $format = array(intval($match[1]), intval($match[2]));
2264                      }
2265                  }
2266  
2267                  // background
2268                  $background = array();
2269                  if (isset($param['backimg'])) {
2270                      $background['img']    = isset($param['backimg'])  ? $param['backimg']  : '';       // src of the image
2271                      $background['posX']   = isset($param['backimgx']) ? $param['backimgx'] : 'center'; // horizontale position of the image
2272                      $background['posY']   = isset($param['backimgy']) ? $param['backimgy'] : 'middle'; // vertical position of the image
2273                      $background['width']  = isset($param['backimgw']) ? $param['backimgw'] : '100%';   // width of the image (100% = page width)
2274  
2275                      // convert the src of the image, if parameters
2276                      $background['img'] = str_replace('&amp;', '&', $background['img']);
2277  
2278                      // convert the positions
2279                      if ($background['posX']=='left')    $background['posX'] = '0%';
2280                      if ($background['posX']=='center')  $background['posX'] = '50%';
2281                      if ($background['posX']=='right')   $background['posX'] = '100%';
2282                      if ($background['posY']=='top')     $background['posY'] = '0%';
2283                      if ($background['posY']=='middle')  $background['posY'] = '50%';
2284                      if ($background['posY']=='bottom')  $background['posY'] = '100%';
2285  
2286                      if ($background['img']) {
2287                          // get the size of the image
2288                          // WARNING : if URL, "allow_url_fopen" must turned to "on" in php.ini
2289                          $infos=@getimagesize($background['img']);
2290                          if (count($infos)>1) {
2291                              $imageWidth = $this->parsingCss->ConvertToMM($background['width'], $this->pdf->getW());
2292                              $imageHeight = $imageWidth*$infos[1]/$infos[0];
2293  
2294                              $background['width'] = $imageWidth;
2295                              $background['posX']  = $this->parsingCss->ConvertToMM($background['posX'], $this->pdf->getW() - $imageWidth);
2296                              $background['posY']  = $this->parsingCss->ConvertToMM($background['posY'], $this->pdf->getH() - $imageHeight);
2297                          } else {
2298                              $background = array();
2299                          }
2300                      } else {
2301                          $background = array();
2302                      }
2303                  }
2304  
2305                  // margins of the page
2306                  $background['top']    = isset($param['backtop'])    ? $param['backtop']    : '0';
2307                  $background['bottom'] = isset($param['backbottom']) ? $param['backbottom'] : '0';
2308                  $background['left']   = isset($param['backleft'])   ? $param['backleft']   : '0';
2309                  $background['right']  = isset($param['backright'])  ? $param['backright']  : '0';
2310  
2311                  // if no unit => mm
2312                  if (preg_match('/^([0-9]*)$/isU', $background['top']))    $background['top']    .= 'mm';
2313                  if (preg_match('/^([0-9]*)$/isU', $background['bottom'])) $background['bottom'] .= 'mm';
2314                  if (preg_match('/^([0-9]*)$/isU', $background['left']))   $background['left']   .= 'mm';
2315                  if (preg_match('/^([0-9]*)$/isU', $background['right']))  $background['right']  .= 'mm';
2316  
2317                  // convert to mm
2318                  $background['top']    = $this->parsingCss->ConvertToMM($background['top'], $this->pdf->getH());
2319                  $background['bottom'] = $this->parsingCss->ConvertToMM($background['bottom'], $this->pdf->getH());
2320                  $background['left']   = $this->parsingCss->ConvertToMM($background['left'], $this->pdf->getW());
2321                  $background['right']  = $this->parsingCss->ConvertToMM($background['right'], $this->pdf->getW());
2322  
2323                  // get the background color
2324                  $res = false;
2325                  $background['color']    = isset($param['backcolor'])    ? $this->parsingCss->convertToColor($param['backcolor'], $res) : null;
2326                  if (!$res) $background['color'] = null;
2327  
2328                  $this->parsingCss->save();
2329                  $this->parsingCss->analyse('PAGE', $param);
2330                  $this->parsingCss->setPosition();
2331                  $this->parsingCss->fontSet();
2332  
2333                  // new page
2334                  $this->_setNewPage($format, $orientation, $background, null, $resetPageNumber);
2335  
2336                  // automatic footer
2337                  if (isset($param['footer'])) {
2338                      $lst = explode(';', $param['footer']);
2339                      foreach ($lst as $key => $val) $lst[$key] = trim(strtolower($val));
2340                      $page    = in_array('page', $lst);
2341                      $date    = in_array('date', $lst);
2342                      $hour    = in_array('heure', $lst);
2343                      $form    = in_array('form', $lst);
2344                  } else {
2345                      $page    = null;
2346                      $date    = null;
2347                      $hour    = null;
2348                      $form    = null;
2349                  }
2350                  $this->pdf->SetMyFooter($page, $date, $hour, $form);
2351              // else => we use the last page set used
2352              } else {
2353                  $this->parsingCss->save();
2354                  $this->parsingCss->analyse('PAGE', $param);
2355                  $this->parsingCss->setPosition();
2356                  $this->parsingCss->fontSet();
2357  
2358                  $this->_setNewPage(null, null, null, null, $resetPageNumber);
2359              }
2360  
2361              return true;
2362          }
2363  
2364          /**
2365           * tag : PAGE
2366           * mode : CLOSE
2367           *
2368           * @param  array $param
2369           * @return boolean
2370           */
2371          protected function _tag_close_PAGE($param)
2372          {
2373              if ($this->_isForOneLine) return false;
2374  
2375              $this->_maxH = 0;
2376  
2377              $this->parsingCss->load();
2378              $this->parsingCss->fontSet();
2379  
2380              if ($this->_debugActif) $this->_DEBUG_add('PAGE '.$this->_page, false);
2381  
2382              return true;
2383          }
2384  
2385          /**
2386           * tag : PAGE_HEADER
2387           * mode : OPEN
2388           *
2389           * @param  array $param
2390           * @return boolean
2391           */
2392          protected function _tag_open_PAGE_HEADER($param)
2393          {
2394              if ($this->_isForOneLine) return false;
2395  
2396              $this->_subHEADER = array();
2397              for ($this->_parsePos; $this->_parsePos<count($this->parsingHtml->code); $this->_parsePos++) {
2398                  $action = $this->parsingHtml->code[$this->_parsePos];
2399                  if ($action['name']=='page_header') $action['name']='page_header_sub';
2400                  $this->_subHEADER[] = $action;
2401                  if (strtolower($action['name'])=='page_header_sub' && $action['close']) break;
2402              }
2403  
2404              $this->_setPageHeader();
2405  
2406              return true;
2407          }
2408  
2409          /**
2410           * tag : PAGE_FOOTER
2411           * mode : OPEN
2412           *
2413           * @param  array $param
2414           * @return boolean
2415           */
2416          protected function _tag_open_PAGE_FOOTER($param)
2417          {
2418              if ($this->_isForOneLine) return false;
2419  
2420              $this->_subFOOTER = array();
2421              for ($this->_parsePos; $this->_parsePos<count($this->parsingHtml->code); $this->_parsePos++) {
2422                  $action = $this->parsingHtml->code[$this->_parsePos];
2423                  if ($action['name']=='page_footer') $action['name']='page_footer_sub';
2424                  $this->_subFOOTER[] = $action;
2425                  if (strtolower($action['name'])=='page_footer_sub' && $action['close']) break;
2426              }
2427  
2428              $this->_setPageFooter();
2429  
2430              return true;
2431          }
2432  
2433          /**
2434           * It is not a real tag. Does not use it directly
2435           *
2436           * @param  array $param
2437           * @return boolean
2438           */
2439          protected function _tag_open_PAGE_HEADER_SUB($param)
2440          {
2441              if ($this->_isForOneLine) return false;
2442  
2443              // save the current stat
2444              $this->_subSTATES = array();
2445              $this->_subSTATES['x']  = $this->pdf->getX();
2446              $this->_subSTATES['y']  = $this->pdf->getY();
2447              $this->_subSTATES['s']  = $this->parsingCss->value;
2448              $this->_subSTATES['t']  = $this->parsingCss->table;
2449              $this->_subSTATES['ml'] = $this->_margeLeft;
2450              $this->_subSTATES['mr'] = $this->_margeRight;
2451              $this->_subSTATES['mt'] = $this->_margeTop;
2452              $this->_subSTATES['mb'] = $this->_margeBottom;
2453              $this->_subSTATES['mp'] = $this->_pageMarges;
2454  
2455              // new stat for the header
2456              $this->_pageMarges = array();
2457              $this->_margeLeft    = $this->_defaultLeft;
2458              $this->_margeRight   = $this->_defaultRight;
2459              $this->_margeTop     = $this->_defaultTop;
2460              $this->_margeBottom  = $this->_defaultBottom;
2461              $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2462              $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2463              $this->pdf->setXY($this->_defaultLeft, $this->_defaultTop);
2464  
2465              $this->parsingCss->initStyle();
2466              $this->parsingCss->resetStyle();
2467              $this->parsingCss->value['width'] = $this->pdf->getW() - $this->_defaultLeft - $this->_defaultRight;
2468              $this->parsingCss->table = array();
2469  
2470              $this->parsingCss->save();
2471              $this->parsingCss->analyse('page_header_sub', $param);
2472              $this->parsingCss->setPosition();
2473              $this->parsingCss->fontSet();
2474              $this->_setNewPositionForNewLine();
2475              return true;
2476          }
2477  
2478          /**
2479           * It is not a real tag. Does not use it directly
2480           *
2481           * @param  array $param
2482           * @return boolean
2483           */
2484          protected function _tag_close_PAGE_HEADER_SUB($param)
2485          {
2486              if ($this->_isForOneLine) return false;
2487  
2488              $this->parsingCss->load();
2489  
2490              // restore the stat
2491              $this->parsingCss->value = $this->_subSTATES['s'];
2492              $this->parsingCss->table = $this->_subSTATES['t'];
2493              $this->_pageMarges       = $this->_subSTATES['mp'];
2494              $this->_margeLeft        = $this->_subSTATES['ml'];
2495              $this->_margeRight       = $this->_subSTATES['mr'];
2496              $this->_margeTop         = $this->_subSTATES['mt'];
2497              $this->_margeBottom      = $this->_subSTATES['mb'];
2498              $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2499              $this->pdf->setbMargin($this->_margeBottom);
2500              $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2501              $this->pdf->setXY($this->_subSTATES['x'], $this->_subSTATES['y']);
2502  
2503              $this->parsingCss->fontSet();
2504              $this->_maxH = 0;
2505  
2506              return true;
2507          }
2508  
2509          /**
2510           * It is not a real tag. Does not use it directly
2511           *
2512           * @param  array $param
2513           * @return boolean
2514           */
2515          protected function _tag_open_PAGE_FOOTER_SUB($param)
2516          {
2517              if ($this->_isForOneLine) return false;
2518  
2519              // save the current stat
2520              $this->_subSTATES = array();
2521              $this->_subSTATES['x']    = $this->pdf->getX();
2522              $this->_subSTATES['y']    = $this->pdf->getY();
2523              $this->_subSTATES['s']    = $this->parsingCss->value;
2524              $this->_subSTATES['t']    = $this->parsingCss->table;
2525              $this->_subSTATES['ml']    = $this->_margeLeft;
2526              $this->_subSTATES['mr']    = $this->_margeRight;
2527              $this->_subSTATES['mt']    = $this->_margeTop;
2528              $this->_subSTATES['mb']    = $this->_margeBottom;
2529              $this->_subSTATES['mp']    = $this->_pageMarges;
2530  
2531              // new stat for the footer
2532              $this->_pageMarges  = array();
2533              $this->_margeLeft   = $this->_defaultLeft;
2534              $this->_margeRight  = $this->_defaultRight;
2535              $this->_margeTop    = $this->_defaultTop;
2536              $this->_margeBottom = $this->_defaultBottom;
2537              $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2538              $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2539              $this->pdf->setXY($this->_defaultLeft, $this->_defaultTop);
2540  
2541              $this->parsingCss->initStyle();
2542              $this->parsingCss->resetStyle();
2543              $this->parsingCss->value['width']    = $this->pdf->getW() - $this->_defaultLeft - $this->_defaultRight;
2544              $this->parsingCss->table                = array();
2545  
2546              // we create a sub HTML2PFDF, and we execute on it the content of the footer, to get the height of it
2547              $sub = null;
2548              $this->_createSubHTML($sub);
2549              $sub->parsingHtml->code = $this->parsingHtml->getLevel($this->_parsePos);
2550              $sub->_makeHTMLcode();
2551              $this->pdf->setY($this->pdf->getH() - $sub->_maxY - $this->_defaultBottom - 0.01);
2552              $this->_destroySubHTML($sub);
2553  
2554              $this->parsingCss->save();
2555              $this->parsingCss->analyse('page_footer_sub', $param);
2556              $this->parsingCss->setPosition();
2557              $this->parsingCss->fontSet();
2558              $this->_setNewPositionForNewLine();
2559  
2560              return true;
2561          }
2562  
2563          /**
2564           * It is not a real tag. Does not use it directly
2565           *
2566           * @param  array $param
2567           * @return boolean
2568           */
2569          protected function _tag_close_PAGE_FOOTER_SUB($param)
2570          {
2571              if ($this->_isForOneLine) return false;
2572  
2573              $this->parsingCss->load();
2574  
2575              $this->parsingCss->value                = $this->_subSTATES['s'];
2576              $this->parsingCss->table                = $this->_subSTATES['t'];
2577              $this->_pageMarges                 = $this->_subSTATES['mp'];
2578              $this->_margeLeft                = $this->_subSTATES['ml'];
2579              $this->_margeRight                = $this->_subSTATES['mr'];
2580              $this->_margeTop                    = $this->_subSTATES['mt'];
2581              $this->_margeBottom                = $this->_subSTATES['mb'];
2582              $this->pdf->SetMargins($this->_margeLeft, $this->_margeTop, $this->_margeRight);
2583              $this->pdf->SetAutoPageBreak(false, $this->_margeBottom);
2584              $this->pdf->setXY($this->_subSTATES['x'], $this->_subSTATES['y']);
2585  
2586              $this->parsingCss->fontSet();
2587              $this->_maxH = 0;
2588  
2589              return true;
2590          }
2591  
2592          /**
2593           * tag : NOBREAK
2594           * mode : OPEN
2595           *
2596           * @param  array $param
2597           * @return boolean
2598           */
2599          protected function _tag_open_NOBREAK($param)
2600          {
2601              if ($this->_isForOneLine) return false;
2602  
2603              $this->_maxH = 0;
2604  
2605              // create a sub HTML2PDF to execute the content of the tag, to get the dimensions
2606              $sub = null;
2607              $this->_createSubHTML($sub);
2608              $sub->parsingHtml->code = $this->parsingHtml->getLevel($this->_parsePos);
2609              $sub->_makeHTMLcode();
2610              $y = $this->pdf->getY();
2611  
2612              // if the content does not fit on the page => new page
2613              if (
2614                  $sub->_maxY < ($this->pdf->getH() - $this->pdf->gettMargin()-$this->pdf->getbMargin()) &&
2615                  $y + $sub->_maxY>=($this->pdf->getH() - $this->pdf->getbMargin())
2616              ) {
2617                  $this->_setNewPage();
2618              }
2619  
2620              // destroy the sub HTML2PDF
2621              $this->_destroySubHTML($sub);
2622  
2623              return true;
2624          }
2625  
2626  
2627          /**
2628           * tag : NOBREAK
2629           * mode : CLOSE
2630           *
2631           * @param  array $param
2632           * @return boolean
2633           */
2634          protected function _tag_close_NOBREAK($param)
2635          {
2636              if ($this->_isForOneLine) return false;
2637  
2638              $this->_maxH = 0;
2639  
2640              return true;
2641          }
2642  
2643          /**
2644           * tag : DIV
2645           * mode : OPEN
2646           *
2647           * @param  array $param
2648           * @param  string $other name of tag that used the div tag
2649           * @return boolean
2650           */
2651          protected function _tag_open_DIV($param, $other = 'div')
2652          {
2653              if ($this->_isForOneLine) return false;
2654              if ($this->_debugActif) $this->_DEBUG_add(strtoupper($other), true);
2655  
2656              $this->parsingCss->save();
2657              $this->parsingCss->analyse($other, $param);
2658              $this->parsingCss->fontSet();
2659  
2660              // for fieldset and legend
2661              if (in_array($other, array('fieldset', 'legend'))) {
2662                  if (isset($param['moveTop']))  $this->parsingCss->value['margin']['t']    += $param['moveTop'];
2663                  if (isset($param['moveLeft'])) $this->parsingCss->value['margin']['l']    += $param['moveLeft'];
2664                  if (isset($param['moveDown'])) $this->parsingCss->value['margin']['b']    += $param['moveDown'];
2665              }
2666  
2667              $alignObject = null;
2668              if ($this->parsingCss->value['margin-auto']) $alignObject = 'center';
2669  
2670              $marge = array();
2671              $marge['l'] = $this->parsingCss->value['border']['l']['width'] + $this->parsingCss->value['padding']['l']+0.03;
2672              $marge['r'] = $this->parsingCss->value['border']['r']['width'] + $this->parsingCss->value['padding']['r']+0.03;
2673              $marge['t'] = $this->parsingCss->value['border']['t']['width'] + $this->parsingCss->value['padding']['t']+0.03;
2674              $marge['b'] = $this->parsingCss->value['border']['b']['width'] + $this->parsingCss->value['padding']['b']+0.03;
2675  
2676              // extract the content of the div
2677              $level = $this->parsingHtml->getLevel($this->_parsePos);
2678  
2679              // create a sub HTML2PDF to get the dimensions of the content of the div
2680              $w = 0; $h = 0;
2681              if (count($level)) {
2682                  $sub = null;
2683                  $this->_createSubHTML($sub);
2684                  $sub->parsingHtml->code = $level;
2685                  $sub->_makeHTMLcode();
2686                  $w = $sub->_maxX;
2687                  $h = $sub->_maxY;
2688                  $this->_destroySubHTML($sub);
2689              }
2690              $wReel = $w;
2691              $hReel = $h;
2692  
2693              $w+= $marge['l']+$marge['r']+0.001;
2694              $h+= $marge['t']+$marge['b']+0.001;
2695  
2696              if ($this->parsingCss->value['overflow']=='hidden') {
2697                  $overW = max($w, $this->parsingCss->value['width']);
2698                  $overH = max($h, $this->parsingCss->value['height']);
2699                  $overflow = true;
2700                  $this->parsingCss->value['old_maxX'] = $this->_maxX;
2701                  $this->parsingCss->value['old_maxY'] = $this->_maxY;
2702                  $this->parsingCss->value['old_maxH'] = $this->_maxH;
2703                  $this->parsingCss->value['old_overflow'] = $this->_isInOverflow;
2704                  $this->_isInOverflow = true;
2705              } else {
2706                  $overW = null;
2707                  $overH = null;
2708                  $overflow = false;
2709                  $this->parsingCss->value['width']    = max($w, $this->parsingCss->value['width']);
2710                  $this->parsingCss->value['height']    = max($h, $this->parsingCss->value['height']);
2711              }
2712  
2713              switch($this->parsingCss->value['rotate'])
2714              {
2715                  case 90:
2716                      $tmp = $overH; $overH = $overW; $overW = $tmp;
2717                      $tmp = $hReel; $hReel = $wReel; $wReel = $tmp;
2718                      unset($tmp);
2719                      $w = $this->parsingCss->value['height'];
2720                      $h = $this->parsingCss->value['width'];
2721                      $tX =-$h;
2722                      $tY = 0;
2723                      break;
2724  
2725                  case 180:
2726                      $w = $this->parsingCss->value['width'];
2727                      $h = $this->parsingCss->value['height'];
2728                      $tX = -$w;
2729                      $tY = -$h;
2730                      break;
2731  
2732                  case 270:
2733                      $tmp = $overH; $overH = $overW; $overW = $tmp;
2734                      $tmp = $hReel; $hReel = $wReel; $wReel = $tmp;
2735                      unset($tmp);
2736                      $w = $this->parsingCss->value['height'];
2737                      $h = $this->parsingCss->value['width'];
2738                      $tX = 0;
2739                      $tY =-$w;
2740                      break;
2741  
2742                  default:
2743                      $w = $this->parsingCss->value['width'];
2744                      $h = $this->parsingCss->value['height'];
2745                      $tX = 0;
2746                      $tY = 0;
2747                      break;
2748              }
2749  
2750              if (!$this->parsingCss->value['position']) {
2751                  if (
2752                      $w < ($this->pdf->getW() - $this->pdf->getlMargin()-$this->pdf->getrMargin()) &&
2753                      $this->pdf->getX() + $w>=($this->pdf->getW() - $this->pdf->getrMargin())
2754                      )
2755                      $this->_tag_open_BR(array());
2756  
2757                  if (
2758                          ($h < ($this->pdf->getH() - $this->pdf->gettMargin()-$this->pdf->getbMargin())) &&
2759                          ($this->pdf->getY() + $h>=($this->pdf->getH() - $this->pdf->getbMargin())) &&
2760                          !$this->_isInOverflow
2761                      )
2762                      $this->_setNewPage();
2763  
2764                  $old = $this->parsingCss->getOldValues();
2765                  $parentWidth = $old['width'] ? $old['width'] : $this->pdf->getW() - $this->pdf->getlMargin() - $this->pdf->getrMargin();
2766  
2767