source: trunk/client/inc/hpdf403/_class/parsingCss.class.php@ 394

Last change on this file since 394 was 349, checked in by roby, 3 years ago
File size: 62.9 KB
Line 
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
12class 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}
Note: See TracBrowser for help on using the repository browser.