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