[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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

   1  <?php
   2  /**
   3   * HTML2PDF Librairy - parsingCss 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  class HTML2PDF_parsingCss
  13  {
  14      /**
  15       * reference to the pdf object
  16       * @var TCPDF
  17       */
  18      protected $_pdf         = null;
  19  
  20      protected $_htmlColor   = array(); // list of the HTML colors
  21      protected $_onlyLeft    = false;   // flag if we are in a sub html => only "text-align:left" is used
  22      protected $_defaultFont = null;    // default font to use if the asked font does not exist
  23  
  24      public    $value        = array(); // current values
  25      public    $css          = array(); // css values
  26      public    $cssKeys      = array(); // css key, for the execution order
  27      public    $table        = array(); // level history
  28  
  29      /**
  30       * Constructor
  31       *
  32       * @param  &HTML2PDF_myPdf reference to the PDF $object
  33       * @access public
  34       */
  35      public function __construct(&$pdf)
  36      {
  37          $this->_init();
  38          $this->setPdfParent($pdf);
  39      }
  40  
  41      /**
  42       * Set the HTML2PDF parent object
  43       *
  44       * @param  &HTML2PDF reference to the HTML2PDF parent $object
  45       * @access public
  46       */
  47      public function setPdfParent(&$pdf)
  48      {
  49          $this->_pdf = &$pdf;
  50      }
  51  
  52      /**
  53       * Inform that we want only "test-align:left" because we are in a sub HTML
  54       *
  55       * @access public
  56       */
  57      public function setOnlyLeft()
  58      {
  59          $this->value['text-align'] = 'left';
  60          $this->_onlyLeft = true;
  61      }
  62  
  63      /**
  64       * Get the vales of the parent, if exist
  65       *
  66       * @return array CSS values
  67       * @access public
  68       */
  69      public function getOldValues()
  70      {
  71          return isset($this->table[count($this->table)-1]) ? $this->table[count($this->table)-1] : $this->value;
  72      }
  73  
  74      /**
  75      * define the Default Font to use, if the font does not exist, or if no font asked
  76      *
  77      * @param  string  default font-family. If null : Arial for no font asked, and error fot ont does not exist
  78      * @return string  old default font-family
  79      * @access public
  80      */
  81      public function setDefaultFont($default = null)
  82      {
  83          $old = $this->_defaultFont;
  84          $this->_defaultFont = $default;
  85          if ($default) $this->value['font-family'] = $default;
  86          return $old;
  87      }
  88  
  89       /**
  90       * Init the object
  91       *
  92       * @access protected
  93       */
  94      protected function _init()
  95      {
  96          // get the Web Colors from TCPDF
  97          require(K_PATH_MAIN.'htmlcolors.php');
  98          $this->_htmlColor = $webcolor;
  99  
 100          // init the Style
 101          $this->table = array();
 102          $this->value = array();
 103          $this->initStyle();
 104  
 105          // Init the styles without legacy
 106          $this->resetStyle();
 107      }
 108  
 109      /**
 110       * Init the CSS Style
 111       *
 112       * @access public
 113       */
 114      public function initStyle()
 115      {
 116          $this->value['id_tag']       = 'body';        // tag name
 117          $this->value['id_name']          = null;         // tag - attribute name
 118          $this->value['id_id']            = null;         // tag - attribute id
 119          $this->value['id_class']         = null;         // tag - attribute class
 120          $this->value['id_lst']           = array('*');   // tag - list of legacy
 121          $this->value['mini-size']        = 1.;           // specific size report for sup, sub
 122          $this->value['mini-decal']       = 0;            // specific position report for sup, sub
 123          $this->value['font-family']      = 'Arial';
 124          $this->value['font-bold']        = false;
 125          $this->value['font-italic']      = false;
 126          $this->value['font-underline']   = false;
 127          $this->value['font-overline']    = false;
 128          $this->value['font-linethrough'] = false;
 129          $this->value['text-transform']   = 'none';
 130          $this->value['font-size']        = $this->convertToMM('10pt');
 131          $this->value['text-indent']      = 0;
 132          $this->value['text-align']       = 'left';
 133          $this->value['vertical-align']   = 'middle';
 134          $this->value['line-height']      = 'normal';
 135  
 136          $this->value['position']         = null;
 137          $this->value['x']                = null;
 138          $this->value['y']                = null;
 139          $this->value['width']            = 0;
 140          $this->value['height']           = 0;
 141          $this->value['top']              = null;
 142          $this->value['right']            = null;
 143          $this->value['bottom']           = null;
 144          $this->value['left']             = null;
 145          $this->value['float']            = null;
 146          $this->value['display']          = null;
 147          $this->value['rotate']           = null;
 148          $this->value['overflow']         = 'visible';
 149  
 150          $this->value['color']            = array(0, 0, 0);
 151          $this->value['background']       = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null);
 152          $this->value['border']           = array();
 153          $this->value['padding']          = array();
 154          $this->value['margin']           = array();
 155          $this->value['margin-auto']      = false;
 156  
 157          $this->value['list-style-type']  = '';
 158          $this->value['list-style-image'] = '';
 159  
 160          $this->value['xc'] = null;
 161          $this->value['yc'] = null;
 162      }
 163  
 164      /**
 165       * Init the CSS Style without legacy
 166       *
 167       * @param  string  tag name
 168       * @access public
 169       */
 170      public function resetStyle($tagName = '')
 171      {
 172          // prepare somme values
 173          $border = $this->readBorder('solid 1px #000000');
 174          $units = array(
 175              '1px' => $this->convertToMM('1px'),
 176              '5px' => $this->convertToMM('5px'),
 177          );
 178  
 179  
 180          // prepare the Collapse attribute
 181          $collapse = isset($this->value['border']['collapse']) ? $this->value['border']['collapse'] : false;
 182          if (!in_array($tagName, array('tr', 'td', 'th', 'thead', 'tbody', 'tfoot'))) $collapse = false;
 183  
 184          // set the global css values
 185          $this->value['position']   = null;
 186          $this->value['x']          = null;
 187          $this->value['y']          = null;
 188          $this->value['width']      = 0;
 189          $this->value['height']     = 0;
 190          $this->value['top']        = null;
 191          $this->value['right']      = null;
 192          $this->value['bottom']     = null;
 193          $this->value['left']       = null;
 194          $this->value['float']      = null;
 195          $this->value['display']    = null;
 196          $this->value['rotate']     = null;
 197          $this->value['overflow']   = 'visible';
 198          $this->value['background'] = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null);
 199          $this->value['border']     = array(
 200              't' => $this->readBorder('none'),
 201              'r' => $this->readBorder('none'),
 202              'b' => $this->readBorder('none'),
 203              'l' => $this->readBorder('none'),
 204              'radius' => array(
 205                  'tl' => array(0, 0),
 206                  'tr' => array(0, 0),
 207                  'br' => array(0, 0),
 208                  'bl' => array(0, 0)
 209              ),
 210              'collapse' => $collapse,
 211          );
 212  
 213          // specific values for some tags
 214          if (!in_array($tagName, array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
 215              $this->value['margin'] = array('t'=>0,'r'=>0,'b'=>0,'l'=>0);
 216          }
 217  
 218          if (in_array($tagName, array('input', 'select', 'textarea'))) {
 219              $this->value['border']['t'] = null;
 220              $this->value['border']['r'] = null;
 221              $this->value['border']['b'] = null;
 222              $this->value['border']['l'] = null;
 223          }
 224  
 225          if ($tagName=='p') {
 226              $this->value['margin']['t'] = null;
 227              $this->value['margin']['b'] = null;
 228          }
 229          if ($tagName=='blockquote') {
 230              $this->value['margin']['t'] = 3;
 231              $this->value['margin']['r'] = 3;
 232              $this->value['margin']['b'] = 3;
 233              $this->value['margin']['l'] = 6;
 234          }
 235          $this->value['margin-auto'] = false;
 236  
 237          if (in_array($tagName, array('blockquote', 'div', 'fieldset'))) {
 238              $this->value['vertical-align'] = 'top';
 239          }
 240  
 241          if (in_array($tagName, array('fieldset', 'legend'))) {
 242              $this->value['border'] = array(
 243                  't' => $border,
 244                  'r' => $border,
 245                  'b' => $border,
 246                  'l' => $border,
 247                  'radius' => array(
 248                      'tl' => array($units['5px'], $units['5px']),
 249                      'tr' => array($units['5px'], $units['5px']),
 250                      'br' => array($units['5px'], $units['5px']),
 251                      'bl' => array($units['5px'], $units['5px'])
 252                  ),
 253                  'collapse' => false,
 254              );
 255          }
 256  
 257          if (in_array($tagName, array('ul', 'li'))) {
 258              $this->value['list-style-type']  = '';
 259              $this->value['list-style-image'] = '';
 260          }
 261  
 262          if (!in_array($tagName, array('tr', 'td'))) {
 263              $this->value['padding'] = array(
 264                  't' => 0,
 265                  'r' => 0,
 266                  'b' => 0,
 267                  'l' => 0
 268              );
 269          } else {
 270              $this->value['padding'] = array(
 271                  't' => $units['1px'],
 272                  'r' => $units['1px'],
 273                  'b' => $units['1px'],
 274                  'l' => $units['1px']
 275              );
 276          }
 277  
 278          if ($tagName=='hr') {
 279              $this->value['border'] = array(
 280                  't' => $border,
 281                  'r' => $border,
 282                  'b' => $border,
 283                  'l' => $border,
 284                  'radius' => array(
 285                      'tl' => array(0, 0),
 286                      'tr' => array(0, 0),
 287                      'br' => array(0, 0),
 288                      'bl' => array(0, 0)
 289                  ),
 290                  'collapse' => false,
 291              );
 292              $this->convertBackground('#FFFFFF', $this->value['background']);
 293          }
 294  
 295          $this->value['xc'] = null;
 296          $this->value['yc'] = null;
 297      }
 298  
 299      /**
 300       * Init the PDF Font
 301       *
 302       * @access public
 303       */
 304      public function fontSet()
 305      {
 306          $family = strtolower($this->value['font-family']);
 307  
 308          $b = ($this->value['font-bold']        ? 'B' : '');
 309          $i = ($this->value['font-italic']      ? 'I' : '');
 310          $u = ($this->value['font-underline']   ? 'U' : '');
 311          $d = ($this->value['font-linethrough'] ? 'D' : '');
 312          $o = ($this->value['font-overline']    ? 'O' : '');
 313  
 314          // font style
 315          $style = $b.$i;
 316  
 317          if ($this->_defaultFont) {
 318              if($family=='arial')
 319                  $family='helvetica';
 320              elseif($family=='symbol' || $family=='zapfdingbats')
 321                  $style='';
 322  
 323              $fontkey = $family.$style;
 324              if (!$this->_pdf->isLoadedFont($fontkey))
 325                  $family = $this->_defaultFont;
 326          }
 327  
 328          if($family=='arial')
 329              $family='helvetica';
 330          elseif($family=='symbol' || $family=='zapfdingbats')
 331              $style='';
 332  
 333          // complete style
 334          $style.= $u.$d.$o;
 335  
 336          // size : mm => pt
 337          $size = $this->value['font-size'];
 338          $size = 72 * $size / 25.4;
 339  
 340          // apply the font
 341          $this->_pdf->SetFont($family, $style, $this->value['mini-size']*$size);
 342          $this->_pdf->setTextColorArray($this->value['color']);
 343          if ($this->value['background']['color'])
 344              $this->_pdf->setFillColorArray($this->value['background']['color']);
 345          else
 346              $this->_pdf->setFillColor(255);
 347      }
 348  
 349       /**
 350       * add a level in the CSS history
 351       *
 352       * @access public
 353       */
 354      public function save()
 355      {
 356          array_push($this->table, $this->value);
 357      }
 358  
 359       /**
 360       * remove a level in the CSS history
 361       *
 362       * @access public
 363       */
 364      public function load()
 365      {
 366          if (count($this->table)) {
 367              $this->value = array_pop($this->table);
 368          }
 369      }
 370  
 371       /**
 372       * restore the Y positiony (used after a span)
 373       *
 374       * @access public
 375       */
 376      public function restorePosition()
 377      {
 378          if ($this->value['y']==$this->_pdf->getY()) $this->_pdf->setY($this->value['yc'], false);
 379      }
 380  
 381       /**
 382       * set the New position for the current Tag
 383       *
 384       * @access public
 385       */
 386      public function setPosition()
 387      {
 388          // get the current position
 389          $currentX = $this->_pdf->getX();
 390          $currentY = $this->_pdf->getY();
 391  
 392          // save it
 393          $this->value['xc'] = $currentX;
 394          $this->value['yc'] = $currentY;
 395  
 396          if ($this->value['position']=='relative' || $this->value['position']=='absolute') {
 397              if ($this->value['right']!==null) {
 398                  $x = $this->getLastWidth(true) - $this->value['right'] - $this->value['width'];
 399                  if ($this->value['margin']['r']) $x-= $this->value['margin']['r'];
 400              } else {
 401                  $x = $this->value['left'];
 402                  if ($this->value['margin']['l']) $x+= $this->value['margin']['l'];
 403              }
 404  
 405              if ($this->value['bottom']!==null) {
 406                  $y = $this->getLastHeight(true) - $this->value['bottom'] - $this->value['height'];
 407                  if ($this->value['margin']['b']) $y-= $this->value['margin']['b'];
 408              } else {
 409                  $y = $this->value['top'];
 410                  if ($this->value['margin']['t']) $y+= $this->value['margin']['t'];
 411              }
 412  
 413              if ($this->value['position']=='relative') {
 414                  $this->value['x'] = $currentX + $x;
 415                  $this->value['y'] = $currentY + $y;
 416              } else {
 417                  $this->value['x'] = $this->_getLastAbsoluteX()+$x;
 418                  $this->value['y'] = $this->_getLastAbsoluteY()+$y;
 419              }
 420          } else {
 421              $this->value['x'] = $currentX;
 422              $this->value['y'] = $currentY;
 423              if ($this->value['margin']['l']) $this->value['x']+= $this->value['margin']['l'];
 424              if ($this->value['margin']['t']) $this->value['y']+= $this->value['margin']['t'];
 425          }
 426  
 427          // save the new position
 428          $this->_pdf->setXY($this->value['x'], $this->value['y']);
 429      }
 430  
 431       /**
 432       * Analise the CSS style to convert it into Form style
 433       *
 434       * @access public
 435       * @param  array    styles
 436       */
 437      public function getFormStyle()
 438      {
 439          $prop = array();
 440  
 441          $prop['alignment'] = $this->value['text-align'];
 442  
 443          if (isset($this->value['background']['color']) && is_array($this->value['background']['color'])) {
 444              $prop['fillColor'] = $this->value['background']['color'];
 445          }
 446  
 447          if (isset($this->value['border']['t']['color'])) {
 448              $prop['strokeColor'] = $this->value['border']['t']['color'];
 449          }
 450  
 451          if (isset($this->value['border']['t']['width'])) {
 452              $prop['lineWidth'] = $this->value['border']['t']['width'];
 453          }
 454  
 455          if (isset($this->value['border']['t']['type'])) {
 456              $prop['borderStyle'] = $this->value['border']['t']['type'];
 457          }
 458  
 459          if (!empty($this->value['color'])) {
 460              $prop['textColor'] = $this->value['color'];
 461          }
 462  
 463          if (!empty($this->value['font-size'])) {
 464              $prop['textSize'] = $this->value['font-size'];
 465          }
 466  
 467          return $prop;
 468      }
 469  
 470       /**
 471       * Analise the CSS style to convert it into SVG style
 472       *
 473       * @access public
 474       * @param  string   tag name
 475       * @param  array    styles
 476       */
 477      public function getSvgStyle($tagName, &$param)
 478      {
 479          // prepare
 480          $tagName = strtolower($tagName);
 481          $id   = isset($param['id'])   ? strtolower(trim($param['id']))   : null; if (!$id)   $id   = null;
 482          $name = isset($param['name']) ? strtolower(trim($param['name'])) : null; if (!$name) $name = null;
 483  
 484          // read the class attribute
 485          $class = array();
 486          $tmp = isset($param['class']) ? strtolower(trim($param['class'])) : '';
 487          $tmp = explode(' ', $tmp);
 488          foreach ($tmp as $k => $v) {
 489              $v = trim($v);
 490              if ($v) $class[] = $v;
 491          }
 492  
 493          // identify the tag, and the direct styles
 494          $this->value['id_tag'] = $tagName;
 495          $this->value['id_name']   = $name;
 496          $this->value['id_id']     = $id;
 497          $this->value['id_class']  = $class;
 498          $this->value['id_lst']    = array();
 499          $this->value['id_lst'][] = '*';
 500          $this->value['id_lst'][] = $tagName;
 501          if (!isset($this->value['svg'])) {
 502              $this->value['svg'] = array(
 503                  'stroke'         => null,
 504                  'stroke-width'   => $this->convertToMM('1pt'),
 505                  'fill'           => null,
 506                  'fill-opacity'   => null,
 507              );
 508          }
 509  
 510          if (count($class)) {
 511              foreach ($class as $v) {
 512                  $this->value['id_lst'][] = '*.'.$v;
 513                  $this->value['id_lst'][] = '.'.$v;
 514                  $this->value['id_lst'][] = $tagName.'.'.$v;
 515              }
 516          }
 517          if ($id) {
 518              $this->value['id_lst'][] = '*#'.$id;
 519              $this->value['id_lst'][] = '#'.$id;
 520              $this->value['id_lst'][] = $tagName.'#'.$id;
 521          }
 522  
 523          // CSS style
 524          $styles = $this->_getFromCSS();
 525  
 526          // adding the style from the tag
 527          $styles = array_merge($styles, $param['style']);
 528  
 529          if (isset($styles['stroke']))        $this->value['svg']['stroke']       = $this->convertToColor($styles['stroke'], $res);
 530          if (isset($styles['stroke-width']))  $this->value['svg']['stroke-width'] = $this->convertToMM($styles['stroke-width']);
 531          if (isset($styles['fill']))          $this->value['svg']['fill']         = $this->convertToColor($styles['fill'], $res);
 532          if (isset($styles['fill-opacity']))  $this->value['svg']['fill-opacity'] = 1.*$styles['fill-opacity'];
 533  
 534          return $this->value['svg'];
 535      }
 536  
 537      /**
 538       * analyse the css properties from the HTML parsing
 539       *
 540       * @access public
 541       * @param  string  $tagName
 542       * @param  array   $param
 543       * @param  array   $legacy
 544       */
 545      public function analyse($tagName, &$param, $legacy = null)
 546      {
 547          // prepare the informations
 548          $tagName = strtolower($tagName);
 549          $id   = isset($param['id'])   ? strtolower(trim($param['id']))    : null; if (!$id)   $id   = null;
 550          $name = isset($param['name']) ? strtolower(trim($param['name']))  : null; if (!$name) $name = null;
 551  
 552          // get the class names to use
 553          $class = array();
 554          $tmp = isset($param['class']) ? strtolower(trim($param['class'])) : '';
 555          $tmp = explode(' ', $tmp);
 556          foreach ($tmp as $k => $v) {
 557              $v = trim($v);
 558              if ($v) $class[] = $v;
 559          }
 560  
 561          // prepare the values, and the list of css tags to identify
 562          $this->value['id_tag']   = $tagName;
 563          $this->value['id_name']  = $name;
 564          $this->value['id_id']    = $id;
 565          $this->value['id_class'] = $class;
 566          $this->value['id_lst']   = array();
 567          $this->value['id_lst'][] = '*';
 568          $this->value['id_lst'][] = $tagName;
 569          if (count($class)) {
 570              foreach ($class as $v) {
 571                  $this->value['id_lst'][] = '*.'.$v;
 572                  $this->value['id_lst'][] = '.'.$v;
 573                  $this->value['id_lst'][] = $tagName.'.'.$v;
 574              }
 575          }
 576          if ($id) {
 577              $this->value['id_lst'][] = '*#'.$id;
 578              $this->value['id_lst'][] = '#'.$id;
 579              $this->value['id_lst'][] = $tagName.'#'.$id;
 580          }
 581  
 582          // get the css styles from class
 583          $styles = $this->_getFromCSS();
 584  
 585          // merge with the css styles from tag
 586          $styles = array_merge($styles, $param['style']);
 587          if (isset($param['allwidth']) && !isset($styles['width'])) $styles['width'] = '100%';
 588  
 589          // reset some styles, depending on the tag name
 590          $this->resetStyle($tagName);
 591  
 592          // add the legacy values
 593          if ($legacy) {
 594              foreach ($legacy as $legacyName => $legacyValue) {
 595                  if (is_array($legacyValue)) {
 596                      foreach($legacyValue as $legacy2Name => $legacy2Value)
 597                          $this->value[$legacyName][$legacy2Name] = $legacy2Value;
 598                  } else {
 599                      $this->value[$legacyName] = $legacyValue;
 600                  }
 601              }
 602          }
 603  
 604          // some flags
 605          $correctWidth = false;
 606          $noWidth = true;
 607  
 608          // read all the css styles
 609          foreach ($styles as $nom => $val) {
 610              switch($nom)
 611              {
 612                  case 'font-family':
 613                      $val = explode(',', $val);
 614                      $val = trim($val[0]);
 615                      if ($val) $this->value['font-family'] = $val;
 616                      break;
 617  
 618                  case 'font-weight':
 619                      $this->value['font-bold'] = ($val=='bold');
 620                      break;
 621  
 622                  case 'font-style':
 623                      $this->value['font-italic'] = ($val=='italic');
 624                      break;
 625  
 626                  case 'text-decoration':
 627                      $val = explode(' ', $val);
 628                      $this->value['font-underline']   = (in_array('underline', $val));
 629                      $this->value['font-overline']    = (in_array('overline', $val));
 630                      $this->value['font-linethrough'] = (in_array('line-through', $val));
 631                      break;
 632  
 633                  case 'text-indent':
 634                      $this->value['text-indent'] = $this->convertToMM($val);
 635                      break;
 636  
 637                  case 'text-transform':
 638                      if (!in_array($val, array('none', 'capitalize', 'uppercase', 'lowercase'))) $val = 'none';
 639                      $this->value['text-transform']  = $val;
 640                      break;
 641  
 642                  case 'font-size':
 643                      $val = $this->convertToMM($val, $this->value['font-size']);
 644                      if ($val) $this->value['font-size'] = $val;
 645                      break;
 646  
 647                  case 'color':
 648                      $res = null;
 649                      $this->value['color'] = $this->convertToColor($val, $res);
 650                      if ($tagName=='hr') {
 651                          $this->value['border']['l']['color'] = $this->value['color'];
 652                          $this->value['border']['t']['color'] = $this->value['color'];
 653                          $this->value['border']['r']['color'] = $this->value['color'];
 654                          $this->value['border']['b']['color'] = $this->value['color'];
 655                      }
 656                      break;
 657  
 658                  case 'text-align':
 659                      $val = strtolower($val);
 660                      if (!in_array($val, array('left', 'right', 'center', 'justify', 'li_right'))) $val = 'left';
 661                      $this->value['text-align'] = $val;
 662                      break;
 663  
 664                  case 'vertical-align':
 665                      $this->value['vertical-align'] = $val;
 666                      break;
 667  
 668                  case 'width':
 669                      $this->value['width'] = $this->convertToMM($val, $this->getLastWidth());
 670                      if ($this->value['width'] && substr($val, -1)=='%') $correctWidth=true;
 671                      $noWidth = false;
 672                      break;
 673  
 674                  case 'height':
 675                      $this->value['height'] = $this->convertToMM($val, $this->getLastHeight());
 676                      break;
 677  
 678                  case 'line-height':
 679                      if (preg_match('/^[0-9\.]+$/isU', $val)) $val = floor($val*100).'%';
 680                      $this->value['line-height'] = $val;
 681                      break;
 682  
 683                  case 'rotate':
 684                      if (!in_array($val, array(0, -90, 90, 180, 270, -180, -270))) $val = null;
 685                      if ($val<0) $val+= 360;
 686                      $this->value['rotate'] = $val;
 687                      break;
 688  
 689                  case 'overflow':
 690                      if (!in_array($val, array('visible', 'hidden'))) $val = 'visible';
 691                      $this->value['overflow'] = $val;
 692                      break;
 693  
 694                  case 'padding':
 695                      $val = explode(' ', $val);
 696                      foreach ($val as $k => $v) {
 697                          $v = trim($v);
 698                          if ($v!='') {
 699                              $val[$k] = $v;
 700                          } else {
 701                              unset($val[$k]);
 702                          }
 703                      }
 704                      $val = array_values($val);
 705                      $this->_duplicateBorder($val);
 706                      $this->value['padding']['t'] = $this->convertToMM($val[0], 0);
 707                      $this->value['padding']['r'] = $this->convertToMM($val[1], 0);
 708                      $this->value['padding']['b'] = $this->convertToMM($val[2], 0);
 709                      $this->value['padding']['l'] = $this->convertToMM($val[3], 0);
 710                      break;
 711  
 712                  case 'padding-top':
 713                      $this->value['padding']['t'] = $this->convertToMM($val, 0);
 714                      break;
 715  
 716                  case 'padding-right':
 717                      $this->value['padding']['r'] = $this->convertToMM($val, 0);
 718                      break;
 719  
 720                  case 'padding-bottom':
 721                      $this->value['padding']['b'] = $this->convertToMM($val, 0);
 722                      break;
 723  
 724                  case 'padding-left':
 725                      $this->value['padding']['l'] = $this->convertToMM($val, 0);
 726                      break;
 727  
 728                  case 'margin':
 729                      if ($val=='auto') {
 730                          $this->value['margin-auto'] = true;
 731                          break;
 732                      }
 733                      $val = explode(' ', $val);
 734                      foreach ($val as $k => $v) {
 735                          $v = trim($v);
 736                          if ($v!='') {
 737                              $val[$k] = $v;
 738                          } else {
 739                              unset($val[$k]);
 740                          }
 741                      }
 742                      $val = array_values($val);
 743                      $this->_duplicateBorder($val);
 744                      $this->value['margin']['t'] = $this->convertToMM($val[0], 0);
 745                      $this->value['margin']['r'] = $this->convertToMM($val[1], 0);
 746                      $this->value['margin']['b'] = $this->convertToMM($val[2], 0);
 747                      $this->value['margin']['l'] = $this->convertToMM($val[3], 0);
 748                      break;
 749  
 750                  case 'margin-top':
 751                      $this->value['margin']['t'] = $this->convertToMM($val, 0);
 752                      break;
 753  
 754                  case 'margin-right':
 755                      $this->value['margin']['r'] = $this->convertToMM($val, 0);
 756                      break;
 757  
 758                  case 'margin-bottom':
 759                      $this->value['margin']['b'] = $this->convertToMM($val, 0);
 760                      break;
 761  
 762                  case 'margin-left':
 763                      $this->value['margin']['l'] = $this->convertToMM($val, 0);
 764                      break;
 765  
 766                  case 'border':
 767                      $val = $this->readBorder($val);
 768                      $this->value['border']['t'] = $val;
 769                      $this->value['border']['r'] = $val;
 770                      $this->value['border']['b'] = $val;
 771                      $this->value['border']['l'] = $val;
 772                      break;
 773  
 774                  case 'border-style':
 775                      $val = explode(' ', $val);
 776                      foreach ($val as $valK => $valV) {
 777                          if (!in_array($valV, array('solid', 'dotted', 'dashed'))) {
 778                              $val[$valK] = null;
 779                          }
 780                      }
 781                      $this->_duplicateBorder($val);
 782                      if ($val[0]) $this->value['border']['t']['type'] = $val[0];
 783                      if ($val[1]) $this->value['border']['r']['type'] = $val[1];
 784                      if ($val[2]) $this->value['border']['b']['type'] = $val[2];
 785                      if ($val[3]) $this->value['border']['l']['type'] = $val[3];
 786                      break;
 787  
 788                  case 'border-top-style':
 789                      if (in_array($val, array('solid', 'dotted', 'dashed'))) {
 790                          $this->value['border']['t']['type'] = $val;
 791                      }
 792                      break;
 793  
 794                  case 'border-right-style':
 795                      if (in_array($val, array('solid', 'dotted', 'dashed'))) {
 796                          $this->value['border']['r']['type'] = $val;
 797                      }
 798                      break;
 799  
 800                  case 'border-bottom-style':
 801                      if (in_array($val, array('solid', 'dotted', 'dashed'))) {
 802                          $this->value['border']['b']['type'] = $val;
 803                      }
 804                      break;
 805  
 806                  case 'border-left-style':
 807                      if (in_array($val, array('solid', 'dotted', 'dashed')))
 808                          $this->value['border']['l']['type'] = $val;
 809                      break;
 810  
 811                  case 'border-color':
 812                      $res = false;
 813                      $val = preg_replace('/,[\s]+/', ',', $val);
 814                      $val = explode(' ', $val);
 815                      foreach ($val as $valK => $valV) {
 816                              $val[$valK] = $this->convertToColor($valV, $res);
 817                              if (!$res) {
 818                                  $val[$valK] = null;
 819                              }
 820                      }
 821                      $this->_duplicateBorder($val);
 822                      if (is_array($val[0])) $this->value['border']['t']['color'] = $val[0];
 823                      if (is_array($val[1])) $this->value['border']['r']['color'] = $val[1];
 824                      if (is_array($val[2])) $this->value['border']['b']['color'] = $val[2];
 825                      if (is_array($val[3])) $this->value['border']['l']['color'] = $val[3];
 826  
 827                      break;
 828  
 829                  case 'border-top-color':
 830                      $res = false;
 831                      $val = $this->convertToColor($val, $res);
 832                      if ($res) $this->value['border']['t']['color'] = $val;
 833                      break;
 834  
 835                  case 'border-right-color':
 836                      $res = false;
 837                      $val = $this->convertToColor($val, $res);
 838                      if ($res) $this->value['border']['r']['color'] = $val;
 839                      break;
 840  
 841                  case 'border-bottom-color':
 842                      $res = false;
 843                      $val = $this->convertToColor($val, $res);
 844                      if ($res) $this->value['border']['b']['color'] = $val;
 845                      break;
 846  
 847                  case 'border-left-color':
 848                      $res = false;
 849                      $val = $this->convertToColor($val, $res);
 850                      if ($res) $this->value['border']['l']['color'] = $val;
 851                      break;
 852  
 853                  case 'border-width':
 854                      $val = explode(' ', $val);
 855                      foreach ($val as $valK => $valV) {
 856                              $val[$valK] = $this->convertToMM($valV, 0);
 857                      }
 858                      $this->_duplicateBorder($val);
 859                      if ($val[0]) $this->value['border']['t']['width'] = $val[0];
 860                      if ($val[1]) $this->value['border']['r']['width'] = $val[1];
 861                      if ($val[2]) $this->value['border']['b']['width'] = $val[2];
 862                      if ($val[3]) $this->value['border']['l']['width'] = $val[3];
 863                      break;
 864  
 865                  case 'border-top-width':
 866                      $val = $this->convertToMM($val, 0);
 867                      if ($val) $this->value['border']['t']['width'] = $val;
 868                      break;
 869  
 870                  case 'border-right-width':
 871                      $val = $this->convertToMM($val, 0);
 872                      if ($val) $this->value['border']['r']['width'] = $val;
 873                      break;
 874  
 875                  case 'border-bottom-width':
 876                      $val = $this->convertToMM($val, 0);
 877                      if ($val) $this->value['border']['b']['width'] = $val;
 878                      break;
 879  
 880                  case 'border-left-width':
 881                      $val = $this->convertToMM($val, 0);
 882                      if ($val) $this->value['border']['l']['width'] = $val;
 883                      break;
 884  
 885                  case 'border-collapse':
 886                      if ($tagName=='table') $this->value['border']['collapse'] = ($val=='collapse');
 887                      break;
 888  
 889                  case 'border-radius':
 890                      $val = explode('/', $val);
 891                      if (count($val)>2) {
 892                          break;
 893                      }
 894                      $valH = $this->convertToRadius(trim($val[0]));
 895                      if (count($valH)<1 || count($valH)>4) {
 896                          break;
 897                      }
 898                      if (!isset($valH[1])) $valH[1] = $valH[0];
 899                      if (!isset($valH[2])) $valH = array($valH[0], $valH[0], $valH[1], $valH[1]);
 900                      if (!isset($valH[3])) $valH[3] = $valH[1];
 901                      if (isset($val[1])) {
 902                          $valV = $this->convertToRadius(trim($val[1]));
 903                          if (count($valV)<1 || count($valV)>4) {
 904                              break;
 905                          }
 906                          if (!isset($valV[1])) $valV[1] = $valV[0];
 907                          if (!isset($valV[2])) $valV = array($valV[0], $valV[0], $valV[1], $valV[1]);
 908                          if (!isset($valV[3])) $valV[3] = $valV[1];
 909                      } else {
 910                          $valV = $valH;
 911                      }
 912                      $this->value['border']['radius'] = array(
 913                                  'tl' => array($valH[0], $valV[0]),
 914                                  'tr' => array($valH[1], $valV[1]),
 915                                  'br' => array($valH[2], $valV[2]),
 916                                  'bl' => array($valH[3], $valV[3])
 917                              );
 918                      break;
 919  
 920                  case 'border-top-left-radius':
 921                      $val = $this->convertToRadius($val);
 922                      if (count($val)<1 || count($val)>2) {
 923                          break;
 924                      }
 925                      $this->value['border']['radius']['tl'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
 926                      break;
 927  
 928                  case 'border-top-right-radius':
 929                      $val = $this->convertToRadius($val);
 930                      if (count($val)<1 || count($val)>2) {
 931                          break;
 932                      }
 933                      $this->value['border']['radius']['tr'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
 934                      break;
 935  
 936                  case 'border-bottom-right-radius':
 937                      $val = $this->convertToRadius($val);
 938                      if (count($val)<1 || count($val)>2) {
 939                          break;
 940                      }
 941                      $this->value['border']['radius']['br'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
 942                      break;
 943  
 944                  case 'border-bottom-left-radius':
 945                      $val = $this->convertToRadius($val);
 946                      if (count($val)<1 || count($val)>2) {
 947                          break;
 948                      }
 949                      $this->value['border']['radius']['bl'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
 950                      break;
 951  
 952                  case 'border-top':
 953                      $this->value['border']['t'] = $this->readBorder($val);
 954                      break;
 955  
 956                  case 'border-right':
 957                      $this->value['border']['r'] = $this->readBorder($val);
 958                      break;
 959  
 960                  case 'border-bottom':
 961                      $this->value['border']['b'] = $this->readBorder($val);
 962                      break;
 963  
 964                  case 'border-left':
 965                      $this->value['border']['l'] = $this->readBorder($val);
 966                      break;
 967  
 968                  case 'background-color':
 969                      $this->value['background']['color'] = $this->convertBackgroundColor($val);
 970                      break;
 971  
 972                  case 'background-image':
 973                      $this->value['background']['image'] = $this->convertBackgroundImage($val);
 974                      break;
 975  
 976                  case 'background-position':
 977                      $res = null;
 978                      $this->value['background']['position'] = $this->convertBackgroundPosition($val, $res);
 979                      break;
 980  
 981                  case 'background-repeat':
 982                      $this->value['background']['repeat'] = $this->convertBackgroundRepeat($val);
 983                      break;
 984  
 985                  case 'background':
 986                      $this->convertBackground($val, $this->value['background']);
 987                      break;
 988  
 989                  case 'position':
 990                      if ($val=='absolute')       $this->value['position'] = 'absolute';
 991                      else if ($val=='relative')  $this->value['position'] = 'relative';
 992                      else                        $this->value['position'] = null;
 993                      break;
 994  
 995                  case 'float':
 996                      if ($val=='left')           $this->value['float'] = 'left';
 997                      else if ($val=='right')     $this->value['float'] = 'right';
 998                      else                        $this->value['float'] = null;
 999                      break;
1000  
1001                  case 'display':
1002                      if ($val=='inline')         $this->value['display'] = 'inline';
1003                      else if ($val=='block')     $this->value['display'] = 'block';
1004                      else if ($val=='none')      $this->value['display'] = 'none';
1005                      else                        $this->value['display'] = null;
1006                      break;
1007  
1008                  case 'top':
1009                  case 'bottom':
1010                  case 'left':
1011                  case 'right':
1012                      $this->value[$nom] = $val;
1013                      break;
1014  
1015                  case 'list-style':
1016                  case 'list-style-type':
1017                  case 'list-style-image':
1018                      if ($nom=='list-style') $nom = 'list-style-type';
1019                      $this->value[$nom] = $val;
1020                      break;
1021  
1022                  default:
1023                      break;
1024              }
1025          }
1026  
1027          $return = true;
1028  
1029          // only for P tag
1030          if ($this->value['margin']['t']===null) $this->value['margin']['t'] = $this->value['font-size'];
1031          if ($this->value['margin']['b']===null) $this->value['margin']['b'] = $this->value['font-size'];
1032  
1033          // force the text align to left, if asked by html2pdf
1034          if ($this->_onlyLeft) $this->value['text-align'] = 'left';
1035  
1036          // correction on the width (quick box)
1037          if ($noWidth && in_array($tagName, array('div', 'blockquote', 'fieldset')) && $this->value['position']!='absolute') {
1038              $this->value['width'] = $this->getLastWidth();
1039              $this->value['width']-= $this->value['margin']['l'] + $this->value['margin']['r'];
1040          } else {
1041              if ($correctWidth) {
1042                  if (!in_array($tagName, array('table', 'div', 'blockquote', 'fieldset', 'hr'))) {
1043                      $this->value['width']-= $this->value['padding']['l'] + $this->value['padding']['r'];
1044                      $this->value['width']-= $this->value['border']['l']['width'] + $this->value['border']['r']['width'];
1045                  }
1046                  if (in_array($tagName, array('th', 'td'))) {
1047                      $this->value['width']-= $this->convertToMM(isset($param['cellspacing']) ? $param['cellspacing'] : '2px');
1048                      $return = false;
1049                  }
1050                  if ($this->value['width']<0) $this->value['width']=0;
1051              } else {
1052                  if ($this->value['width']) {
1053                      if ($this->value['border']['l']['width']) $this->value['width'] += $this->value['border']['l']['width'];
1054                      if ($this->value['border']['r']['width']) $this->value['width'] += $this->value['border']['r']['width'];
1055                      if ($this->value['padding']['l'])         $this->value['width'] += $this->value['padding']['l'];
1056                      if ($this->value['padding']['r'])         $this->value['width'] += $this->value['padding']['r'];
1057                  }
1058              }
1059          }
1060          if ($this->value['height']) {
1061              if ($this->value['border']['b']['width']) $this->value['height'] += $this->value['border']['b']['width'];
1062              if ($this->value['border']['t']['width']) $this->value['height'] += $this->value['border']['t']['width'];
1063              if ($this->value['padding']['b'])         $this->value['height'] += $this->value['padding']['b'];
1064              if ($this->value['padding']['t'])         $this->value['height'] += $this->value['padding']['t'];
1065          }
1066  
1067          if ($this->value['top']!=null)      $this->value['top']     = $this->convertToMM($this->value['top'], $this->getLastHeight(true));
1068          if ($this->value['bottom']!=null)   $this->value['bottom']  = $this->convertToMM($this->value['bottom'], $this->getLastHeight(true));
1069          if ($this->value['left']!=null)     $this->value['left']    = $this->convertToMM($this->value['left'], $this->getLastWidth(true));
1070          if ($this->value['right']!=null)    $this->value['right']   = $this->convertToMM($this->value['right'], $this->getLastWidth(true));
1071  
1072          if ($this->value['top'] && $this->value['bottom'] && $this->value['height'])    $this->value['bottom']  = null;
1073          if ($this->value['left'] && $this->value['right'] && $this->value['width'])     $this->value['right']   = null;
1074  
1075          return $return;
1076      }
1077  
1078       /**
1079       * get the height of the current line
1080       *
1081       * @access public
1082       * @return float $height in mm
1083       */
1084      public function getLineHeight()
1085      {
1086          $val = $this->value['line-height'];
1087          if ($val=='normal') $val = '108%';
1088          return $this->convertToMM($val, $this->value['font-size']);
1089      }
1090  
1091       /**
1092       * get the width of the parent
1093       *
1094       * @access public
1095       * @param  boolean $mode true => adding padding and border
1096       * @return float $width in mm
1097       */
1098      public function getLastWidth($mode = false)
1099      {
1100          for ($k=count($this->table)-1; $k>=0; $k--) {
1101              if ($this->table[$k]['width']) {
1102                  $w = $this->table[$k]['width'];
1103                  if ($mode) {
1104                      $w+= $this->table[$k]['border']['l']['width'] + $this->table[$k]['padding']['l'] + 0.02;
1105                      $w+= $this->table[$k]['border']['r']['width'] + $this->table[$k]['padding']['r'] + 0.02;
1106                  }
1107                  return $w;
1108              }
1109          }
1110          return $this->_pdf->getW() - $this->_pdf->getlMargin() - $this->_pdf->getrMargin();
1111      }
1112  
1113       /**
1114       * get the height of the parent
1115       *
1116       * @access public
1117       * @param  boolean $mode true => adding padding and border
1118       * @return float $height in mm
1119       */
1120      public function getLastHeight($mode = false)
1121      {
1122          for ($k=count($this->table)-1; $k>=0; $k--) {
1123              if ($this->table[$k]['height']) {
1124                  $h = $this->table[$k]['height'];
1125                  if ($mode) {
1126                      $h+= $this->table[$k]['border']['t']['width'] + $this->table[$k]['padding']['t'] + 0.02;
1127                      $h+= $this->table[$k]['border']['b']['width'] + $this->table[$k]['padding']['b'] + 0.02;
1128                  }
1129                  return $h;
1130              }
1131          }
1132          return $this->_pdf->getH() - $this->_pdf->gettMargin() - $this->_pdf->getbMargin();
1133      }
1134  
1135      /**
1136       * get the value of the float property
1137       *
1138       * @access public
1139       * @return $float left/right
1140       */
1141      public function getFloat()
1142      {
1143          if ($this->value['float']=='left')    return 'left';
1144          if ($this->value['float']=='right')   return 'right';
1145          return null;
1146      }
1147  
1148      /**
1149       * get the last value for a specific key
1150       *
1151       * @access public
1152       * @param  string $key
1153       * @return mixed
1154       */
1155      public function getLastValue($key)
1156      {
1157          $nb = count($this->table);
1158          if ($nb>0) {
1159              return $this->table[$nb-1][$key];
1160          } else {
1161              return null;
1162          }
1163      }
1164  
1165      /**
1166       * get the last absolute X
1167       *
1168       * @access protected
1169       * @return float $x
1170       */
1171      protected function _getLastAbsoluteX()
1172      {
1173          for ($k=count($this->table)-1; $k>=0; $k--) {
1174              if ($this->table[$k]['x'] && $this->table[$k]['position']) return $this->table[$k]['x'];
1175          }
1176          return $this->_pdf->getlMargin();
1177      }
1178  
1179      /**
1180       * get the last absolute Y
1181       *
1182       * @access protected
1183       * @return float $y
1184       */
1185      protected function _getLastAbsoluteY()
1186      {
1187          for ($k=count($this->table)-1; $k>=0; $k--) {
1188              if ($this->table[$k]['y'] && $this->table[$k]['position']) return $this->table[$k]['y'];
1189          }
1190          return $this->_pdf->gettMargin();
1191      }
1192  
1193      /**
1194       * get the CSS properties of the current tag
1195       *
1196       * @access protected
1197       * @return array $styles
1198       */
1199      protected function _getFromCSS()
1200      {
1201          // styles to apply
1202          $styles = array();
1203  
1204          // list of the selectors to get in the CSS files
1205          $getit  = array();
1206  
1207          // get the list of the selectors of each tags
1208          $lst = array();
1209          $lst[] = $this->value['id_lst'];
1210          for ($i=count($this->table)-1; $i>=0; $i--) {
1211              $lst[] = $this->table[$i]['id_lst'];
1212          }
1213  
1214          // foreach selectors in the CSS files, verify if it match with the list of selectors
1215          foreach ($this->cssKeys as $key => $num) {
1216              if ($this->_getReccursiveStyle($key, $lst)) {
1217                  $getit[$key] = $num;
1218              }
1219          }
1220  
1221          // if we have selectors
1222          if (count($getit)) {
1223              // get them, but in the definition order, because of priority
1224              asort($getit);
1225              foreach ($getit as $key => $val) $styles = array_merge($styles, $this->css[$key]);
1226          }
1227  
1228          return $styles;
1229      }
1230  
1231      /**
1232       * identify if the selector $key match with the list of tag selectors
1233       *
1234       * @access protected
1235       * @param  string   $key CSS selector to analyse
1236       * @param  array    $lst list of the selectors of each tags
1237       * @param  string   $next next step of parsing the selector
1238       * @return boolean
1239       */
1240      protected function _getReccursiveStyle($key, $lst, $next = null)
1241      {
1242          // if next step
1243          if ($next!==null) {
1244              // we remove this step
1245              if ($next) $key = trim(substr($key, 0, -strlen($next)));
1246              array_shift($lst);
1247  
1248              // if no more step to identify => return false
1249              if (!count($lst)) {
1250                  return false;
1251              }
1252          }
1253  
1254          // for each selector of the current step
1255          foreach ($lst[0] as $name) {
1256              // if selector = key => ok
1257              if ($key==$name) {
1258                  return true;
1259              }
1260  
1261              // if the end of the key = the selector and the next step is ok => ok
1262              if (substr($key, -strlen(' '.$name))==' '.$name && $this->_getReccursiveStyle($key, $lst, $name)) {
1263                  return true;
1264              }
1265          }
1266  
1267          // if we are not in the first step, we analyse the sub steps (the pareng tag of the current tag)
1268          if ($next!==null && $this->_getReccursiveStyle($key, $lst, '')) {
1269              return true;
1270          }
1271  
1272          // no corresponding found
1273          return false;
1274      }
1275  
1276      /**
1277       * Analyse a border
1278       *
1279       * @access  public
1280       * @param   string $css css border properties
1281       * @return  array  border properties
1282       */
1283      public function readBorder($css)
1284      {
1285          // border none
1286          $none = array('type' => 'none', 'width' => 0, 'color' => array(0, 0, 0));
1287  
1288          // default value
1289          $type  = 'solid';
1290          $width = $this->convertToMM('1pt');
1291          $color = array(0, 0, 0);
1292  
1293          // clean up the values
1294          $css = explode(' ', $css);
1295          foreach ($css as $k => $v) {
1296              $v = trim($v);
1297              if ($v) $css[$k] = $v;
1298              else    unset($css[$k]);
1299          }
1300          $css = array_values($css);
1301  
1302          // read the values
1303          $res = null;
1304          foreach ($css as $value) {
1305  
1306              // if no border => return none
1307              if ($value=='none' || $value=='hidden') {
1308                  return $none;
1309              }
1310  
1311              // try to convert the value as a distance
1312              $tmp = $this->convertToMM($value);
1313  
1314              // if the convert is ok => it is a width
1315              if ($tmp!==null) {
1316                  $width = $tmp;
1317              // else, it could be the type
1318              } else if (in_array($value, array('solid', 'dotted', 'dashed', 'double'))) {
1319                  $type = $value;
1320              // else, it could be the color
1321              } else {
1322                  $tmp = $this->convertToColor($value, $res);
1323                  if ($res) $color = $tmp;
1324              }
1325          }
1326  
1327          // if no witdh => return none
1328          if (!$width) return $none;
1329  
1330          // return the border properties
1331          return array('type' => $type, 'width' => $width, 'color' => $color);
1332      }
1333  
1334      /**
1335       * duplicate the borders if needed
1336       *
1337       * @access protected
1338       * @param  &array $val
1339       */
1340      protected function _duplicateBorder(&$val)
1341      {
1342          // 1 value => L => RTB
1343          if (count($val)==1) {
1344              $val[1] = $val[0];
1345              $val[2] = $val[0];
1346              $val[3] = $val[0];
1347          // 2 values => L => R & T => B
1348          } else if (count($val)==2) {
1349              $val[2] = $val[0];
1350              $val[3] = $val[1];
1351          // 3 values => T => B
1352          } else if (count($val)==3) {
1353              $val[3] = $val[1];
1354          }
1355      }
1356  
1357      /**
1358       * Analyse a background
1359       *
1360       * @access public
1361       * @param  string $css css background properties
1362       * @param  &array $value parsed values (by reference, because, ther is a legacy of the parent CSS properties)
1363       */
1364      public function convertBackground($css, &$value)
1365      {
1366          // is there a image ?
1367          $text = '/url\(([^)]*)\)/isU';
1368          if (preg_match($text, $css, $match)) {
1369              // get the image
1370              $value['image'] = $this->convertBackgroundImage($match[0]);
1371  
1372              // remove if from the css properties
1373              $css = preg_replace($text, '', $css);
1374              $css = preg_replace('/[\s]+/', ' ', $css);
1375          }
1376  
1377          // protect some spaces
1378          $css = preg_replace('/,[\s]+/', ',', $css);
1379  
1380          // explode the values
1381          $css = explode(' ', $css);
1382  
1383          // background position to parse
1384          $pos = '';
1385  
1386          // foreach value
1387          foreach ($css as $val) {
1388              // try to parse the value as a color
1389              $ok = false;
1390              $color = $this->convertToColor($val, $ok);
1391  
1392              // if ok => it is a color
1393              if ($ok) {
1394                  $value['color'] = $color;
1395              // else if transparent => no coloàr
1396              } else if ($val=='transparent') {
1397                  $value['color'] = null;
1398              // else
1399              } else {
1400                  // try to parse the value as a repeat
1401                  $repeat = $this->convertBackgroundRepeat($val);
1402  
1403                  // if ok => it is repeat
1404                  if ($repeat) {
1405                      $value['repeat'] = $repeat;
1406                  // else => it could only be a position
1407                  } else {
1408                      $pos.= ($pos ? ' ' : '').$val;
1409                  }
1410              }
1411          }
1412  
1413          // if we have a position to parse
1414          if ($pos) {
1415              // try to read it
1416              $pos = $this->convertBackgroundPosition($pos, $ok);
1417              if ($ok) $value['position'] = $pos;
1418          }
1419      }
1420  
1421      /**
1422       * parse a background color
1423       *
1424       * @access public
1425       * @param  string $css
1426       * @return string $value
1427       */
1428      public function convertBackgroundColor($css)
1429      {
1430          $res = null;
1431          if ($css=='transparent') return null;
1432          else                     return $this->convertToColor($css, $res);
1433      }
1434  
1435      /**
1436       * parse a background image
1437       *
1438       * @access public
1439       * @param  string $css
1440       * @return string $value
1441       */
1442      public function convertBackgroundImage($css)
1443      {
1444          if ($css=='none')
1445              return null;
1446          else if (preg_match('/^url\(([^)]*)\)$/isU', $css, $match))
1447              return $match[1];
1448          else
1449              return null;
1450      }
1451  
1452      /**
1453       * parse a background position
1454       *
1455       * @access public
1456       * @param  string $css
1457       * @param  &boolean $res flag if conver is ok or not
1458       * @return array ($x, $y)
1459       */
1460      public function convertBackgroundPosition($css, &$res)
1461      {
1462          // init the res
1463          $res = false;
1464  
1465          // explode the value
1466          $css = explode(' ', $css);
1467  
1468          // we must have 2 values. if 0 or >2 : error. if 1 => put center for 2
1469          if (count($css)<2) {
1470              if (!$css[0]) return null;
1471              $css[1] = 'center';
1472          }
1473          if (count($css)>2) return null;
1474  
1475          // prepare the values
1476          $x = 0;
1477          $y = 0;
1478          $res = true;
1479  
1480          // convert the first value
1481          if ($css[0]=='left')        $x = '0%';
1482          else if ($css[0]=='center') $x = '50%';
1483          else if ($css[0]=='right')  $x = '100%';
1484          else if ($css[0]=='top')    $y = '0%';
1485          else if ($css[0]=='bottom') $y = '100%';
1486          else if (preg_match('/^[-]?[0-9\.]+%$/isU', $css[0])) $x = $css[0];
1487          else if ($this->convertToMM($css[0])) $x = $this->convertToMM($css[0]);
1488          else $res = false;
1489  
1490          // convert the second value
1491          if ($css[1]=='left')        $x = '0%';
1492          else if ($css[1]=='right')  $x = '100%';
1493          else if ($css[1]=='top')    $y = '0%';
1494          else if ($css[1]=='center') $y = '50%';
1495          else if ($css[1]=='bottom') $y = '100%';
1496          else if (preg_match('/^[-]?[0-9\.]+%$/isU', $css[1])) $y = $css[1];
1497          else if ($this->convertToMM($css[1])) $y = $this->convertToMM($css[1]);
1498          else $res = false;
1499  
1500          // return the values
1501          return array($x, $y);
1502      }
1503  
1504      /**
1505       * parse a background repeat
1506       *
1507       * @access public
1508       * @param  string $css
1509       * @return string $value
1510       */
1511      public function convertBackgroundRepeat($css)
1512      {
1513          switch($css)
1514          {
1515              case 'repeat':
1516                  return array(true, true);
1517              case 'repeat-x':
1518                  return array(true, false);
1519              case 'repeat-y':
1520                  return array(false, true);
1521              case 'no-repeat':
1522                  return array(false, false);
1523          }
1524          return null;
1525      }
1526  
1527       /**
1528       * convert a distance to mm
1529       *
1530       * @access public
1531       * @param  string $css distance to convert
1532       * @param  float  $old parent distance
1533       * @return float  $value
1534       */
1535      public function convertToMM($css, $old=0.)
1536      {
1537          $css = trim($css);
1538          if (preg_match('/^[0-9\.\-]+$/isU', $css))        $css.= 'px';
1539          if (preg_match('/^[0-9\.\-]+px$/isU', $css))      $css = 25.4/96. * str_replace('px', '', $css);
1540          else if (preg_match('/^[0-9\.\-]+pt$/isU', $css)) $css = 25.4/72. * str_replace('pt', '', $css);
1541          else if (preg_match('/^[0-9\.\-]+in$/isU', $css)) $css = 25.4 * str_replace('in', '', $css);
1542          else if (preg_match('/^[0-9\.\-]+mm$/isU', $css)) $css = 1.*str_replace('mm', '', $css);
1543          else if (preg_match('/^[0-9\.\-]+%$/isU', $css))  $css = 1.*$old*str_replace('%', '', $css)/100.;
1544          else                                              $css = null;
1545  
1546          return $css;
1547      }
1548  
1549      /**
1550       * convert a css radius
1551       *
1552       * @access public
1553       * @param  string $css
1554       * @return float  $value
1555       */
1556      public function convertToRadius($css)
1557      {
1558          // explode the value
1559          $css = explode(' ', $css);
1560  
1561          foreach ($css as $k => $v) {
1562              $v = trim($v);
1563              if ($v) {
1564                  $v = $this->convertToMM($v, 0);
1565                  if ($v!==null) {
1566                      $css[$k] = $v;
1567                  } else {
1568                      unset($css[$k]);
1569                  }
1570              } else {
1571                  unset($css[$k]);
1572              }
1573          }
1574  
1575          return array_values($css);
1576      }
1577  
1578      /**
1579       * convert a css color
1580       *
1581       * @access public
1582       * @param  string $css
1583       * @param  &boolean $res
1584       * @return array (r,g, b)
1585       */
1586      public function convertToColor($css, &$res)
1587      {
1588          // prepare the value
1589          $css = trim($css);
1590          $res = true;
1591  
1592          // if transparent => return null
1593          if (strtolower($css)=='transparent') return array(null, null, null);
1594  
1595          // HTML color
1596          if (isset($this->_htmlColor[strtolower($css)])) {
1597              $css = $this->_htmlColor[strtolower($css)];
1598              $r = floatVal(hexdec(substr($css, 0, 2)));
1599              $v = floatVal(hexdec(substr($css, 2, 2)));
1600              $b = floatVal(hexdec(substr($css, 4, 2)));
1601              return array($r, $v, $b);
1602          }
1603  
1604          // like #FFFFFF
1605          if (preg_match('/^#[0-9A-Fa-f]{6}$/isU', $css)) {
1606              $r = floatVal(hexdec(substr($css, 1, 2)));
1607              $v = floatVal(hexdec(substr($css, 3, 2)));
1608              $b = floatVal(hexdec(substr($css, 5, 2)));
1609              return array($r, $v, $b);
1610          }
1611  
1612          // like #FFF
1613          if (preg_match('/^#[0-9A-F]{3}$/isU', $css)) {
1614              $r = floatVal(hexdec(substr($css, 1, 1).substr($css, 1, 1)));
1615              $v = floatVal(hexdec(substr($css, 2, 1).substr($css, 2, 1)));
1616              $b = floatVal(hexdec(substr($css, 3, 1).substr($css, 3, 1)));
1617              return array($r, $v, $b);
1618          }
1619  
1620          // like rgb(100, 100, 100)
1621          if (preg_match('/rgb\([\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*\)/isU', $css, $match)) {
1622              $r = $this->_convertSubColor($match[1]);
1623              $v = $this->_convertSubColor($match[2]);
1624              $b = $this->_convertSubColor($match[3]);
1625              return array($r*255., $v*255., $b*255.);
1626          }
1627  
1628          // like cmyk(100, 100, 100, 100)
1629          if (preg_match('/cmyk\([\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*\)/isU', $css, $match)) {
1630              $c = $this->_convertSubColor($match[1]);
1631              $m = $this->_convertSubColor($match[2]);
1632              $y = $this->_convertSubColor($match[3]);
1633              $k = $this->_convertSubColor($match[4]);
1634              return array($c*100., $m*100., $y*100., $k*100.);
1635          }
1636  
1637          $res = false;
1638          return array(0., 0., 0.);
1639      }
1640  
1641      /**
1642       * color value to convert
1643       *
1644       * @access protected
1645       * @param  string $c
1646       * @return float $c 0.->1.
1647       */
1648      protected function _convertSubColor($c)
1649      {
1650          if (substr($c, -1)=='%') {
1651              $c = floatVal(substr($c, 0, -1))/100.;
1652          } else {
1653              $c = floatVal($c);
1654              if ($c>1) $c = $c/255.;
1655          }
1656  
1657          return $c;
1658      }
1659  
1660      /**
1661       * read a css content
1662       *
1663       * @access protected
1664       * @param  &string $code
1665       */
1666      protected function _analyseStyle(&$code)
1667      {
1668          // clean the spaces
1669          $code = preg_replace('/[\s]+/', ' ', $code);
1670  
1671          // remove the comments
1672          $code = preg_replace('/\/\*.*?\*\//s', '', $code);
1673  
1674          // split each CSS code "selector { value }"
1675          preg_match_all('/([^{}]+){([^}]*)}/isU', $code, $match);
1676  
1677          // for each CSS code
1678          for ($k=0; $k<count($match[0]); $k++) {
1679  
1680              // selectors
1681              $names = strtolower(trim($match[1][$k]));
1682  
1683              // css style
1684              $styles = trim($match[2][$k]);
1685  
1686              // explode each value
1687              $styles = explode(';', $styles);
1688  
1689              // parse each value
1690              $css = array();
1691              foreach ($styles as $style) {
1692                  $tmp = explode(':', $style);
1693                  if (count($tmp)>1) {
1694                      $cod = $tmp[0]; unset($tmp[0]); $tmp = implode(':', $tmp);
1695                      $css[trim(strtolower($cod))] = trim($tmp);
1696                  }
1697              }
1698  
1699              // explode the names
1700              $names = explode(',', $names);
1701  
1702              // save the values for each names
1703              foreach ($names as $name) {
1704                  // clean the name
1705                  $name = trim($name);
1706  
1707                  // if a selector with somethink lige :hover => continue
1708                  if (strpos($name, ':')!==false) continue;
1709  
1710                  // save the value
1711                  if (!isset($this->css[$name]))
1712                      $this->css[$name] = $css;
1713                  else
1714                      $this->css[$name] = array_merge($this->css[$name], $css);
1715  
1716              }
1717          }
1718  
1719          // get he list of the keys
1720          $this->cssKeys = array_flip(array_keys($this->css));
1721      }
1722  
1723      /**
1724       * Extract the css files from a html code
1725       *
1726       * @access public
1727       * @param  string   &$html
1728       */
1729      public function readStyle(&$html)
1730      {
1731          // the CSS content
1732          $style = ' ';
1733  
1734          // extract the link tags, and remove them in the html code
1735          preg_match_all('/<link([^>]*)>/isU', $html, $match);
1736          $html = preg_replace('/<link[^>]*>/isU', '', $html);
1737          $html = preg_replace('/<\/link[^>]*>/isU', '', $html);
1738  
1739          // analyse each link tag
1740          foreach ($match[1] as $code) {
1741              $tmp = array();
1742  
1743              // read the attributes name=value
1744              $prop = '([a-zA-Z0-9_]+)=([^"\'\s>]+)';
1745              preg_match_all('/'.$prop.'/is', $code, $match);
1746              for ($k=0; $k<count($match[0]); $k++) {
1747                  $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
1748              }
1749  
1750              // read the attributes name="value"
1751              $prop = '([a-zA-Z0-9_]+)=["]([^"]*)["]';
1752              preg_match_all('/'.$prop.'/is', $code, $match);
1753              for ($k=0; $k<count($match[0]); $k++) {
1754                  $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
1755              }
1756  
1757              // read the attributes name='value'
1758              $prop = "([a-zA-Z0-9_]+)=[']([^']*)[']";
1759              preg_match_all('/'.$prop.'/is', $code, $match);
1760              for ($k=0; $k<count($match[0]); $k++) {
1761                  $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
1762              }
1763  
1764              // if type text/css => we keep it
1765              if (isset($tmp['type']) && strtolower($tmp['type'])=='text/css' && isset($tmp['href'])) {
1766  
1767                  // get the href
1768                  $url = $tmp['href'];
1769  
1770                  // get the content of the css file
1771                  $content = @file_get_contents($url);
1772  
1773                  // if "http://" in the url
1774                  if (strpos($url, 'http://')!==false) {
1775  
1776                      // get the domain "http://xxx/"
1777                      $url = str_replace('http://', '', $url);
1778                      $url = explode('/', $url);
1779                      $urlMain = 'http://'.$url[0].'/';
1780  
1781                      // get the absolute url of the path
1782                      $urlSelf = $url; unset($urlSelf[count($urlSelf)-1]); $urlSelf = 'http://'.implode('/', $urlSelf).'/';
1783  
1784                      // adapt the url in the css content
1785                      $content = preg_replace('/url\(([^\\\\][^)]*)\)/isU', 'url('.$urlSelf.'$1)', $content);
1786                      $content = preg_replace('/url\((\\\\[^)]*)\)/isU', 'url('.$urlMain.'$1)', $content);
1787                  } else {
1788  // @TODO correction on url in absolute on a local css content
1789                      // $content = preg_replace('/url\(([^)]*)\)/isU', 'url('.dirname($url).'/$1)', $content);
1790                  }
1791  
1792                  // add to the CSS content
1793                  $style.= $content."\n";
1794              }
1795          }
1796  
1797          // extract the style tags des tags style, and remove them in the html code
1798          preg_match_all('/<style[^>]*>(.*)<\/style[^>]*>/isU', $html, $match);
1799          $html = preg_replace('/<style[^>]*>(.*)<\/style[^>]*>/isU', '', $html);
1800  
1801          // analyse each style tags
1802          foreach ($match[1] as $code) {
1803              // add to the CSS content
1804              $code = str_replace('<!--', '', $code);
1805              $code = str_replace('-->', '', $code);
1806              $style.= $code."\n";
1807          }
1808  
1809          //analyse the css content
1810          $this->_analyseStyle($style);
1811      }
1812  }


Generated: Tue Mar 17 22:47:18 2015 Cross-referenced by PHPXref 0.7.1