Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
35.41% |
205 / 579 |
|
63.83% |
30 / 47 |
CRAP | |
0.00% |
0 / 1 |
Parser | |
35.41% |
205 / 579 |
|
63.83% |
30 / 47 |
14991.43 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
parseString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
parseFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
parseUri | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
setDocument | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
parseHtml | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
parseHtmlFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
parseHtmlUri | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
parseCss | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
parseCssFile | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
parseCssUri | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getDocument | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
document | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setPageSize | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getPageSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setPageMargins | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
setPageTopMargin | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setPageRightMargin | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setPageBottomMargin | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setPageLeftMargin | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setDefaultStyle | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setX | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setY | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getPageMargins | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPageTopMargin | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPageRightMargin | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPageBottomMargin | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPageLeftMargin | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getX | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getY | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDefaultStyle | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getDefaultStyles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getCss | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getHtml | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
prepare | |
31.25% |
5 / 16 |
|
0.00% |
0 / 1 |
58.79 | |||
process | |
71.43% |
5 / 7 |
|
0.00% |
0 / 1 |
4.37 | |||
addNodeToDocument | |
17.83% |
28 / 157 |
|
0.00% |
0 / 1 |
1436.79 | |||
addNodeStreamToDocument | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
90 | |||
createDefaultStyles | |
98.11% |
52 / 53 |
|
0.00% |
0 / 1 |
9 | |||
prepareNodeStyles | |
22.96% |
45 / 196 |
|
0.00% |
0 / 1 |
3710.95 | |||
getCurrentX | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
resetX | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getCurrentY | |
66.67% |
8 / 12 |
|
0.00% |
0 / 1 |
5.93 | |||
resetY | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
newPage | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
goToNextLine | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getStringLines | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Pdf\Build\Html; |
15 | |
16 | use Pop\Css; |
17 | use Pop\Dom\Child; |
18 | use Pop\Pdf\Build\Compiler; |
19 | use Pop\Pdf\Document; |
20 | |
21 | /** |
22 | * Pdf HTML parser class |
23 | * |
24 | * @category Pop |
25 | * @package Pop\Pdf |
26 | * @author Nick Sagona, III <dev@nolainteractive.com> |
27 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
28 | * @license http://www.popphp.org/license New BSD License |
29 | * @version 4.2.0 |
30 | */ |
31 | class Parser |
32 | { |
33 | |
34 | /** |
35 | * PDF document |
36 | * @var Document |
37 | */ |
38 | protected $document = null; |
39 | |
40 | /** |
41 | * HTML object or array of HTML objects |
42 | * @var Child|array |
43 | */ |
44 | protected $html = null; |
45 | |
46 | /** |
47 | * CSS object |
48 | * @var Css\Css |
49 | */ |
50 | protected $css = null; |
51 | |
52 | /** |
53 | * Page size |
54 | * @var string |
55 | */ |
56 | protected $pageSize = 'LETTER'; |
57 | |
58 | /** |
59 | * Page margins |
60 | * @var array |
61 | */ |
62 | protected $pageMargins = [ |
63 | 'top' => 80, |
64 | 'right' => 60, |
65 | 'bottom' => 60, |
66 | 'left' => 60 |
67 | ]; |
68 | |
69 | /** |
70 | * Default styles |
71 | * @var array |
72 | */ |
73 | protected $defaultStyles = [ |
74 | 'font-family' => 'Arial', |
75 | 'font-size' => 10, |
76 | 'font-weight' => 'normal', |
77 | 'color' => [0, 0, 0], |
78 | 'line-height' => 14 |
79 | ]; |
80 | |
81 | /** |
82 | * Current x-position |
83 | * @var int |
84 | */ |
85 | protected $x = 0; |
86 | |
87 | /** |
88 | * Current y-position |
89 | * @var int |
90 | */ |
91 | protected $y = 0; |
92 | |
93 | /** |
94 | * Current page object |
95 | * @var Document\Page |
96 | */ |
97 | protected $page = null; |
98 | |
99 | /** |
100 | * HTML file directory |
101 | * @var string |
102 | */ |
103 | protected $fileDir = null; |
104 | |
105 | /** |
106 | * Text wrap object |
107 | * @var Document\Page\Text\Wrap |
108 | */ |
109 | protected $textWrap = null; |
110 | |
111 | /** |
112 | * Y-override |
113 | * @var int |
114 | */ |
115 | protected $yOverride = null; |
116 | |
117 | /** |
118 | * Constructor |
119 | * |
120 | * Instantiate the HTML parser object |
121 | * |
122 | * @param Document $document |
123 | */ |
124 | public function __construct(Document $document = null) |
125 | { |
126 | if (null !== $document) { |
127 | $this->setDocument($document); |
128 | } |
129 | $this->createDefaultStyles(); |
130 | } |
131 | |
132 | /** |
133 | * Parse HTML string |
134 | * |
135 | * @param string $htmlString |
136 | * @param Document $document |
137 | * @return self |
138 | */ |
139 | public static function parseString($htmlString, Document $document = null) |
140 | { |
141 | $html = new self($document); |
142 | $html->parseHtml($htmlString); |
143 | |
144 | return $html; |
145 | } |
146 | |
147 | /** |
148 | * Parse $html from file |
149 | * |
150 | * @param string $htmlFile |
151 | * @param Document $document |
152 | * @return self |
153 | */ |
154 | public static function parseFile($htmlFile, Document $document = null) |
155 | { |
156 | $html = new self($document); |
157 | $html->parseHtmlFile($htmlFile); |
158 | |
159 | return $html; |
160 | } |
161 | |
162 | /** |
163 | * Parse $html from URI |
164 | * |
165 | * @param string $htmlUri |
166 | * @param Document $document |
167 | * @return self |
168 | */ |
169 | public static function parseUri($htmlUri, Document $document = null) |
170 | { |
171 | $html = new self($document); |
172 | $html->parseHtmlUri($htmlUri); |
173 | |
174 | return $html; |
175 | } |
176 | |
177 | /** |
178 | * Set document |
179 | * |
180 | * @param Document $document |
181 | * @return self |
182 | */ |
183 | public function setDocument(Document $document) |
184 | { |
185 | $this->document = $document; |
186 | return $this; |
187 | } |
188 | |
189 | /** |
190 | * Parse HTML string |
191 | * |
192 | * @param string $htmlString |
193 | * @param string $basePath |
194 | * @return self |
195 | */ |
196 | public function parseHtml($htmlString, $basePath = null) |
197 | { |
198 | if (null !== $basePath) { |
199 | $this->fileDir = $basePath; |
200 | } |
201 | $this->html = Child::parseString($htmlString); |
202 | return $this; |
203 | } |
204 | |
205 | /** |
206 | * Parse HTML string from file |
207 | * |
208 | * @param string $htmlFile |
209 | * @throws Exception |
210 | * @return self |
211 | */ |
212 | public function parseHtmlFile($htmlFile) |
213 | { |
214 | if (!file_exists($htmlFile)) { |
215 | throw new Exception('Error: That file does not exist.'); |
216 | } |
217 | return $this->parseHtml(file_get_contents($htmlFile), dirname(realpath($htmlFile))); |
218 | } |
219 | |
220 | /** |
221 | * Parse HTML string from URI |
222 | * |
223 | * @param string $htmlUri |
224 | * @throws Exception |
225 | * @return self |
226 | */ |
227 | public function parseHtmlUri($htmlUri) |
228 | { |
229 | return $this->parseHtml(file_get_contents($htmlUri)); |
230 | } |
231 | |
232 | /** |
233 | * Parse CSS string |
234 | * |
235 | * @param string $cssString |
236 | * @return self |
237 | */ |
238 | public function parseCss($cssString) |
239 | { |
240 | if (null === $this->css) { |
241 | $this->css = Css\Css::parseString($cssString); |
242 | } else { |
243 | $this->css->parseCss($cssString); |
244 | } |
245 | return $this; |
246 | } |
247 | |
248 | /** |
249 | * Parse CSS file |
250 | * |
251 | * @param string $cssFile |
252 | * @return self |
253 | */ |
254 | public function parseCssFile($cssFile) |
255 | { |
256 | if (null === $this->css) { |
257 | $this->css = Css\Css::parseFile($cssFile); |
258 | } else { |
259 | $this->css->parseCssFile($cssFile); |
260 | } |
261 | return $this; |
262 | } |
263 | |
264 | /** |
265 | * Parse CSS URI |
266 | * |
267 | * @param string $cssUri |
268 | * @return self |
269 | */ |
270 | public function parseCssUri($cssUri) |
271 | { |
272 | if (null === $this->css) { |
273 | $this->css = Css\Css::parseUri($cssUri); |
274 | } else { |
275 | $this->css->parseCssUri($cssUri); |
276 | } |
277 | return $this; |
278 | } |
279 | |
280 | /** |
281 | * Get document |
282 | * |
283 | * @return Document |
284 | */ |
285 | public function getDocument() |
286 | { |
287 | return $this->document; |
288 | } |
289 | |
290 | /** |
291 | * Get document (alias) |
292 | * |
293 | * @return Document |
294 | */ |
295 | public function document() |
296 | { |
297 | return $this->document; |
298 | } |
299 | |
300 | /** |
301 | * Set page size |
302 | * |
303 | * @param mixed $size |
304 | * @param mixed $height |
305 | * @return self |
306 | */ |
307 | public function setPageSize($size, $height = null) |
308 | { |
309 | $this->pageSize = (null !== $height) ? ['width' => $size, 'height' => $height] : $size; |
310 | |
311 | return $this; |
312 | } |
313 | |
314 | /** |
315 | * Get page size |
316 | * |
317 | * @return string|array |
318 | */ |
319 | public function getPageSize() |
320 | { |
321 | return $this->pageSize; |
322 | } |
323 | |
324 | /** |
325 | * Set page margins |
326 | * |
327 | * @param int $top |
328 | * @param int $right |
329 | * @param int $bottom |
330 | * @param int $left |
331 | * @return self |
332 | */ |
333 | public function setPageMargins($top, $right, $bottom, $left) |
334 | { |
335 | $this->pageMargins['top'] = $top; |
336 | $this->pageMargins['right'] = $right; |
337 | $this->pageMargins['bottom'] = $bottom; |
338 | $this->pageMargins['left'] = $left; |
339 | return $this; |
340 | } |
341 | |
342 | /** |
343 | * Set page top margin |
344 | * |
345 | * @param int $margin |
346 | * @return self |
347 | */ |
348 | public function setPageTopMargin($margin) |
349 | { |
350 | $this->pageMargins['top'] = $margin; |
351 | return $this; |
352 | } |
353 | |
354 | /** |
355 | * Set page right margin |
356 | * |
357 | * @param int $margin |
358 | * @return self |
359 | */ |
360 | public function setPageRightMargin($margin) |
361 | { |
362 | $this->pageMargins['right'] = $margin; |
363 | return $this; |
364 | } |
365 | |
366 | /** |
367 | * Set page bottom margin |
368 | * |
369 | * @param int $margin |
370 | * @return self |
371 | */ |
372 | public function setPageBottomMargin($margin) |
373 | { |
374 | $this->pageMargins['bottom'] = $margin; |
375 | return $this; |
376 | } |
377 | |
378 | /** |
379 | * Set page left margin |
380 | * |
381 | * @param int $margin |
382 | * @return self |
383 | */ |
384 | public function setPageLeftMargin($margin) |
385 | { |
386 | $this->pageMargins['left'] = $margin; |
387 | return $this; |
388 | } |
389 | |
390 | /** |
391 | * Set a default style |
392 | * |
393 | * @param string $property |
394 | * @param string $value |
395 | * @return self |
396 | */ |
397 | public function setDefaultStyle($property, $value) |
398 | { |
399 | $this->defaultStyles[$property] = $value; |
400 | return $this; |
401 | } |
402 | |
403 | /** |
404 | * Set x-position |
405 | * |
406 | * @param int $x |
407 | * @return self |
408 | */ |
409 | public function setX($x) |
410 | { |
411 | $this->x = $x; |
412 | return $this; |
413 | } |
414 | |
415 | /** |
416 | * Set y-position |
417 | * |
418 | * @param int $y |
419 | * @return self |
420 | */ |
421 | public function setY($y) |
422 | { |
423 | $this->y = $y; |
424 | return $this; |
425 | } |
426 | |
427 | /** |
428 | * Get page margins |
429 | * |
430 | * @return array |
431 | */ |
432 | public function getPageMargins() |
433 | { |
434 | return $this->pageMargins; |
435 | } |
436 | |
437 | /** |
438 | * Get page top margin |
439 | * |
440 | * @return int |
441 | */ |
442 | public function getPageTopMargin() |
443 | { |
444 | return $this->pageMargins['top']; |
445 | } |
446 | |
447 | /** |
448 | * Get page right margin |
449 | * |
450 | * @return int |
451 | */ |
452 | public function getPageRightMargin() |
453 | { |
454 | return $this->pageMargins['right']; |
455 | } |
456 | |
457 | /** |
458 | * Get page bottom margin |
459 | * |
460 | * @return int |
461 | */ |
462 | public function getPageBottomMargin() |
463 | { |
464 | return $this->pageMargins['bottom']; |
465 | } |
466 | |
467 | /** |
468 | * Get page left margin |
469 | * |
470 | * @return int |
471 | */ |
472 | public function getPageLeftMargin() |
473 | { |
474 | return $this->pageMargins['left']; |
475 | } |
476 | |
477 | /** |
478 | * Get x-position |
479 | * |
480 | * @return int |
481 | */ |
482 | public function getX() |
483 | { |
484 | return $this->x; |
485 | } |
486 | |
487 | /** |
488 | * Get y-position |
489 | * |
490 | * @return int |
491 | */ |
492 | public function getY() |
493 | { |
494 | return $this->y; |
495 | } |
496 | |
497 | /** |
498 | * Get a default style |
499 | * |
500 | * @param string $property |
501 | * @return string |
502 | */ |
503 | public function getDefaultStyle($property) |
504 | { |
505 | return (isset($this->defaultStyles[$property])) ? $this->defaultStyles[$property] : null; |
506 | } |
507 | |
508 | /** |
509 | * Get default styles |
510 | * |
511 | * @return array |
512 | */ |
513 | public function getDefaultStyles() |
514 | { |
515 | return $this->defaultStyles; |
516 | } |
517 | |
518 | /** |
519 | * Get styles |
520 | * |
521 | * @return Css\Css |
522 | */ |
523 | public function getCss() |
524 | { |
525 | return $this->css; |
526 | } |
527 | |
528 | /** |
529 | * Get HTML nodes |
530 | * |
531 | * @return Child|array |
532 | */ |
533 | public function getHtml() |
534 | { |
535 | return $this->html; |
536 | } |
537 | |
538 | /** |
539 | * Prepare for conversion of HTML into PDF objects |
540 | * |
541 | * @return array|Child |
542 | */ |
543 | public function prepare() |
544 | { |
545 | $htmlNodes = null; |
546 | if ($this->html instanceof Child) { |
547 | foreach ($this->html->getChildNodes() as $child) { |
548 | if ($child->getNodeName() == 'head') { |
549 | foreach ($child->getChildNodes() as $c) { |
550 | if (($c->getNodeName() == 'link') && ($c->hasAttribute('href')) && |
551 | ($c->hasAttribute('type')) && ($c->getAttribute('type') == 'text/css')) { |
552 | $href = $c->getAttribute('href'); |
553 | if (null === $this->css) { |
554 | $this->css = Css\Css::parseFile($this->fileDir . '/' . $href); |
555 | } else { |
556 | $this->css->parseCssFile($this->fileDir . '/' . $href); |
557 | } |
558 | } |
559 | } |
560 | } else if ($child->getNodeName() == 'body') { |
561 | $htmlNodes = $child; |
562 | } |
563 | } |
564 | } |
565 | |
566 | if (null === $htmlNodes) { |
567 | $htmlNodes = $this->html; |
568 | } |
569 | |
570 | return $htmlNodes; |
571 | } |
572 | |
573 | /** |
574 | * Process conversion of HTML into PDF objects |
575 | * |
576 | * @return Document |
577 | */ |
578 | public function process() |
579 | { |
580 | $htmlNodes = $this->prepare(); |
581 | |
582 | if ($htmlNodes instanceof Child) { |
583 | foreach ($htmlNodes->getChildNodes() as $child) { |
584 | $this->addNodeToDocument($child); |
585 | } |
586 | } else { |
587 | foreach ($htmlNodes as $child) { |
588 | $this->addNodeToDocument($child); |
589 | } |
590 | } |
591 | |
592 | return $this->document; |
593 | } |
594 | |
595 | /** |
596 | * Add node to document |
597 | * |
598 | * @param Child $child |
599 | * @throws Exception |
600 | * @return void |
601 | */ |
602 | protected function addNodeToDocument(Child $child) |
603 | { |
604 | $styles = $this->prepareNodeStyles($child->getNodeName(), $child->getAttributes()); |
605 | $currentX = $this->getCurrentX(); |
606 | |
607 | if (null !== $this->yOverride) { |
608 | $currentY = $this->yOverride; |
609 | $this->yOverride = null; |
610 | } else { |
611 | $currentY = $this->getCurrentY(); |
612 | } |
613 | |
614 | $wrapLength = ($this->x > $this->pageMargins['left']) ? |
615 | $this->page->getWidth() - $this->pageMargins['right'] - $this->x : |
616 | $this->page->getWidth() - $this->pageMargins['right'] - $this->pageMargins['left']; |
617 | |
618 | // Image node |
619 | if ($child->getNodeName() == 'img') { |
620 | $image = Document\Page\Image::createImageFromFile($this->fileDir . '/' . $child->getAttribute('src')); |
621 | $width = null; |
622 | $height = null; |
623 | $align = null; |
624 | |
625 | if ($child->hasAttribute('width')) { |
626 | $width = (strpos($child->getAttribute('width'), '%')) ? |
627 | $this->page->getWidth() * ((int)$child->getAttribute('width') / 100) : (int)$child->getAttribute('width'); |
628 | } else if ($child->hasAttribute('height')) { |
629 | $height = (strpos($child->getAttribute('height'), '%')) ? |
630 | $this->page->getHeight() * ((int)$child->getAttribute('height') / 100) : (int)$child->getAttribute('height'); |
631 | } else if (null !== $styles['width']) { |
632 | $width = (strpos($styles['width'], '%')) ? |
633 | $this->page->getWidth() * ((int)$styles['width'] / 100) : (int)$styles['width']; |
634 | } else if (null !== $styles['height']) { |
635 | $height = (strpos($styles['height'], '%')) ? |
636 | $this->page->getHeight() * ((int)$styles['height'] / 100) : (int)$styles['height']; |
637 | } |
638 | |
639 | if (null !== $width) { |
640 | $image->resizeToWidth($width); |
641 | } else if (null !== $height) { |
642 | $image->resizeToHeight($height); |
643 | } |
644 | |
645 | if (null === $height) { |
646 | $height = (null !== $image->getResizedHeight()) ? $image->getResizedHeight() : $image->getHeight(); |
647 | } |
648 | |
649 | if ($child->hasAttribute('align')) { |
650 | $align = strtoupper($child->getAttribute('align')); |
651 | } else if (isset($styles['float'])) { |
652 | $align = strtoupper($styles['float']); |
653 | } |
654 | |
655 | if ($align == 'LEFT') { |
656 | $box = [ |
657 | 'left' => $currentX, |
658 | 'right' => $currentX + $width + ($styles['marginRight'] ?? 0), |
659 | 'top' => $currentY, |
660 | 'bottom' => $currentY - $height - ($styles['marginBottom'] ?? 0) |
661 | ]; |
662 | $this->textWrap = new Document\Page\Text\Wrap('RIGHT', $this->pageMargins['left'], $this->page->getWidth() - $this->pageMargins['right'], $box); |
663 | } else if ($align == 'RIGHT') { |
664 | $box = [ |
665 | 'left' => $this->page->getWidth() - $this->pageMargins['right'] - $width - ($styles['marginLeft'] ?? 0), |
666 | 'right' => $this->page->getWidth() - $this->pageMargins['right'], |
667 | 'top' => $currentY, |
668 | 'bottom' => $currentY - $height - ($styles['marginBottom'] ?? 0) |
669 | ]; |
670 | $this->textWrap = new Document\Page\Text\Wrap('LEFT', $this->pageMargins['left'], $this->page->getWidth() - $this->pageMargins['right'], $box); |
671 | } |
672 | |
673 | if (null !== $this->textWrap) { |
674 | $newY = $currentY - ((null !== $image->getResizedHeight()) ? $image->getResizedHeight() : $image->getHeight()); |
675 | if ($align == 'RIGHT') { |
676 | $this->page->addImage($image, ($this->page->getWidth() - $this->pageMargins['right'] - $width), $newY); |
677 | } else { |
678 | $this->page->addImage($image, $currentX, $newY); |
679 | } |
680 | $currentY -= $styles['lineHeight']; |
681 | $this->y += $styles['lineHeight']; |
682 | } else { |
683 | $currentY -= (null !== $image->getResizedHeight()) ? $image->getResizedHeight() : $image->getHeight(); |
684 | $this->y += (null !== $image->getResizedHeight()) ? $image->getResizedHeight() : $image->getHeight(); |
685 | $this->page->addImage($image, $currentX, $currentY); |
686 | $currentY -= $styles['lineHeight']; |
687 | $this->y += $styles['lineHeight']; |
688 | } |
689 | // Table node |
690 | } else if ($child->getNodeName() == 'table') { |
691 | $tableWidth = $this->page->getWidth() - $this->pageMargins['left'] - $this->pageMargins['right']; |
692 | $columnCount = count($child->getChild(0)->getChildNodes());; |
693 | $rowCount = 0; |
694 | $columnWidth = floor($tableWidth / $columnCount); |
695 | $fontObject = $this->document->getFont($styles['currentFont']); |
696 | $currentRow = 0; |
697 | $currentX = $this->pageMargins['left'] + 10; |
698 | $thHeight = 0; |
699 | $offset = 0; |
700 | $startY = $currentY; |
701 | |
702 | foreach ($child->getChildNodes() as $childNode) { |
703 | if (($childNode->getNodeName() == 'tr') && ($childNode->hasChildNodes())) { |
704 | foreach ($childNode->getChildNodes() as $grandChild) { |
705 | if ($grandChild->getNodeName() == 'th') { |
706 | $thString = $grandChild->getNodeValue(); |
707 | |
708 | $thStringWidth = $fontObject->getStringWidth($thString, $styles['fontSize']); |
709 | if ($thStringWidth > ($columnWidth - 20)) { |
710 | $strings = $this->getStringLines($thString, $styles['fontSize'], $columnWidth - 20, $fontObject); |
711 | foreach ($strings as $i => $string) { |
712 | $text = new Document\Page\Text($string, $styles['fontSize']); |
713 | $this->page->addText($text, $styles['currentFont'], $currentX, ($currentY - ($styles['fontSize'] * $i))); |
714 | } |
715 | $newThHeight = (count($strings) * $styles['fontSize']) + 20; |
716 | if ($newThHeight > $thHeight) { |
717 | $thHeight = $newThHeight; |
718 | $offset = $thHeight - 30; |
719 | } |
720 | } else { |
721 | $text = new Document\Page\Text($thString, $styles['fontSize']); |
722 | $this->page->addText($text, $styles['currentFont'], $currentX, $currentY); |
723 | } |
724 | $currentX += $columnWidth; |
725 | } |
726 | } |
727 | } |
728 | } |
729 | |
730 | foreach ($child->getChildNodes() as $childNode) { |
731 | if (($childNode->getNodeName() == 'tr') && ($childNode->hasChildNodes())) { |
732 | $rowCount++; |
733 | $currentX = $this->pageMargins['left'] + 10; |
734 | foreach ($childNode->getChildNodes() as $grandChild) { |
735 | if ($grandChild->getNodeName() == 'td') { |
736 | $text = new Document\Page\Text($grandChild->getNodeValue(), $styles['fontSize']); |
737 | $this->page->addText($text, $styles['currentFont'], $currentX, $currentY - $offset); |
738 | $currentX += $columnWidth; |
739 | } |
740 | } |
741 | $currentY -= 25; |
742 | } |
743 | } |
744 | |
745 | $currentY += 15; |
746 | $x1 = $this->pageMargins['left']; |
747 | /* |
748 | |
749 | $finalHeight = (25 * $rowCount); |
750 | $path = new Document\Page\Path(); |
751 | $path->drawRectangle($x1, $currentY, $tableWidth, $finalHeight); |
752 | $this->page->addPath($path); |
753 | |
754 | for ($i = 1; $i < $columnCount; $i++) { |
755 | $path = new Document\Page\Path(); |
756 | $path->drawLine($x1 + ($i * $columnWidth), $currentY, $x1 + ($i * $columnWidth), $this->page->getHeight() - $finalHeight + 35); |
757 | $this->page->addPath($path); |
758 | } |
759 | */ |
760 | |
761 | for ($i = 0; $i < $rowCount; $i++) { |
762 | if (($i == 0) && ($thHeight > 0)) { |
763 | $lineY = $startY - $thHeight + 20; |
764 | } else { |
765 | $lineY = ($startY - ($i * 25) - 10) - $offset; |
766 | } |
767 | $path = new Document\Page\Path(); |
768 | $path->drawLine($x1, $lineY, $tableWidth + $this->pageMargins['left'], $lineY); |
769 | $this->page->addPath($path); |
770 | /* |
771 | if (($i == 1) && ($thHeight > 0)) { |
772 | $lineY = ($currentY + ($i * 25)); |
773 | } else { |
774 | $lineY = ($currentY + ($i * 25)); |
775 | } |
776 | $path = new Document\Page\Path(); |
777 | $path->drawLine($x1, $lineY, $tableWidth + $this->pageMargins['left'], $lineY); |
778 | $this->page->addPath($path); |
779 | */ |
780 | } |
781 | |
782 | |
783 | // Text node |
784 | } else { |
785 | if (null !== $this->textWrap) { |
786 | $box = $this->textWrap->getBox(); |
787 | if ($this->textWrap->isRight()) { |
788 | $startX = $box['right']; |
789 | $startY = $box['top'] - $styles['fontSize']; |
790 | $edgeX = $wrapLength; |
791 | $edgeY = $box['bottom']; |
792 | } else { |
793 | $startX = $currentX; |
794 | $startY = $box['top'] - $styles['fontSize']; |
795 | $edgeX = $box['left'] - 40; |
796 | $edgeY = $box['bottom']; |
797 | } |
798 | } else { |
799 | $startX = $currentX; |
800 | $startY = $currentY; |
801 | $edgeX = $wrapLength; |
802 | $edgeY = $this->pageMargins['bottom']; |
803 | } |
804 | |
805 | $textStream = new Document\Page\Text\Stream($startX, $startY, $edgeX, $edgeY); |
806 | $textStream->setCurrentStyle( |
807 | $styles['currentFont'], |
808 | $styles['fontSize'], |
809 | new Document\Page\Color\Rgb($styles['color'][0], $styles['color'][1], $styles['color'][2]) |
810 | ); |
811 | $streamY = $styles['lineHeight'] ?? null; |
812 | $textStream->addText($child->getNodeValue(), $streamY); |
813 | |
814 | if ($child->hasChildNodes()) { |
815 | foreach ($child->getChildNodes() as $grandChild) { |
816 | $gcStyles = $this->prepareNodeStyles($grandChild->getNodeName(), $grandChild->getAttributes(), $styles); |
817 | $textStream->setCurrentStyle( |
818 | $gcStyles['currentFont'], |
819 | $gcStyles['fontSize'], |
820 | new Document\Page\Color\Rgb($gcStyles['color'][0], $gcStyles['color'][1], $gcStyles['color'][2]) |
821 | ); |
822 | $streamY = $gcStyles['lineHeight'] ?? null; |
823 | $textStream->addText($grandChild->getNodeValue(), $streamY); |
824 | } |
825 | } |
826 | |
827 | $this->page->addTextStream($textStream); |
828 | |
829 | $orphanStream = clone $textStream; |
830 | $hasOrphans = false; |
831 | |
832 | while ($orphanStream->hasOrphans($this->document->getFonts())) { |
833 | $orphanStream = $orphanStream->getOrphanStream(); |
834 | if ($orphanStream->getCurrentY() <= $this->pageMargins['bottom']) { |
835 | $currentY = $this->newPage(); |
836 | $orphanStream->setCurrentY($currentY); |
837 | } else { |
838 | $orphanStream->setStartX($this->pageMargins['left']); |
839 | $orphanStream->setEdgeX($wrapLength); |
840 | $orphanStream->setEdgeY($this->pageMargins['bottom']); |
841 | } |
842 | |
843 | $orphanStream->setCurrentX($currentX); |
844 | $this->page->addTextStream($orphanStream); |
845 | |
846 | $orphanStream = clone $orphanStream; |
847 | $hasOrphans = true; |
848 | } |
849 | |
850 | if ($hasOrphans) { |
851 | $this->yOverride = $orphanStream->getCurrentY(); |
852 | } else { |
853 | $this->yOverride = null; |
854 | $this->y += (!empty($styles['marginBottom'])) ? $styles['marginBottom'] : 25; |
855 | } |
856 | } |
857 | } |
858 | |
859 | /** |
860 | * Add node stream to document |
861 | * |
862 | * @param Child $child |
863 | * @throws Exception |
864 | * @return void |
865 | */ |
866 | protected function addNodeStreamToDocument(Child $child) |
867 | { |
868 | $styles = $this->prepareNodeStyles($child->getNodeName(), $child->getAttributes()); |
869 | $currentX = $this->getCurrentX(); |
870 | $currentY = $this->getCurrentY(); |
871 | $fontObject = $this->document->getFont($styles['currentFont']); |
872 | $wrapLength = ($this->x > $this->pageMargins['left']) ? |
873 | $this->page->getWidth() - $this->pageMargins['right'] - $this->x : |
874 | $this->page->getWidth() - $this->pageMargins['right'] - $this->pageMargins['left']; |
875 | |
876 | $string = $child->getNodeValue(); |
877 | $stringWidth = $fontObject->getStringWidth($string, $styles['fontSize']); |
878 | |
879 | if ($stringWidth > $wrapLength) { |
880 | $strings = $this->getStringLines($string, $styles['fontSize'], $wrapLength, $fontObject); |
881 | if ($this->x > $this->pageMargins['left']) { |
882 | $text = new Document\Page\Text($strings[0], $styles['fontSize']); |
883 | $text->setFillColor(new Document\Page\Color\Rgb($styles['color'][0], $styles['color'][1], $styles['color'][2])); |
884 | $this->page->addText($text, $styles['currentFont'], $currentX, $currentY); |
885 | if ($currentY <= $this->pageMargins['bottom']) { |
886 | $currentY = $this->newPage(); |
887 | } else { |
888 | $currentY -= $styles['lineHeight']; |
889 | $this->y += $styles['lineHeight']; |
890 | } |
891 | $currentX = $this->resetX(); |
892 | $wrapLength = $this->page->getWidth() - $this->pageMargins['right'] - $this->pageMargins['left']; |
893 | unset($strings[0]); |
894 | $strings = $this->getStringLines(implode(' ', $strings), $styles['fontSize'], $wrapLength, $fontObject); |
895 | } |
896 | |
897 | foreach ($strings as $i => $string) { |
898 | $text = new Document\Page\Text($string, $styles['fontSize']); |
899 | $text->setFillColor(new Document\Page\Color\Rgb($styles['color'][0], $styles['color'][1], $styles['color'][2])); |
900 | $this->page->addText($text, $styles['currentFont'], $currentX, $currentY); |
901 | if ($i < (count($strings) - 1)) { |
902 | if ($currentY <= $this->pageMargins['bottom']) { |
903 | $currentY = $this->newPage(); |
904 | } else { |
905 | $currentY -= $styles['lineHeight']; |
906 | $this->y += $styles['lineHeight']; |
907 | } |
908 | } |
909 | } |
910 | $this->x += $fontObject->getStringWidth($string, $styles['fontSize']); |
911 | } else { |
912 | $text = new Document\Page\Text($string, $styles['fontSize']); |
913 | $text->setFillColor(new Document\Page\Color\Rgb($styles['color'][0], $styles['color'][1], $styles['color'][2])); |
914 | $this->page->addText($text, $styles['currentFont'], $currentX, $currentY); |
915 | $this->x += $fontObject->getStringWidth($string, $styles['fontSize']); |
916 | } |
917 | |
918 | foreach ($child->getChildNodes() as $grandChild) { |
919 | $this->addNodeStreamToDocument($grandChild); |
920 | } |
921 | } |
922 | |
923 | /** |
924 | * Create default styles |
925 | * |
926 | * @return void |
927 | */ |
928 | protected function createDefaultStyles() |
929 | { |
930 | $h1 = new Css\Selector('h1'); |
931 | $h1['margin-bottom'] = '18px'; |
932 | $h1['font-size'] = '32px'; |
933 | $h1['font-weight'] = 'bold'; |
934 | |
935 | $h2 = new Css\Selector('h2'); |
936 | $h2['margin-bottom'] = '18px'; |
937 | $h2['font-size'] = '28px'; |
938 | $h2['font-weight'] = 'bold'; |
939 | |
940 | $h3 = new Css\Selector('h3'); |
941 | $h3['margin-bottom'] = '16px'; |
942 | $h3['font-size'] = '24px'; |
943 | $h3['font-weight'] = 'bold'; |
944 | |
945 | $h4 = new Css\Selector('h4'); |
946 | $h4['margin-bottom'] = '14px'; |
947 | $h4['font-size'] = '20px'; |
948 | $h4['font-weight'] = 'bold'; |
949 | |
950 | $h5 = new Css\Selector('h5'); |
951 | $h5['margin-bottom'] = '12px'; |
952 | $h5['font-size'] = '16px'; |
953 | $h5['font-weight'] = 'bold'; |
954 | |
955 | $h6 = new Css\Selector('h6'); |
956 | $h6['margin-bottom'] = '10px'; |
957 | $h6['font-size'] = '14px'; |
958 | $h6['font-weight'] = 'bold'; |
959 | |
960 | $p = new Css\Selector('p'); |
961 | $p['margin-bottom'] = '24px'; |
962 | $p['font-size'] = '12px'; |
963 | |
964 | $a = new Css\Selector('a'); |
965 | $a['color'] = new Css\Color\Rgb(0, 0, 255); |
966 | |
967 | $strong = new Css\Selector('strong'); |
968 | $strong['font-size'] = '10px'; |
969 | $strong['font-weight'] = 'bold'; |
970 | |
971 | $em = new Css\Selector('em'); |
972 | $em['font-size'] = '10px'; |
973 | $em['font-style'] = 'italic'; |
974 | |
975 | $this->css = new Css\Css(); |
976 | $this->css->addSelectors([$h1, $h2, $h3, $h4, $h5, $h6, $p, $a, $strong, $em]); |
977 | |
978 | if (!($this->document->hasFont('Arial'))) { |
979 | $this->document->addFont(new Document\Font('Arial')); |
980 | } |
981 | if (!($this->document->hasFont('Arial,Bold'))) { |
982 | $this->document->addFont(new Document\Font('Arial,Bold')); |
983 | } |
984 | if (!($this->document->hasFont('Arial,Italic'))) { |
985 | $this->document->addFont(new Document\Font('Arial,Italic')); |
986 | } |
987 | if (!($this->document->hasFont('Arial,BoldItalic'))) { |
988 | $this->document->addFont(new Document\Font('Arial,BoldItalic')); |
989 | } |
990 | if (!($this->document->hasFont('TimesNewRoman'))) { |
991 | $this->document->addFont(new Document\Font('TimesNewRoman')); |
992 | } |
993 | if (!($this->document->hasFont('TimesNewRoman,Bold'))) { |
994 | $this->document->addFont(new Document\Font('TimesNewRoman,Bold')); |
995 | } |
996 | if (!($this->document->hasFont('TimesNewRoman,Italic'))) { |
997 | $this->document->addFont(new Document\Font('TimesNewRoman,Italic')); |
998 | } |
999 | if (!($this->document->hasFont('TimesNewRoman,BoldItalic'))) { |
1000 | $this->document->addFont(new Document\Font('TimesNewRoman,BoldItalic')); |
1001 | } |
1002 | } |
1003 | |
1004 | /** |
1005 | * Prepare node styles |
1006 | * |
1007 | * @param string $name |
1008 | * @param array $attribs |
1009 | * @param array $currentStyles |
1010 | * @throws Exception |
1011 | * @return array |
1012 | */ |
1013 | protected function prepareNodeStyles($name, array $attribs = [], $currentStyles = []) |
1014 | { |
1015 | $styles = [ |
1016 | 'currentFont' => null, |
1017 | 'fontFamily' => $currentStyles['fontFamily'] ?? $this->defaultStyles['font-family'], |
1018 | 'fontSize' => $currentStyles['fontSize'] ?? $this->defaultStyles['font-size'], |
1019 | 'fontWeight' => $currentStyles['fontWeight'] ?? $this->defaultStyles['font-weight'], |
1020 | 'float' => null, |
1021 | 'width' => null, |
1022 | 'height' => null, |
1023 | 'color' => $currentStyles['color'] ?? $this->defaultStyles['color'], |
1024 | 'lineHeight' => $currentStyles['lineHeight'] ?? $this->defaultStyles['line-height'], |
1025 | 'marginTop' => 0, |
1026 | 'paddingTop' => 0, |
1027 | 'marginRight' => 0, |
1028 | 'paddingRight' => 0, |
1029 | 'marginBottom' => 0, |
1030 | 'paddingBottom' => 0, |
1031 | 'marginLeft' => 0, |
1032 | 'paddingLeft' => 0 |
1033 | ]; |
1034 | |
1035 | if (in_array($name, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])) { |
1036 | switch ($name) { |
1037 | case 'h1': |
1038 | $styles['fontSize'] = round($styles['fontSize'] * 2.67); // 32 |
1039 | $styles['fontWeight'] = 'bold'; |
1040 | break; |
1041 | case 'h2': |
1042 | $styles['fontSize'] = round($styles['fontSize'] * 2.33); // 28 |
1043 | $styles['fontWeight'] = 'bold'; |
1044 | break; |
1045 | case 'h3': |
1046 | $styles['fontSize'] = $styles['fontSize'] * 2; // 24 |
1047 | $styles['fontWeight'] = 'bold'; |
1048 | break; |
1049 | case 'h4': |
1050 | $styles['fontSize'] = round($styles['fontSize'] * 1.67); // 20 |
1051 | $styles['fontWeight'] = 'bold'; |
1052 | break; |
1053 | case 'h5': |
1054 | $styles['fontSize'] = round($styles['fontSize'] * 1.33); // 16 |
1055 | $styles['fontWeight'] = 'bold'; |
1056 | break; |
1057 | case 'h6': |
1058 | $styles['fontWeight'] = 'bold'; |
1059 | break; |
1060 | } |
1061 | } |
1062 | |
1063 | if ($this->css->hasSelector($name)) { |
1064 | if ($this->css[$name]->hasProperty('font-family')) { |
1065 | $styles['fontFamily'] = str_replace('"', '', $this->css[$name]['font-family']); |
1066 | } |
1067 | if ($this->css[$name]->hasProperty('font-size')) { |
1068 | //$styles['fontSize'] = (int)$this->css[$name]['font-size']; |
1069 | } |
1070 | if ($this->css[$name]->hasProperty('font-weight')) { |
1071 | $styles['fontWeight'] = $this->css[$name]['font-weight']; |
1072 | } |
1073 | if ($this->css[$name]->hasProperty('color')) { |
1074 | $styles['color'] = $this->css[$name]['color']; |
1075 | if (is_string($styles['color'])) { |
1076 | $cssColor = Css\Color::parse($styles['color']); |
1077 | $styles['color'] = $cssColor->toRgb()->toArray(false); |
1078 | } |
1079 | } |
1080 | if ($this->css[$name]->hasProperty('float')) { |
1081 | $styles['float'] = $this->css[$name]['float']; |
1082 | } |
1083 | if ($this->css[$name]->hasProperty('width')) { |
1084 | $styles['width'] = $this->css[$name]['width']; |
1085 | } |
1086 | if ($this->css[$name]->hasProperty('height')) { |
1087 | $styles['height'] = $this->css[$name]['height']; |
1088 | } |
1089 | if ($this->css[$name]->hasProperty('line-height')) { |
1090 | $styles['lineHeight'] = (int)$this->css[$name]['line-height']; |
1091 | } |
1092 | if ((int)$this->css[$name]['margin-top'] > 0) { |
1093 | $styles['marginTop'] = (int)$this->css[$name]['margin-top']; |
1094 | } |
1095 | if ((int)$this->css[$name]['padding-top'] > 0) { |
1096 | $styles['paddingTop'] = (int)$this->css[$name]['padding-top']; |
1097 | } |
1098 | if ((int)$this->css[$name]['margin-right'] > 0) { |
1099 | $styles['marginRight'] = (int)$this->css[$name]['margin-right']; |
1100 | } |
1101 | if ((int)$this->css[$name]['padding-right'] > 0) { |
1102 | $styles['paddingRight'] = (int)$this->css[$name]['padding-right']; |
1103 | } |
1104 | if ((int)$this->css[$name]['margin-bottom'] > 0) { |
1105 | $styles['marginBottom'] = (int)$this->css[$name]['margin-bottom']; |
1106 | } |
1107 | if ((int)$this->css[$name]['padding-bottom'] > 0) { |
1108 | $styles['paddingBottom'] = (int)$this->css[$name]['padding-bottom']; |
1109 | } |
1110 | if ((int)$this->css[$name]['margin-left'] > 0) { |
1111 | $styles['marginLeft'] = (int)$this->css[$name]['margin-left']; |
1112 | } |
1113 | if ((int)$this->css[$name]['padding-left'] > 0) { |
1114 | $styles['paddingLeft'] = (int)$this->css[$name]['padding-left']; |
1115 | } |
1116 | } |
1117 | |
1118 | if (isset($attribs['id']) && $this->css->hasSelector('#' . $attribs['id'])) { |
1119 | if ($this->css['#' . $attribs['id']]->hasProperty('font-family')) { |
1120 | $styles['fontFamily'] = str_replace('"', '', $this->css['#' . $attribs['id']]['font-family']); |
1121 | } |
1122 | if ($this->css['#' . $attribs['id']]->hasProperty('font-size')) { |
1123 | $styles['fontSize'] = (int)$this->css['#' . $attribs['id']]['font-size']; |
1124 | } |
1125 | if ($this->css['#' . $attribs['id']]->hasProperty('font-weight')) { |
1126 | $styles['fontWeight'] = $this->css['#' . $attribs['id']]['font-weight']; |
1127 | } |
1128 | if ($this->css['#' . $attribs['id']]->hasProperty('color')) { |
1129 | $styles['color'] = $this->css['#' . $attribs['id']]['color']; |
1130 | if (is_string($styles['color'])) { |
1131 | $cssColor = Css\Color::parse($styles['color']); |
1132 | $styles['color'] = $cssColor->toRgb()->toArray(false); |
1133 | } |
1134 | } |
1135 | if ($this->css['#' . $attribs['id']]->hasProperty('float')) { |
1136 | $styles['float'] = $this->css['#' . $attribs['id']]['float']; |
1137 | } |
1138 | if ($this->css['#' . $attribs['id']]->hasProperty('width')) { |
1139 | $styles['width'] = $this->css['#' . $attribs['id']]['width']; |
1140 | } |
1141 | if ($this->css['#' . $attribs['id']]->hasProperty('height')) { |
1142 | $styles['height'] = $this->css['#' . $attribs['id']]['height']; |
1143 | } |
1144 | if ($this->css['#' . $attribs['id']]->hasProperty('line-height')) { |
1145 | $styles['lineHeight'] = (int)$this->css['#' . $attribs['id']]['line-height']; |
1146 | } |
1147 | if ((int)$this->css['#' . $attribs['id']]['margin-top'] > 0) { |
1148 | $styles['marginTop'] = (int)$this->css['#' . $attribs['id']]['margin-top']; |
1149 | } |
1150 | if ((int)$this->css['#' . $attribs['id']]['padding-top'] > 0) { |
1151 | $styles['paddingTop'] = (int)$this->css['#' . $attribs['id']]['padding-top']; |
1152 | } |
1153 | if ((int)$this->css['#' . $attribs['id']]['margin-right'] > 0) { |
1154 | $styles['marginRight'] = (int)$this->css['#' . $attribs['id']]['margin-right']; |
1155 | } |
1156 | if ((int)$this->css['#' . $attribs['id']]['padding-right'] > 0) { |
1157 | $styles['paddingRight'] = (int)$this->css['#' . $attribs['id']]['padding-right']; |
1158 | } |
1159 | if ((int)$this->css['#' . $attribs['id']]['margin-bottom'] > 0) { |
1160 | $styles['marginBottom'] = (int)$this->css['#' . $attribs['id']]['margin-bottom']; |
1161 | } |
1162 | if ((int)$this->css['#' . $attribs['id']]['padding-bottom'] > 0) { |
1163 | $styles['paddingBottom'] = (int)$this->css['#' . $attribs['id']]['padding-bottom']; |
1164 | } |
1165 | if ((int)$this->css['#' . $attribs['id']]['margin-left'] > 0) { |
1166 | $styles['marginLeft'] = (int)$this->css['#' . $attribs['id']]['margin-left']; |
1167 | } |
1168 | if ((int)$this->css['#' . $attribs['id']]['padding-left'] > 0) { |
1169 | $styles['paddingLeft'] = (int)$this->css['#' . $attribs['id']]['padding-left']; |
1170 | } |
1171 | } |
1172 | |
1173 | if (isset($attribs['class']) && $this->css->hasSelector('.' . $attribs['class'])) { |
1174 | if ($this->css['.' . $attribs['class']]->hasProperty('font-family')) { |
1175 | $styles['fontFamily'] = str_replace('"', '', $this->css['.' . $attribs['class']]['font-family']); |
1176 | } |
1177 | if ($this->css['.' . $attribs['class']]->hasProperty('font-size')) { |
1178 | $styles['fontSize'] = (int)$this->css['.' . $attribs['class']]['font-size']; |
1179 | } |
1180 | if ($this->css['.' . $attribs['class']]->hasProperty('font-weight')) { |
1181 | $styles['fontWeight'] = $this->css['.' . $attribs['class']]['font-weight']; |
1182 | } |
1183 | if ($this->css['.' . $attribs['class']]->hasProperty('color')) { |
1184 | $styles['color'] = $this->css['.' . $attribs['class']]['color']; |
1185 | if (is_string($styles['color'])) { |
1186 | $cssColor = Css\Color::parse($styles['color']); |
1187 | $styles['color'] = $cssColor->toRgb()->toArray(false); |
1188 | } |
1189 | } |
1190 | if ($this->css['.' . $attribs['class']]->hasProperty('float')) { |
1191 | $styles['float'] = $this->css['.' . $attribs['class']]['float']; |
1192 | } |
1193 | if ($this->css['.' . $attribs['class']]->hasProperty('width')) { |
1194 | $styles['width'] = $this->css['.' . $attribs['class']]['width']; |
1195 | } |
1196 | if ($this->css['.' . $attribs['class']]->hasProperty('height')) { |
1197 | $styles['height'] = $this->css['.' . $attribs['class']]['height']; |
1198 | } |
1199 | if ($this->css['.' . $attribs['class']]->hasProperty('line-height')) { |
1200 | $styles['lineHeight'] = (int)$this->css['.' . $attribs['class']]['line-height']; |
1201 | } |
1202 | if ((int)$this->css['.' . $attribs['class']]['margin-top'] > 0) { |
1203 | $styles['marginTop'] = (int)$this->css['.' . $attribs['class']]['margin-top']; |
1204 | } |
1205 | if ((int)$this->css['.' . $attribs['class']]['padding-top'] > 0) { |
1206 | $styles['paddingTop'] = (int)$this->css['.' . $attribs['class']]['padding-top']; |
1207 | } |
1208 | if ((int)$this->css['.' . $attribs['class']]['margin-right'] > 0) { |
1209 | $styles['marginRight'] = (int)$this->css['.' . $attribs['class']]['margin-right']; |
1210 | } |
1211 | if ((int)$this->css['.' . $attribs['class']]['padding-right'] > 0) { |
1212 | $styles['paddingRight'] = (int)$this->css['.' . $attribs['class']]['padding-right']; |
1213 | } |
1214 | if ((int)$this->css['.' . $attribs['class']]['margin-bottom'] > 0) { |
1215 | $styles['marginBottom'] = (int)$this->css['.' . $attribs['class']]['margin-bottom']; |
1216 | } |
1217 | if ((int)$this->css['.' . $attribs['class']]['padding-bottom'] > 0) { |
1218 | $styles['paddingBottom'] = (int)$this->css['.' . $attribs['class']]['padding-bottom']; |
1219 | } |
1220 | if ((int)$this->css['.' . $attribs['class']]['margin-left'] > 0) { |
1221 | $styles['marginLeft'] = (int)$this->css['.' . $attribs['class']]['margin-left']; |
1222 | } |
1223 | if ((int)$this->css['.' . $attribs['class']]['padding-left'] > 0) { |
1224 | $styles['paddingLeft'] = (int)$this->css['.' . $attribs['class']]['padding-left']; |
1225 | } |
1226 | } |
1227 | |
1228 | if (strpos($styles['fontFamily'], ',') !== false) { |
1229 | $fonts = explode(',', $styles['fontFamily']); |
1230 | foreach ($fonts as $font) { |
1231 | $font = trim($font); |
1232 | if ($this->document->hasFont($font)) { |
1233 | $styles['currentFont'] = $font; |
1234 | break; |
1235 | } else if ($this->document->hasFont(str_replace(' ', '-', $font))) { |
1236 | $styles['currentFont'] = str_replace(' ', '-', $font); |
1237 | } else if ($this->document->hasFont(str_replace(' ', ',', $font))) { |
1238 | $styles['currentFont'] = str_replace(' ', ',', $font); |
1239 | } else if ($this->document->hasFont(str_replace(' ', '', $font))) { |
1240 | $styles['currentFont'] = str_replace(' ', '', $font); |
1241 | } |
1242 | } |
1243 | } else { |
1244 | $styles['currentFont'] = $styles['fontFamily']; |
1245 | } |
1246 | |
1247 | if (null === $styles['currentFont']) { |
1248 | throw new Exception('Error: No available font has been detected.'); |
1249 | } else if ($styles['currentFont'] == 'sans-serif') { |
1250 | $styles['currentFont'] = 'Arial'; |
1251 | } else if ($styles['currentFont'] == 'serif') { |
1252 | $styles['currentFont'] = 'TimesNewRoman'; |
1253 | } |
1254 | |
1255 | if ($styles['fontWeight'] == 'bold') { |
1256 | if ($this->document->hasFont($styles['currentFont'] . 'Bold')) { |
1257 | $styles['currentFont'] .= 'Bold'; |
1258 | } else if ($this->document->hasFont($styles['currentFont'] . '-Bold')) { |
1259 | $styles['currentFont'] .= '-Bold'; |
1260 | } else if ($this->document->hasFont($styles['currentFont'] . ',Bold')) { |
1261 | $styles['currentFont'] .= ',Bold'; |
1262 | } |
1263 | } |
1264 | |
1265 | if (!($this->document->hasFont($styles['currentFont']))) { |
1266 | $standardFonts = Document\Font::standardFonts(); |
1267 | if (in_array($styles['currentFont'], $standardFonts)) { |
1268 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1269 | } else if (in_array(str_replace(' ', '-', $styles['currentFont']), $standardFonts)) { |
1270 | $styles['currentFont'] = str_replace(' ', '-', $styles['currentFont']); |
1271 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1272 | } else if (in_array(str_replace(' ', ',', $styles['currentFont']), $standardFonts)) { |
1273 | $styles['currentFont'] = str_replace(' ', ',', $styles['currentFont']); |
1274 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1275 | } else if (in_array(str_replace(' ', '', $styles['currentFont']), $standardFonts)) { |
1276 | $styles['currentFont'] = str_replace(' ', '', $styles['currentFont']); |
1277 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1278 | } else { |
1279 | throw new Exception('Error: The current font has not been added to the document.'); |
1280 | } |
1281 | |
1282 | if ($styles['fontWeight'] == 'bold') { |
1283 | if ($this->document->hasFont($styles['currentFont'] . 'Bold')) { |
1284 | $styles['currentFont'] .= 'Bold'; |
1285 | } else if ($this->document->hasFont($styles['currentFont'] . '-Bold')) { |
1286 | $styles['currentFont'] .= '-Bold'; |
1287 | } else if ($this->document->hasFont($styles['currentFont'] . ',Bold')) { |
1288 | $styles['currentFont'] .= ',Bold'; |
1289 | } else if (in_array($styles['currentFont'] . 'Bold', $standardFonts)) { |
1290 | $styles['currentFont'] .= 'Bold'; |
1291 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1292 | } else if (in_array($styles['currentFont'] . '-Bold', $standardFonts)) { |
1293 | $styles['currentFont'] .= '-Bold'; |
1294 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1295 | } else if (in_array($styles['currentFont'] . ',Bold', $standardFonts)) { |
1296 | $styles['currentFont'] .= ',Bold'; |
1297 | $this->document->addFont(new Document\Font($styles['currentFont'])); |
1298 | } |
1299 | } |
1300 | } |
1301 | |
1302 | return $styles; |
1303 | } |
1304 | |
1305 | /** |
1306 | * Get current X-position |
1307 | * |
1308 | * @return int |
1309 | */ |
1310 | protected function getCurrentX() |
1311 | { |
1312 | if ($this->x < $this->pageMargins['left']) { |
1313 | $this->x = $this->pageMargins['left']; |
1314 | } |
1315 | return $this->x; |
1316 | } |
1317 | |
1318 | /** |
1319 | * Reset X-position |
1320 | * |
1321 | * @return int |
1322 | */ |
1323 | protected function resetX() |
1324 | { |
1325 | $this->x = $this->pageMargins['left']; |
1326 | return $this->x; |
1327 | } |
1328 | |
1329 | /** |
1330 | * Get current Y-position |
1331 | * |
1332 | * @return int |
1333 | */ |
1334 | protected function getCurrentY() |
1335 | { |
1336 | if (!($this->document->hasPages())) { |
1337 | $this->page = (is_array($this->pageSize)) ? |
1338 | new Document\Page($this->pageSize['width'], $this->pageSize['height']) : new Document\Page($this->pageSize); |
1339 | $this->document->addPage($this->page); |
1340 | } else { |
1341 | $this->page = $this->document->getPage($this->document->getCurrentPage()); |
1342 | } |
1343 | |
1344 | $currentY = $this->page->getHeight() - $this->pageMargins['top'] - $this->y; |
1345 | |
1346 | if ($currentY <= $this->pageMargins['bottom']) { |
1347 | $this->page = (is_array($this->pageSize)) ? |
1348 | new Document\Page($this->pageSize['width'], $this->pageSize['height']) : new Document\Page($this->pageSize); |
1349 | $this->document->addPage($this->page); |
1350 | $currentY = $this->resetY(); |
1351 | } |
1352 | |
1353 | return $currentY; |
1354 | } |
1355 | |
1356 | /** |
1357 | * Reset Y-position |
1358 | * |
1359 | * @return int |
1360 | */ |
1361 | protected function resetY() |
1362 | { |
1363 | if (!($this->document->hasPages())) { |
1364 | $this->page = (is_array($this->pageSize)) ? |
1365 | new Document\Page($this->pageSize['width'], $this->pageSize['height']) : new Document\Page($this->pageSize); |
1366 | $this->document->addPage($this->page); |
1367 | } else { |
1368 | $this->page = $this->document->getPage($this->document->getCurrentPage()); |
1369 | } |
1370 | |
1371 | $this->y = 0; |
1372 | |
1373 | return $this->page->getHeight() - $this->pageMargins['top']; |
1374 | } |
1375 | |
1376 | /** |
1377 | * Create new page |
1378 | * |
1379 | * @return int |
1380 | */ |
1381 | protected function newPage() |
1382 | { |
1383 | $this->page = (is_array($this->pageSize)) ? |
1384 | new Document\Page($this->pageSize['width'], $this->pageSize['height']) : new Document\Page($this->pageSize); |
1385 | $this->document->addPage($this->page); |
1386 | $this->y = 0; |
1387 | return $this->page->getHeight() - $this->pageMargins['top'] - $this->y; |
1388 | } |
1389 | |
1390 | /** |
1391 | * Go to next line |
1392 | * |
1393 | * @param array $styles |
1394 | * @return void |
1395 | */ |
1396 | protected function goToNextLine(array $styles) |
1397 | { |
1398 | $this->y += $styles['marginBottom'] + $styles['paddingBottom'] + $styles['lineHeight']; |
1399 | } |
1400 | |
1401 | /** |
1402 | * Get string lines |
1403 | * |
1404 | * @param string $string |
1405 | * @param int $fontSize |
1406 | * @param int $wrapLength |
1407 | * @param Document\Font $fontObject |
1408 | * @return array |
1409 | */ |
1410 | protected function getStringLines($string, $fontSize, $wrapLength, Document\Font $fontObject) |
1411 | { |
1412 | $strings = []; |
1413 | $curString = ''; |
1414 | $words = explode(' ', $string); |
1415 | |
1416 | foreach ($words as $word) { |
1417 | $newString = ($curString != '') ? $curString . ' ' . $word : $word; |
1418 | if ($fontObject->getStringWidth($newString, $fontSize) <= $wrapLength) { |
1419 | $curString = $newString; |
1420 | } else { |
1421 | $strings[] = $curString; |
1422 | $curString = $word; |
1423 | } |
1424 | } |
1425 | if (!empty($curString)) { |
1426 | $strings[] = $curString; |
1427 | } |
1428 | |
1429 | return $strings; |
1430 | } |
1431 | |
1432 | } |