source: trunk/client/inc/hpdf5/spipu/html2pdf/src/Parsing/Css.php

Last change on this file was 347, checked in by roby, 4 years ago

Aggiornamento per compatibilità con php7.4

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