File: parsingCss.class.php - Tab length: 1 2 4 8 - Lines: on off - No wrap: on off

0001: <?php
0002: /**
0003:  * HTML2PDF Librairy - parsingCss class
0004:  *
0005:  * HTML => PDF convertor
0006:  * distributed under the LGPL License
0007:  *
0008:  * @author      Laurent MINGUET <webmaster@html2pdf.fr>
0009:  * @version     4.03
0010:  */
0011: 
0012: class HTML2PDF_parsingCss
0013: {
0014:     /**
0015:      * reference to the pdf object
0016:      * @var TCPDF
0017:      */
0018:     protected $_pdf         = null;
0019: 
0020:     protected $_htmlColor   = array(); // list of the HTML colors
0021:     protected $_onlyLeft    = false;   // flag if we are in a sub html => only "text-align:left" is used
0022:     protected $_defaultFont = null;    // default font to use if the asked font does not exist
0023: 
0024:     public    $value        = array(); // current values
0025:     public    $css          = array(); // css values
0026:     public    $cssKeys      = array(); // css key, for the execution order
0027:     public    $table        = array(); // level history
0028: 
0029:     /**
0030:      * Constructor
0031:      *
0032:      * @param  &HTML2PDF_myPdf reference to the PDF $object
0033:      * @access public
0034:      */
0035:     public function __construct(&$pdf)
0036:     {
0037:         $this->_init();
0038:         $this->setPdfParent($pdf);
0039:     }
0040: 
0041:     /**
0042:      * Set the HTML2PDF parent object
0043:      *
0044:      * @param  &HTML2PDF reference to the HTML2PDF parent $object
0045:      * @access public
0046:      */
0047:     public function setPdfParent(&$pdf)
0048:     {
0049:         $this->_pdf = &$pdf;
0050:     }
0051: 
0052:     /**
0053:      * Inform that we want only "test-align:left" because we are in a sub HTML
0054:      *
0055:      * @access public
0056:      */
0057:     public function setOnlyLeft()
0058:     {
0059:         $this->value['text-align'] = 'left';
0060:         $this->_onlyLeft = true;
0061:     }
0062: 
0063:     /**
0064:      * Get the vales of the parent, if exist
0065:      *
0066:      * @return array CSS values
0067:      * @access public
0068:      */
0069:     public function getOldValues()
0070:     {
0071:         return isset($this->table[count($this->table)-1]) ? $this->table[count($this->table)-1] : $this->value;
0072:     }
0073: 
0074:     /**
0075:     * define the Default Font to use, if the font does not exist, or if no font asked
0076:     *
0077:     * @param  string  default font-family. If null : Arial for no font asked, and error fot ont does not exist
0078:     * @return string  old default font-family
0079:     * @access public
0080:     */
0081:     public function setDefaultFont($default = null)
0082:     {
0083:         $old = $this->_defaultFont;
0084:         $this->_defaultFont = $default;
0085:         if ($default) $this->value['font-family'] = $default;
0086:         return $old;
0087:     }
0088: 
0089:      /**
0090:      * Init the object
0091:      *
0092:      * @access protected
0093:      */
0094:     protected function _init()
0095:     {
0096:         // get the Web Colors from TCPDF
0097:         require(K_PATH_MAIN.'htmlcolors.php');
0098:         $this->_htmlColor = $webcolor;
0099: 
0100:         // init the Style
0101:         $this->table = array();
0102:         $this->value = array();
0103:         $this->initStyle();
0104: 
0105:         // Init the styles without legacy
0106:         $this->resetStyle();
0107:     }
0108: 
0109:     /**
0110:      * Init the CSS Style
0111:      *
0112:      * @access public
0113:      */
0114:     public function initStyle()
0115:     {
0116:         $this->value['id_tag']       = 'body';        // tag name
0117:         $this->value['id_name']          = null;         // tag - attribute name
0118:         $this->value['id_id']            = null;         // tag - attribute id
0119:         $this->value['id_class']         = null;         // tag - attribute class
0120:         $this->value['id_lst']           = array('*');   // tag - list of legacy
0121:         $this->value['mini-size']        = 1.;           // specific size report for sup, sub
0122:         $this->value['mini-decal']       = 0;            // specific position report for sup, sub
0123:         $this->value['font-family']      = 'Arial';
0124:         $this->value['font-bold']        = false;
0125:         $this->value['font-italic']      = false;
0126:         $this->value['font-underline']   = false;
0127:         $this->value['font-overline']    = false;
0128:         $this->value['font-linethrough'] = false;
0129:         $this->value['text-transform']   = 'none';
0130:         $this->value['font-size']        = $this->convertToMM('10pt');
0131:         $this->value['text-indent']      = 0;
0132:         $this->value['text-align']       = 'left';
0133:         $this->value['vertical-align']   = 'middle';
0134:         $this->value['line-height']      = 'normal';
0135: 
0136:         $this->value['position']         = null;
0137:         $this->value['x']                = null;
0138:         $this->value['y']                = null;
0139:         $this->value['width']            = 0;
0140:         $this->value['height']           = 0;
0141:         $this->value['top']              = null;
0142:         $this->value['right']            = null;
0143:         $this->value['bottom']           = null;
0144:         $this->value['left']             = null;
0145:         $this->value['float']            = null;
0146:         $this->value['display']          = null;
0147:         $this->value['rotate']           = null;
0148:         $this->value['overflow']         = 'visible';
0149: 
0150:         $this->value['color']            = array(0, 0, 0);
0151:         $this->value['background']       = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null, 'transparency' => null);
0152:         $this->value['border']           = array();
0153:         $this->value['padding']          = array();
0154:         $this->value['margin']           = array();
0155:         $this->value['margin-auto']      = false;
0156: 
0157:         $this->value['list-style-type']  = '';
0158:         $this->value['list-style-image'] = '';
0159: 
0160:         $this->value['xc'] = null;
0161:         $this->value['yc'] = null;
0162:     }
0163: 
0164:     /**
0165:      * Init the CSS Style without legacy
0166:      *
0167:      * @param  string  tag name
0168:      * @access public
0169:      */
0170:     public function resetStyle($tagName = '')
0171:     {
0172:         // prepare somme values
0173:         $border = $this->readBorder('solid 1px #000000');
0174:         $units = array(
0175:             '1px' => $this->convertToMM('1px'),
0176:             '5px' => $this->convertToMM('5px'),
0177:         );
0178: 
0179: 
0180:         // prepare the Collapse attribute
0181:         $collapse = isset($this->value['border']['collapse']) ? $this->value['border']['collapse'] : false;
0182:         if (!in_array($tagName, array('tr', 'td', 'th', 'thead', 'tbody', 'tfoot'))) $collapse = false;
0183: 
0184:         // set the global css values
0185:         $this->value['position']   = null;
0186:         $this->value['x']          = null;
0187:         $this->value['y']          = null;
0188:         $this->value['width']      = 0;
0189:         $this->value['height']     = 0;
0190:         $this->value['top']        = null;
0191:         $this->value['right']      = null;
0192:         $this->value['bottom']     = null;
0193:         $this->value['left']       = null;
0194:         $this->value['float']      = null;
0195:         $this->value['display']    = null;
0196:         $this->value['rotate']     = null;
0197:         $this->value['overflow']   = 'visible';
0198:         $this->value['background'] = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null, 'transparency' => null);
0199:         $this->value['border']     = array(
0200:             't' => $this->readBorder('none'),
0201:             'r' => $this->readBorder('none'),
0202:             'b' => $this->readBorder('none'),
0203:             'l' => $this->readBorder('none'),
0204:             'radius' => array(
0205:                 'tl' => array(0, 0),
0206:                 'tr' => array(0, 0),
0207:                 'br' => array(0, 0),
0208:                 'bl' => array(0, 0)
0209:             ),
0210:             'collapse' => $collapse,
0211:         );
0212: 
0213:         // specific values for some tags
0214:         if (!in_array($tagName, array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) {
0215:             $this->value['margin'] = array('t'=>0,'r'=>0,'b'=>0,'l'=>0);
0216:         }
0217: 
0218:         if (in_array($tagName, array('input', 'select', 'textarea'))) {
0219:             $this->value['border']['t'] = null;
0220:             $this->value['border']['r'] = null;
0221:             $this->value['border']['b'] = null;
0222:             $this->value['border']['l'] = null;
0223:         }
0224: 
0225:         if ($tagName=='p') {
0226:             $this->value['margin']['t'] = null;
0227:             $this->value['margin']['b'] = null;
0228:         }
0229:         if ($tagName=='blockquote') {
0230:             $this->value['margin']['t'] = 3;
0231:             $this->value['margin']['r'] = 3;
0232:             $this->value['margin']['b'] = 3;
0233:             $this->value['margin']['l'] = 6;
0234:         }
0235:         $this->value['margin-auto'] = false;
0236: 
0237:         if (in_array($tagName, array('blockquote', 'div', 'fieldset'))) {
0238:             $this->value['vertical-align'] = 'top';
0239:         }
0240: 
0241:         if (in_array($tagName, array('fieldset', 'legend'))) {
0242:             $this->value['border'] = array(
0243:                 't' => $border,
0244:                 'r' => $border,
0245:                 'b' => $border,
0246:                 'l' => $border,
0247:                 'radius' => array(
0248:                     'tl' => array($units['5px'], $units['5px']),
0249:                     'tr' => array($units['5px'], $units['5px']),
0250:                     'br' => array($units['5px'], $units['5px']),
0251:                     'bl' => array($units['5px'], $units['5px'])
0252:                 ),
0253:                 'collapse' => false,
0254:             );
0255:         }
0256: 
0257:         if (in_array($tagName, array('ul', 'li'))) {
0258:             $this->value['list-style-type']  = '';
0259:             $this->value['list-style-image'] = '';
0260:         }
0261: 
0262:         if (!in_array($tagName, array('tr', 'td'))) {
0263:             $this->value['padding'] = array(
0264:                 't' => 0,
0265:                 'r' => 0,
0266:                 'b' => 0,
0267:                 'l' => 0
0268:             );
0269:         } else {
0270:             $this->value['padding'] = array(
0271:                 't' => $units['1px'],
0272:                 'r' => $units['1px'],
0273:                 'b' => $units['1px'],
0274:                 'l' => $units['1px']
0275:             );
0276:         }
0277: 
0278:         if ($tagName=='hr') {
0279:             $this->value['border'] = array(
0280:                 't' => $border,
0281:                 'r' => $border,
0282:                 'b' => $border,
0283:                 'l' => $border,
0284:                 'radius' => array(
0285:                     'tl' => array(0, 0),
0286:                     'tr' => array(0, 0),
0287:                     'br' => array(0, 0),
0288:                     'bl' => array(0, 0)
0289:                 ),
0290:                 'collapse' => false,
0291:             );
0292:             $this->convertBackground('#FFFFFF', $this->value['background']);
0293:         }
0294: 
0295:         $this->value['xc'] = null;
0296:         $this->value['yc'] = null;
0297:     }
0298: 
0299:     /**
0300:      * Init the PDF Font
0301:      *
0302:      * @access public
0303:      */
0304:     public function fontSet()
0305:     {
0306:         $family = strtolower($this->value['font-family']);
0307: 
0308:         $b = ($this->value['font-bold']        ? 'B' : '');
0309:         $i = ($this->value['font-italic']      ? 'I' : '');
0310:         $u = ($this->value['font-underline']   ? 'U' : '');
0311:         $d = ($this->value['font-linethrough'] ? 'D' : '');
0312:         $o = ($this->value['font-overline']    ? 'O' : '');
0313: 
0314:         // font style
0315:         $style = $b.$i;
0316: 
0317:         if ($this->_defaultFont) {
0318:             if($family=='arial')
0319:                 $family='helvetica';
0320:             elseif($family=='symbol' || $family=='zapfdingbats')
0321:                 $style='';
0322: 
0323:             $fontkey = $family.$style;
0324:             if (!$this->_pdf->isLoadedFont($fontkey))
0325:                 $family = $this->_defaultFont;
0326:         }
0327: 
0328:         if($family=='arial')
0329:             $family='helvetica';
0330:         elseif($family=='symbol' || $family=='zapfdingbats')
0331:             $style='';
0332: 
0333:         // complete style
0334:         $style.= $u.$d.$o;
0335: 
0336:         // size : mm => pt
0337:         $size = $this->value['font-size'];
0338:         $size = 72 * $size / 25.4;
0339: 
0340:         // apply the font
0341:         $this->_pdf->SetFont($family, $style, $this->value['mini-size']*$size);
0342:         $this->_pdf->setTextColorArray($this->value['color']);
0343:         if ($this->value['background']['color'])
0344:             $this->_pdf->setFillColorArray($this->value['background']['color']);
0345:         else
0346:             $this->_pdf->setFillColor(255);
0347:     }
0348: 
0349:      /**
0350:      * add a level in the CSS history
0351:      *
0352:      * @access public
0353:      */
0354:     public function save()
0355:     {
0356:         array_push($this->table, $this->value);
0357:     }
0358: 
0359:      /**
0360:      * remove a level in the CSS history
0361:      *
0362:      * @access public
0363:      */
0364:     public function load()
0365:     {
0366:         if (count($this->table)) {
0367:             $this->value = array_pop($this->table);
0368:         }
0369:     }
0370: 
0371:      /**
0372:      * restore the Y positiony (used after a span)
0373:      *
0374:      * @access public
0375:      */
0376:     public function restorePosition()
0377:     {
0378:         if ($this->value['y']==$this->_pdf->getY()) $this->_pdf->setY($this->value['yc'], false);
0379:     }
0380: 
0381:      /**
0382:      * set the New position for the current Tag
0383:      *
0384:      * @access public
0385:      */
0386:     public function setPosition()
0387:     {
0388:         // get the current position
0389:         $currentX = $this->_pdf->getX();
0390:         $currentY = $this->_pdf->getY();
0391: 
0392:         // save it
0393:         $this->value['xc'] = $currentX;
0394:         $this->value['yc'] = $currentY;
0395: 
0396:         if ($this->value['position']=='relative' || $this->value['position']=='absolute') {
0397:             if ($this->value['right']!==null) {
0398:                 $x = $this->getLastWidth(true) - $this->value['right'] - $this->value['width'];
0399:                 if ($this->value['margin']['r']) $x-= $this->value['margin']['r'];
0400:             } else {
0401:                 $x = $this->value['left'];
0402:                 if ($this->value['margin']['l']) $x+= $this->value['margin']['l'];
0403:             }
0404: 
0405:             if ($this->value['bottom']!==null) {
0406:                 $y = $this->getLastHeight(true) - $this->value['bottom'] - $this->value['height'];
0407:                 if ($this->value['margin']['b']) $y-= $this->value['margin']['b'];
0408:             } else {
0409:                 $y = $this->value['top'];
0410:                 if ($this->value['margin']['t']) $y+= $this->value['margin']['t'];
0411:             }
0412: 
0413:             if ($this->value['position']=='relative') {
0414:                 $this->value['x'] = $currentX + $x;
0415:                 $this->value['y'] = $currentY + $y;
0416:             } else {
0417:                 $this->value['x'] = $this->_getLastAbsoluteX()+$x;
0418:                 $this->value['y'] = $this->_getLastAbsoluteY()+$y;
0419:             }
0420:         } else {
0421:             $this->value['x'] = $currentX;
0422:             $this->value['y'] = $currentY;
0423:             if ($this->value['margin']['l']) $this->value['x']+= $this->value['margin']['l'];
0424:             if ($this->value['margin']['t']) $this->value['y']+= $this->value['margin']['t'];
0425:         }
0426: 
0427:         // save the new position
0428:         $this->_pdf->setXY($this->value['x'], $this->value['y']);
0429:     }
0430: 
0431:      /**
0432:      * Analise the CSS style to convert it into Form style
0433:      *
0434:      * @access public
0435:      * @param  array    styles
0436:      */
0437:     public function getFormStyle()
0438:     {
0439:         $prop = array();
0440: 
0441:         $prop['alignment'] = $this->value['text-align'];
0442: 
0443:         if (isset($this->value['background']['color']) && is_array($this->value['background']['color'])) {
0444:             $prop['fillColor'] = $this->value['background']['color'];
0445:         }
0446: 
0447:         if (isset($this->value['border']['t']['color'])) {
0448:             $prop['strokeColor'] = $this->value['border']['t']['color'];
0449:         }
0450: 
0451:         if (isset($this->value['border']['t']['width'])) {
0452:             $prop['lineWidth'] = $this->value['border']['t']['width'];
0453:         }
0454: 
0455:         if (isset($this->value['border']['t']['type'])) {
0456:             $prop['borderStyle'] = $this->value['border']['t']['type'];
0457:         }
0458: 
0459:         if (!empty($this->value['color'])) {
0460:             $prop['textColor'] = $this->value['color'];
0461:         }
0462: 
0463:         if (!empty($this->value['font-size'])) {
0464:             $prop['textSize'] = $this->value['font-size'];
0465:         }
0466: 
0467:         return $prop;
0468:     }
0469: 
0470:      /**
0471:      * Analise the CSS style to convert it into SVG style
0472:      *
0473:      * @access public
0474:      * @param  string   tag name
0475:      * @param  array    styles
0476:      */
0477:     public function getSvgStyle($tagName, &$param)
0478:     {
0479:         // prepare
0480:         $tagName = strtolower($tagName);
0481:         $id   = isset($param['id'])   ? strtolower(trim($param['id']))   : null; if (!$id)   $id   = null;
0482:         $name = isset($param['name']) ? strtolower(trim($param['name'])) : null; if (!$name) $name = null;
0483: 
0484:         // read the class attribute
0485:         $class = array();
0486:         $tmp = isset($param['class']) ? strtolower(trim($param['class'])) : '';
0487:         $tmp = explode(' ', $tmp);
0488:         foreach ($tmp as $k => $v) {
0489:             $v = trim($v);
0490:             if ($v) $class[] = $v;
0491:         }
0492: 
0493:         // identify the tag, and the direct styles
0494:         $this->value['id_tag'] = $tagName;
0495:         $this->value['id_name']   = $name;
0496:         $this->value['id_id']     = $id;
0497:         $this->value['id_class']  = $class;
0498:         $this->value['id_lst']    = array();
0499:         $this->value['id_lst'][] = '*';
0500:         $this->value['id_lst'][] = $tagName;
0501:         if (!isset($this->value['svg'])) {
0502:             $this->value['svg'] = array(
0503:                 'stroke'         => null,
0504:                 'stroke-width'   => $this->convertToMM('1pt'),
0505:                 'fill'           => null,
0506:                 'fill-opacity'   => null,
0507:             );
0508:         }
0509: 
0510:         if (count($class)) {
0511:             foreach ($class as $v) {
0512:                 $this->value['id_lst'][] = '*.'.$v;
0513:                 $this->value['id_lst'][] = '.'.$v;
0514:                 $this->value['id_lst'][] = $tagName.'.'.$v;
0515:             }
0516:         }
0517:         if ($id) {
0518:             $this->value['id_lst'][] = '*#'.$id;
0519:             $this->value['id_lst'][] = '#'.$id;
0520:             $this->value['id_lst'][] = $tagName.'#'.$id;
0521:         }
0522: 
0523:         // CSS style
0524:         $styles = $this->_getFromCSS();
0525: 
0526:         // adding the style from the tag
0527:         $styles = array_merge($styles, $param['style']);
0528: 
0529:         if (isset($styles['stroke']))        $this->value['svg']['stroke']       = $this->convertToColor($styles['stroke'], $res);
0530:         if (isset($styles['stroke-width']))  $this->value['svg']['stroke-width'] = $this->convertToMM($styles['stroke-width']);
0531:         if (isset($styles['fill']))          $this->value['svg']['fill']         = $this->convertToColor($styles['fill'], $res);
0532:         if (isset($styles['fill-opacity']))  $this->value['svg']['fill-opacity'] = 1.*$styles['fill-opacity'];
0533: 
0534:         return $this->value['svg'];
0535:     }
0536: 
0537:     /**
0538:      * analyse the css properties from the HTML parsing
0539:      *
0540:      * @access public
0541:      * @param  string  $tagName
0542:      * @param  array   $param
0543:      * @param  array   $legacy
0544:      */
0545:     public function analyse($tagName, &$param, $legacy = null)
0546:     {
0547:         // prepare the informations
0548:         $tagName = strtolower($tagName);
0549:         $id   = isset($param['id'])   ? strtolower(trim($param['id']))    : null; if (!$id)   $id   = null;
0550:         $name = isset($param['name']) ? strtolower(trim($param['name']))  : null; if (!$name) $name = null;
0551: 
0552:         // get the class names to use
0553:         $class = array();
0554:         $tmp = isset($param['class']) ? strtolower(trim($param['class'])) : '';
0555:         $tmp = explode(' ', $tmp);
0556:         foreach ($tmp as $k => $v) {
0557:             $v = trim($v);
0558:             if ($v) $class[] = $v;
0559:         }
0560: 
0561:         // prepare the values, and the list of css tags to identify
0562:         $this->value['id_tag']   = $tagName;
0563:         $this->value['id_name']  = $name;
0564:         $this->value['id_id']    = $id;
0565:         $this->value['id_class'] = $class;
0566:         $this->value['id_lst']   = array();
0567:         $this->value['id_lst'][] = '*';
0568:         $this->value['id_lst'][] = $tagName;
0569:         if (count($class)) {
0570:             foreach ($class as $v) {
0571:                 $this->value['id_lst'][] = '*.'.$v;
0572:                 $this->value['id_lst'][] = '.'.$v;
0573:                 $this->value['id_lst'][] = $tagName.'.'.$v;
0574:             }
0575:         }
0576:         if ($id) {
0577:             $this->value['id_lst'][] = '*#'.$id;
0578:             $this->value['id_lst'][] = '#'.$id;
0579:             $this->value['id_lst'][] = $tagName.'#'.$id;
0580:         }
0581: 
0582:         // get the css styles from class
0583:         $styles = $this->_getFromCSS();
0584: 
0585:         // merge with the css styles from tag
0586:         $styles = array_merge($styles, $param['style']);
0587:         if (isset($param['allwidth']) && !isset($styles['width'])) $styles['width'] = '100%';
0588: 
0589:         // reset some styles, depending on the tag name
0590:         $this->resetStyle($tagName);
0591: 
0592:         // add the legacy values
0593:         if ($legacy) {
0594:             foreach ($legacy as $legacyName => $legacyValue) {
0595:                 if (is_array($legacyValue)) {
0596:                     foreach($legacyValue as $legacy2Name => $legacy2Value)
0597:                         $this->value[$legacyName][$legacy2Name] = $legacy2Value;
0598:                 } else {
0599:                     $this->value[$legacyName] = $legacyValue;
0600:                 }
0601:             }
0602:         }
0603: 
0604:         // some flags
0605:         $correctWidth = false;
0606:         $noWidth = true;
0607: 
0608:         // read all the css styles
0609:         foreach ($styles as $nom => $val) {
0610:             switch($nom)
0611:             {
0612:                 case 'font-family':
0613:                     $val = explode(',', $val);
0614:                     $val = trim($val[0]);
0615:                     if ($val) $this->value['font-family'] = $val;
0616:                     break;
0617: 
0618:                 case 'font-weight':
0619:                     $this->value['font-bold'] = ($val=='bold');
0620:                     break;
0621: 
0622:                 case 'font-style':
0623:                     $this->value['font-italic'] = ($val=='italic');
0624:                     break;
0625: 
0626:                 case 'text-decoration':
0627:                     $val = explode(' ', $val);
0628:                     $this->value['font-underline']   = (in_array('underline', $val));
0629:                     $this->value['font-overline']    = (in_array('overline', $val));
0630:                     $this->value['font-linethrough'] = (in_array('line-through', $val));
0631:                     break;
0632: 
0633:                 case 'text-indent':
0634:                     $this->value['text-indent'] = $this->convertToMM($val);
0635:                     break;
0636: 
0637:                 case 'text-transform':
0638:                     if (!in_array($val, array('none', 'capitalize', 'uppercase', 'lowercase'))) $val = 'none';
0639:                     $this->value['text-transform']  = $val;
0640:                     break;
0641: 
0642:                 case 'font-size':
0643:                     $val = $this->convertToMM($val, $this->value['font-size']);
0644:                     if ($val) $this->value['font-size'] = $val;
0645:                     break;
0646: 
0647:                 case 'color':
0648:                     $res = null;
0649:                     $this->value['color'] = $this->convertToColor($val, $res);
0650:                     if ($tagName=='hr') {
0651:                         $this->value['border']['l']['color'] = $this->value['color'];
0652:                         $this->value['border']['t']['color'] = $this->value['color'];
0653:                         $this->value['border']['r']['color'] = $this->value['color'];
0654:                         $this->value['border']['b']['color'] = $this->value['color'];
0655:                     }
0656:                     break;
0657: 
0658:                 case 'text-align':
0659:                     $val = strtolower($val);
0660:                     if (!in_array($val, array('left', 'right', 'center', 'justify', 'li_right'))) $val = 'left';
0661:                     $this->value['text-align'] = $val;
0662:                     break;
0663: 
0664:                 case 'vertical-align':
0665:                     $this->value['vertical-align'] = $val;
0666:                     break;
0667: 
0668:                 case 'width':
0669:                     $this->value['width'] = $this->convertToMM($val, $this->getLastWidth());
0670:                     if ($this->value['width'] && substr($val, -1)=='%') $correctWidth=true;
0671:                     $noWidth = false;
0672:                     break;
0673: 
0674:                 case 'height':
0675:                     $this->value['height'] = $this->convertToMM($val, $this->getLastHeight());
0676:                     break;
0677: 
0678:                 case 'line-height':
0679:                     if (preg_match('/^[0-9\.]+$/isU', $val)) $val = floor($val*100).'%';
0680:                     $this->value['line-height'] = $val;
0681:                     break;
0682: 
0683:                 case 'rotate':
0684:                     if (!in_array($val, array(0, -90, 90, 180, 270, -180, -270))) $val = null;
0685:                     if ($val<0) $val+= 360;
0686:                     $this->value['rotate'] = $val;
0687:                     break;
0688: 
0689:                 case 'overflow':
0690:                     if (!in_array($val, array('visible', 'hidden'))) $val = 'visible';
0691:                     $this->value['overflow'] = $val;
0692:                     break;
0693: 
0694:                 case 'padding':
0695:                     $val = explode(' ', $val);
0696:                     foreach ($val as $k => $v) {
0697:                         $v = trim($v);
0698:                         if ($v!='') {
0699:                             $val[$k] = $v;
0700:                         } else {
0701:                             unset($val[$k]);
0702:                         }
0703:                     }
0704:                     $val = array_values($val);
0705:                     $this->_duplicateBorder($val);
0706:                     $this->value['padding']['t'] = $this->convertToMM($val[0], 0);
0707:                     $this->value['padding']['r'] = $this->convertToMM($val[1], 0);
0708:                     $this->value['padding']['b'] = $this->convertToMM($val[2], 0);
0709:                     $this->value['padding']['l'] = $this->convertToMM($val[3], 0);
0710:                     break;
0711: 
0712:                 case 'padding-top':
0713:                     $this->value['padding']['t'] = $this->convertToMM($val, 0);
0714:                     break;
0715: 
0716:                 case 'padding-right':
0717:                     $this->value['padding']['r'] = $this->convertToMM($val, 0);
0718:                     break;
0719: 
0720:                 case 'padding-bottom':
0721:                     $this->value['padding']['b'] = $this->convertToMM($val, 0);
0722:                     break;
0723: 
0724:                 case 'padding-left':
0725:                     $this->value['padding']['l'] = $this->convertToMM($val, 0);
0726:                     break;
0727: 
0728:                 case 'margin':
0729:                     if ($val=='auto') {
0730:                         $this->value['margin-auto'] = true;
0731:                         break;
0732:                     }
0733:                     $val = explode(' ', $val);
0734:                     foreach ($val as $k => $v) {
0735:                         $v = trim($v);
0736:                         if ($v!='') {
0737:                             $val[$k] = $v;
0738:                         } else {
0739:                             unset($val[$k]);
0740:                         }
0741:                     }
0742:                     $val = array_values($val);
0743:                     $this->_duplicateBorder($val);
0744:                     $this->value['margin']['t'] = $this->convertToMM($val[0], 0);
0745:                     $this->value['margin']['r'] = $this->convertToMM($val[1], 0);
0746:                     $this->value['margin']['b'] = $this->convertToMM($val[2], 0);
0747:                     $this->value['margin']['l'] = $this->convertToMM($val[3], 0);
0748:                     break;
0749: 
0750:                 case 'margin-top':
0751:                     $this->value['margin']['t'] = $this->convertToMM($val, 0);
0752:                     break;
0753: 
0754:                 case 'margin-right':
0755:                     $this->value['margin']['r'] = $this->convertToMM($val, 0);
0756:                     break;
0757: 
0758:                 case 'margin-bottom':
0759:                     $this->value['margin']['b'] = $this->convertToMM($val, 0);
0760:                     break;
0761: 
0762:                 case 'margin-left':
0763:                     $this->value['margin']['l'] = $this->convertToMM($val, 0);
0764:                     break;
0765: 
0766:                 case 'border':
0767:                     $val = $this->readBorder($val);
0768:                     $this->value['border']['t'] = $val;
0769:                     $this->value['border']['r'] = $val;
0770:                     $this->value['border']['b'] = $val;
0771:                     $this->value['border']['l'] = $val;
0772:                     break;
0773: 
0774:                 case 'border-style':
0775:                     $val = explode(' ', $val);
0776:                     foreach ($val as $valK => $valV) {
0777:                         if (!in_array($valV, array('solid', 'dotted', 'dashed'))) {
0778:                             $val[$valK] = null;
0779:                         }
0780:                     }
0781:                     $this->_duplicateBorder($val);
0782:                     if ($val[0]) $this->value['border']['t']['type'] = $val[0];
0783:                     if ($val[1]) $this->value['border']['r']['type'] = $val[1];
0784:                     if ($val[2]) $this->value['border']['b']['type'] = $val[2];
0785:                     if ($val[3]) $this->value['border']['l']['type'] = $val[3];
0786:                     break;
0787: 
0788:                 case 'border-top-style':
0789:                     if (in_array($val, array('solid', 'dotted', 'dashed'))) {
0790:                         $this->value['border']['t']['type'] = $val;
0791:                     }
0792:                     break;
0793: 
0794:                 case 'border-right-style':
0795:                     if (in_array($val, array('solid', 'dotted', 'dashed'))) {
0796:                         $this->value['border']['r']['type'] = $val;
0797:                     }
0798:                     break;
0799: 
0800:                 case 'border-bottom-style':
0801:                     if (in_array($val, array('solid', 'dotted', 'dashed'))) {
0802:                         $this->value['border']['b']['type'] = $val;
0803:                     }
0804:                     break;
0805: 
0806:                 case 'border-left-style':
0807:                     if (in_array($val, array('solid', 'dotted', 'dashed')))
0808:                         $this->value['border']['l']['type'] = $val;
0809:                     break;
0810: 
0811:                 case 'border-color':
0812:                     $res = false;
0813:                     $val = preg_replace('/,[\s]+/', ',', $val);
0814:                     $val = explode(' ', $val);
0815:                     foreach ($val as $valK => $valV) {
0816:                             $val[$valK] = $this->convertToColor($valV, $res);
0817:                             if (!$res) {
0818:                                 $val[$valK] = null;
0819:                             }
0820:                     }
0821:                     $this->_duplicateBorder($val);
0822:                     if (is_array($val[0])) $this->value['border']['t']['color'] = $val[0];
0823:                     if (is_array($val[1])) $this->value['border']['r']['color'] = $val[1];
0824:                     if (is_array($val[2])) $this->value['border']['b']['color'] = $val[2];
0825:                     if (is_array($val[3])) $this->value['border']['l']['color'] = $val[3];
0826: 
0827:                     break;
0828: 
0829:                 case 'border-top-color':
0830:                     $res = false;
0831:                     $val = $this->convertToColor($val, $res);
0832:                     if ($res) $this->value['border']['t']['color'] = $val;
0833:                     break;
0834: 
0835:                 case 'border-right-color':
0836:                     $res = false;
0837:                     $val = $this->convertToColor($val, $res);
0838:                     if ($res) $this->value['border']['r']['color'] = $val;
0839:                     break;
0840: 
0841:                 case 'border-bottom-color':
0842:                     $res = false;
0843:                     $val = $this->convertToColor($val, $res);
0844:                     if ($res) $this->value['border']['b']['color'] = $val;
0845:                     break;
0846: 
0847:                 case 'border-left-color':
0848:                     $res = false;
0849:                     $val = $this->convertToColor($val, $res);
0850:                     if ($res) $this->value['border']['l']['color'] = $val;
0851:                     break;
0852: 
0853:                 case 'border-width':
0854:                     $val = explode(' ', $val);
0855:                     foreach ($val as $valK => $valV) {
0856:                             $val[$valK] = $this->convertToMM($valV, 0);
0857:                     }
0858:                     $this->_duplicateBorder($val);
0859:                     if ($val[0]) $this->value['border']['t']['width'] = $val[0];
0860:                     if ($val[1]) $this->value['border']['r']['width'] = $val[1];
0861:                     if ($val[2]) $this->value['border']['b']['width'] = $val[2];
0862:                     if ($val[3]) $this->value['border']['l']['width'] = $val[3];
0863:                     break;
0864: 
0865:                 case 'border-top-width':
0866:                     $val = $this->convertToMM($val, 0);
0867:                     if ($val) $this->value['border']['t']['width'] = $val;
0868:                     break;
0869: 
0870:                 case 'border-right-width':
0871:                     $val = $this->convertToMM($val, 0);
0872:                     if ($val) $this->value['border']['r']['width'] = $val;
0873:                     break;
0874: 
0875:                 case 'border-bottom-width':
0876:                     $val = $this->convertToMM($val, 0);
0877:                     if ($val) $this->value['border']['b']['width'] = $val;
0878:                     break;
0879: 
0880:                 case 'border-left-width':
0881:                     $val = $this->convertToMM($val, 0);
0882:                     if ($val) $this->value['border']['l']['width'] = $val;
0883:                     break;
0884: 
0885:                 case 'border-collapse':
0886:                     if ($tagName=='table') $this->value['border']['collapse'] = ($val=='collapse');
0887:                     break;
0888: 
0889:                 case 'border-radius':
0890:                     $val = explode('/', $val);
0891:                     if (count($val)>2) {
0892:                         break;
0893:                     }
0894:                     $valH = $this->convertToRadius(trim($val[0]));
0895:                     if (count($valH)<1 || count($valH)>4) {
0896:                         break;
0897:                     }
0898:                     if (!isset($valH[1])) $valH[1] = $valH[0];
0899:                     if (!isset($valH[2])) $valH = array($valH[0], $valH[0], $valH[1], $valH[1]);
0900:                     if (!isset($valH[3])) $valH[3] = $valH[1];
0901:                     if (isset($val[1])) {
0902:                         $valV = $this->convertToRadius(trim($val[1]));
0903:                         if (count($valV)<1 || count($valV)>4) {
0904:                             break;
0905:                         }
0906:                         if (!isset($valV[1])) $valV[1] = $valV[0];
0907:                         if (!isset($valV[2])) $valV = array($valV[0], $valV[0], $valV[1], $valV[1]);
0908:                         if (!isset($valV[3])) $valV[3] = $valV[1];
0909:                     } else {
0910:                         $valV = $valH;
0911:                     }
0912:                     $this->value['border']['radius'] = array(
0913:                                 'tl' => array($valH[0], $valV[0]),
0914:                                 'tr' => array($valH[1], $valV[1]),
0915:                                 'br' => array($valH[2], $valV[2]),
0916:                                 'bl' => array($valH[3], $valV[3])
0917:                             );
0918:                     break;
0919: 
0920:                 case 'border-top-left-radius':
0921:                     $val = $this->convertToRadius($val);
0922:                     if (count($val)<1 || count($val)>2) {
0923:                         break;
0924:                     }
0925:                     $this->value['border']['radius']['tl'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
0926:                     break;
0927: 
0928:                 case 'border-top-right-radius':
0929:                     $val = $this->convertToRadius($val);
0930:                     if (count($val)<1 || count($val)>2) {
0931:                         break;
0932:                     }
0933:                     $this->value['border']['radius']['tr'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
0934:                     break;
0935: 
0936:                 case 'border-bottom-right-radius':
0937:                     $val = $this->convertToRadius($val);
0938:                     if (count($val)<1 || count($val)>2) {
0939:                         break;
0940:                     }
0941:                     $this->value['border']['radius']['br'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
0942:                     break;
0943: 
0944:                 case 'border-bottom-left-radius':
0945:                     $val = $this->convertToRadius($val);
0946:                     if (count($val)<1 || count($val)>2) {
0947:                         break;
0948:                     }
0949:                     $this->value['border']['radius']['bl'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
0950:                     break;
0951: 
0952:                 case 'border-top':
0953:                     $this->value['border']['t'] = $this->readBorder($val);
0954:                     break;
0955: 
0956:                 case 'border-right':
0957:                     $this->value['border']['r'] = $this->readBorder($val);
0958:                     break;
0959: 
0960:                 case 'border-bottom':
0961:                     $this->value['border']['b'] = $this->readBorder($val);
0962:                     break;
0963: 
0964:                 case 'border-left':
0965:                     $this->value['border']['l'] = $this->readBorder($val);
0966:                     break;
0967: 
0968:                 case 'background-color':
0969:                     $this->value['background']['color'] = $this->convertBackgroundColor($val);
0970:                     break;
0971: 
0972:                 case 'background-image':
0973:                     $this->value['background']['image'] = $this->convertBackgroundImage($val);
0974:                     break;
0975: 
0976:                 case 'background-position':
0977:                     $res = null;
0978:                     $this->value['background']['position'] = $this->convertBackgroundPosition($val, $res);
0979:                     break;
0980: 
0981:                 case 'background-repeat':
0982:                     $this->value['background']['repeat'] = $this->convertBackgroundRepeat($val);
0983:                     break;
0984: 
0985:                 case 'background':
0986:                     $this->convertBackground($val, $this->value['background']);
0987:                     break;
0988: 
0989:                 case 'position':
0990:                     if ($val=='absolute')       $this->value['position'] = 'absolute';
0991:                     else if ($val=='relative')  $this->value['position'] = 'relative';
0992:                     else                        $this->value['position'] = null;
0993:                     break;
0994: 
0995:                 case 'float':
0996:                     if ($val=='left')           $this->value['float'] = 'left';
0997:                     else if ($val=='right')     $this->value['float'] = 'right';
0998:                     else                        $this->value['float'] = null;
0999:                     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 rgba(100, 100, 100, 1)
1629:         if (preg_match('/rgba\([\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*\)/isU', $css, $match)) {
1630:             $r = $this->_convertSubColor($match[1]);
1631:             $v = $this->_convertSubColor($match[2]);
1632:             $b = $this->_convertSubColor($match[3]);
1633:             $this->value['background']['transparency'] = 0.6;
1634:             return array($r*255., $v*255., $b*255.);
1635:         }
1636: 
1637:         // like cmyk(100, 100, 100, 100)
1638:         if (preg_match('/cmyk\([\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9]+(\.[0-9]+)?)[\s]*\)/isU', $css, $match)) {
1639:             $c = $this->_convertSubColor($match[1]);
1640:             $m = $this->_convertSubColor($match[2]);
1641:             $y = $this->_convertSubColor($match[3]);
1642:             $k = $this->_convertTransparency($match[4]);
1643:             return array($c*100., $m*100., $y*100., $k*100.);
1644:         }
1645: 
1646:         $res = false;
1647:         return array(0., 0., 0.);
1648:     }
1649: 
1650:     /**
1651:      * color value to convert
1652:      *
1653:      * @access protected
1654:      * @param  string $c
1655:      * @return float $c 0.->1.
1656:      */
1657:     protected function _convertSubColor($c)
1658:     {
1659:         if (substr($c, -1)=='%') {
1660:             $c = floatVal(substr($c, 0, -1))/100.;
1661:         } else {
1662:             $c = floatVal($c);
1663:             if ($c>1) $c = $c/255.;
1664:         }
1665: 
1666:         return $c;
1667:     }
1668:    
1669:     protected function _convertTransparency($c)
1670:     {
1671:         if (substr($c, -1)=='%') {
1672:             $c = floatVal(substr($c, 0, -1))/100.;
1673:         } else {
1674:             $c = floatVal($c);
1675:             if ($c>1) $c = 1;
1676:         }
1677: 
1678:         return $c;
1679:     }
1680:    
1681: 
1682:     /**
1683:      * read a css content
1684:      *
1685:      * @access protected
1686:      * @param  &string $code
1687:      */
1688:     protected function _analyseStyle(&$code)
1689:     {
1690:         // clean the spaces
1691:         $code = preg_replace('/[\s]+/', ' ', $code);
1692: 
1693:         // remove the comments
1694:         $code = preg_replace('/\/\*.*?\*\//s', '', $code);
1695: 
1696:         // split each CSS code "selector { value }"
1697:         preg_match_all('/([^{}]+){([^}]*)}/isU', $code, $match);
1698: 
1699:         // for each CSS code
1700:         for ($k=0; $k<count($match[0]); $k++) {
1701: 
1702:             // selectors
1703:             $names = strtolower(trim($match[1][$k]));
1704: 
1705:             // css style
1706:             $styles = trim($match[2][$k]);
1707: 
1708:             // explode each value
1709:             $styles = explode(';', $styles);
1710: 
1711:             // parse each value
1712:             $css = array();
1713:             foreach ($styles as $style) {
1714:                 $tmp = explode(':', $style);
1715:                 if (count($tmp)>1) {
1716:                     $cod = $tmp[0]; unset($tmp[0]); $tmp = implode(':', $tmp);
1717:                     $css[trim(strtolower($cod))] = trim($tmp);
1718:                 }
1719:             }
1720: 
1721:             // explode the names
1722:             $names = explode(',', $names);
1723: 
1724:             // save the values for each names
1725:             foreach ($names as $name) {
1726:                 // clean the name
1727:                 $name = trim($name);
1728: 
1729:                 // if a selector with somethink lige :hover => continue
1730:                 if (strpos($name, ':')!==false) continue;
1731: 
1732:                 // save the value
1733:                 if (!isset($this->css[$name]))
1734:                     $this->css[$name] = $css;
1735:                 else
1736:                     $this->css[$name] = array_merge($this->css[$name], $css);
1737: 
1738:             }
1739:         }
1740: 
1741:         // get he list of the keys
1742:         $this->cssKeys = array_flip(array_keys($this->css));
1743:     }
1744: 
1745:     /**
1746:      * Extract the css files from a html code
1747:      *
1748:      * @access public
1749:      * @param  string   &$html
1750:      */
1751:     public function readStyle(&$html)
1752:     {
1753:         // the CSS content
1754:         $style = ' ';
1755: 
1756:         // extract the link tags, and remove them in the html code
1757:         preg_match_all('/<link([^>]*)>/isU', $html, $match);
1758:         $html = preg_replace('/<link[^>]*>/isU', '', $html);
1759:         $html = preg_replace('/<\/link[^>]*>/isU', '', $html);
1760: 
1761:         // analyse each link tag
1762:         foreach ($match[1] as $code) {
1763:             $tmp = array();
1764: 
1765:             // read the attributes name=value
1766:             $prop = '([a-zA-Z0-9_]+)=([^"\'\s>]+)';
1767:             preg_match_all('/'.$prop.'/is', $code, $match);
1768:             for ($k=0; $k<count($match[0]); $k++) {
1769:                 $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
1770:             }
1771: 
1772:             // read the attributes name="value"
1773:             $prop = '([a-zA-Z0-9_]+)=["]([^"]*)["]';
1774:             preg_match_all('/'.$prop.'/is', $code, $match);
1775:             for ($k=0; $k<count($match[0]); $k++) {
1776:                 $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
1777:             }
1778: 
1779:             // read the attributes name='value'
1780:             $prop = "([a-zA-Z0-9_]+)=[']([^']*)[']";
1781:             preg_match_all('/'.$prop.'/is', $code, $match);
1782:             for ($k=0; $k<count($match[0]); $k++) {
1783:                 $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
1784:             }
1785: 
1786:             // if type text/css => we keep it
1787:             if (isset($tmp['type']) && strtolower($tmp['type'])=='text/css' && isset($tmp['href'])) {
1788: 
1789:                 // get the href
1790:                 $url = $tmp['href'];
1791: 
1792:                 // get the content of the css file
1793:                 $content = @file_get_contents($url);
1794: 
1795:                 // if "http://" in the url
1796:                 if (strpos($url, 'http://')!==false) {
1797: 
1798:                     // get the domain "http://xxx/"
1799:                     $url = str_replace('http://', '', $url);
1800:                     $url = explode('/', $url);
1801:                     $urlMain = 'http://'.$url[0].'/';
1802: 
1803:                     // get the absolute url of the path
1804:                     $urlSelf = $url; unset($urlSelf[count($urlSelf)-1]); $urlSelf = 'http://'.implode('/', $urlSelf).'/';
1805: 
1806:                     // adapt the url in the css content
1807:                     $content = preg_replace('/url\(([^\\\\][^)]*)\)/isU', 'url('.$urlSelf.'$1)', $content);
1808:                     $content = preg_replace('/url\((\\\\[^)]*)\)/isU', 'url('.$urlMain.'$1)', $content);
1809:                 } else {
1810: // @TODO correction on url in absolute on a local css content
1811:                     // $content = preg_replace('/url\(([^)]*)\)/isU', 'url('.dirname($url).'/$1)', $content);
1812:                 }
1813: 
1814:                 // add to the CSS content
1815:                 $style.= $content."\n";
1816:             }
1817:         }
1818: 
1819:         // extract the style tags des tags style, and remove them in the html code
1820:         preg_match_all('/<style[^>]*>(.*)<\/style[^>]*>/isU', $html, $match);
1821:         $html = preg_replace('/<style[^>]*>(.*)<\/style[^>]*>/isU', '', $html);
1822: 
1823:         // analyse each style tags
1824:         foreach ($match[1] as $code) {
1825:             // add to the CSS content
1826:             $code = str_replace('<!--', '', $code);
1827:             $code = str_replace('-->', '', $code);
1828:             $style.= $code."\n";
1829:         }
1830: 
1831:         //analyse the css content
1832:         $this->_analyseStyle($style);
1833:     }
1834: }