BlockBox::getInstructions()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
dl 0
loc 17
rs 9.8333
c 1
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * BlockBox class.
6
 *
7
 * @package   YetiForcePDF\Layout
8
 *
9
 * @copyright YetiForce Sp. z o.o
10
 * @license   MIT
11
 * @author    Rafal Pospiech <[email protected]>
12
 */
13
14
namespace YetiForcePDF\Layout;
15
16
use YetiForcePDF\Html\Element;
17
use YetiForcePDF\Layout\Coordinates\Coordinates;
18
use YetiForcePDF\Layout\Coordinates\Offset;
19
use YetiForcePDF\Layout\Dimensions\BoxDimensions;
20
use YetiForcePDF\Math;
21
use YetiForcePDF\Style\Style;
22
23
/**
24
 * Class BlockBox.
25
 */
26
class BlockBox extends ElementBox implements BoxInterface, AppendChildInterface, BuildTreeInterface
27
{
28
	/**
29
	 * @var \YetiForcePDF\Layout\LineBox
30
	 */
31
	protected $currentLineBox;
32
	/**
33
	 * Parent width cache.
34
	 *
35
	 * @var string
36
	 */
37
	protected $parentWidth = '0';
38
39
	/**
40
	 * @var LineBox[]
41
	 */
42
	protected $sourceLines = [];
43
44
	/**
45
	 * {@inheritdoc}
46
	 */
47
	public function init()
48
	{
49
		parent::init();
50
		$this->dimensions = (new BoxDimensions())
0 ignored issues
show
Documentation Bug introduced by
It seems like new YetiForcePDF\Layout\...->setBox($this)->init() of type YetiForcePDF\Layout\Dimensions\BoxDimensions is incompatible with the declared type YetiForcePDF\Layout\box of property $dimensions.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
51
			->setDocument($this->document)
52
			->setBox($this)
53
			->init();
54
		$this->coordinates = (new Coordinates())
55
			->setDocument($this->document)
56
			->setBox($this)
57
			->init();
58
		$this->offset = (new Offset())
59
			->setDocument($this->document)
60
			->setBox($this)
61
			->init();
62
63
		return $this;
64
	}
65
66
	/**
67
	 * Get element.
68
	 *
69
	 * @return Element
70
	 */
71
	public function getElement()
72
	{
73
		return $this->element;
74
	}
75
76
	/**
77
	 * Set element.
78
	 *
79
	 * @param Element $element
80
	 *
81
	 * @return $this
82
	 */
83
	public function setElement(Element $element)
84
	{
85
		$this->element = $element;
86
		$element->setBox($this);
87
88
		return $this;
89
	}
90
91
	/**
92
	 * Should we break page after this element?
93
	 *
94
	 * @return bool
95
	 */
96
	public function shouldBreakPage()
97
	{
98
		return 'always' === $this->getStyle()->getRules('break-page-after');
99
	}
100
101
	/**
102
	 * Get new line box.
103
	 *
104
	 * @param Box $before [optional]
105
	 *
106
	 * @return \YetiForcePDF\Layout\LineBox
107
	 */
108
	public function getNewLineBox($before = null)
109
	{
110
		$this->currentLineBox = (new LineBox())
111
			->setDocument($this->document)
112
			->setParent($this)
113
			->init();
114
		if (null !== $before) {
115
			$this->insertBefore($this->currentLineBox, $before);
116
		} else {
117
			$this->appendChild($this->currentLineBox);
118
		}
119
		$style = (new Style())
120
			->setDocument($this->document)
121
			->setBox($this->currentLineBox);
122
		$this->currentLineBox->setStyle($style);
123
		$this->currentLineBox->getStyle()->init();
124
125
		return $this->currentLineBox;
126
	}
127
128
	/**
129
	 * Close line box.
130
	 *
131
	 * @param \YetiForcePDF\Layout\LineBox|null $lineBox
132
	 * @param bool                              $createNew
133
	 *
134
	 * @return \YetiForcePDF\Layout\LineBox
135
	 */
136
	public function closeLine()
137
	{
138
		$this->saveSourceLine($this->currentLineBox);
139
		$this->currentLineBox = null;
140
141
		return $this->currentLineBox;
142
	}
143
144
	/**
145
	 * Get current linebox.
146
	 *
147
	 * @return \YetiForcePDF\Layout\LineBox
148
	 */
149
	public function getCurrentLineBox()
150
	{
151
		return $this->currentLineBox;
152
	}
153
154
	/**
155
	 * {@inheritdoc}
156
	 */
157
	public function appendBlockBox($childDomElement, $element, $style, $parentBlock)
158
	{
159
		if ($this->getCurrentLineBox()) {
160
			$this->closeLine();
161
		}
162
		$box = (new self())
163
			->setDocument($this->document)
164
			->setParent($this)
165
			->setElement($element)
166
			->setStyle($style)
167
			->init();
168
		$this->appendChild($box);
169
		$box->getStyle()->init();
170
		$box->buildTree($box);
171
172
		return $box;
173
	}
174
175
	/**
176
	 * Append header box.
177
	 *
178
	 * @param $childDomElement
179
	 * @param $element
180
	 * @param $style
181
	 * @param $parentBlock
182
	 *
183
	 * @throws \InvalidArgumentException
184
	 *
185
	 * @return HeaderBox
186
	 */
187
	public function appendHeaderBox($childDomElement, $element, $style, $parentBlock)
0 ignored issues
show
Unused Code introduced by
The parameter $childDomElement is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

187
	public function appendHeaderBox(/** @scrutinizer ignore-unused */ $childDomElement, $element, $style, $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parentBlock is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

187
	public function appendHeaderBox($childDomElement, $element, $style, /** @scrutinizer ignore-unused */ $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
188
	{
189
		if ($this->getCurrentLineBox()) {
190
			$this->closeLine();
191
		}
192
		$box = (new HeaderBox())
193
			->setDocument($this->document)
194
			->setParent($this)
195
			->setElement($element)
196
			->setStyle($style)
197
			->init();
198
		$this->appendChild($box);
199
		$box->getStyle()->init();
200
		$box->buildTree($box);
201
		$box->setDisplayable(false);
202
203
		return $box;
204
	}
205
206
	/**
207
	 * Append footer box.
208
	 *
209
	 * @param $childDomElement
210
	 * @param $element
211
	 * @param $style
212
	 * @param $parentBlock
213
	 *
214
	 * @throws \InvalidArgumentException
215
	 *
216
	 * @return HeaderBox
217
	 */
218
	public function appendFooterBox($childDomElement, $element, $style, $parentBlock)
0 ignored issues
show
Unused Code introduced by
The parameter $parentBlock is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

218
	public function appendFooterBox($childDomElement, $element, $style, /** @scrutinizer ignore-unused */ $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $childDomElement is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

218
	public function appendFooterBox(/** @scrutinizer ignore-unused */ $childDomElement, $element, $style, $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
219
	{
220
		if ($this->getCurrentLineBox()) {
221
			$this->closeLine();
222
		}
223
		$box = (new FooterBox())
224
			->setDocument($this->document)
225
			->setParent($this)
226
			->setElement($element)
227
			->setStyle($style)
228
			->init();
229
		$this->appendChild($box);
230
		$box->getStyle()->init();
231
		$box->buildTree($box);
232
		$box->setDisplayable(false);
233
234
		return $box;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $box returns the type YetiForcePDF\Layout\FooterBox which is incompatible with the documented return type YetiForcePDF\Layout\HeaderBox.
Loading history...
235
	}
236
237
	/**
238
	 * Append watermark box.
239
	 *
240
	 * @param $childDomElement
241
	 * @param $element
242
	 * @param $style
243
	 * @param $parentBlock
244
	 *
245
	 * @throws \InvalidArgumentException
246
	 *
247
	 * @return HeaderBox
248
	 */
249
	public function appendWatermarkBox($childDomElement, $element, $style, $parentBlock)
0 ignored issues
show
Unused Code introduced by
The parameter $childDomElement is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

249
	public function appendWatermarkBox(/** @scrutinizer ignore-unused */ $childDomElement, $element, $style, $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parentBlock is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

249
	public function appendWatermarkBox($childDomElement, $element, $style, /** @scrutinizer ignore-unused */ $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
250
	{
251
		if ($this->getCurrentLineBox()) {
252
			$this->closeLine();
253
		}
254
		$box = (new WatermarkBox())
255
			->setDocument($this->document)
256
			->setParent($this)
257
			->setElement($element)
258
			->setStyle($style)
259
			->init();
260
		$this->appendChild($box);
261
		$box->getStyle()->init();
262
		$box->buildTree($box);
263
		$box->setDisplayable(false);
264
265
		return $box;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $box returns the type YetiForcePDF\Layout\WatermarkBox which is incompatible with the documented return type YetiForcePDF\Layout\HeaderBox.
Loading history...
266
	}
267
268
	/**
269
	 * Append font box.
270
	 *
271
	 * @param $childDomElement
272
	 * @param $element
273
	 * @param $style
274
	 * @param $parentBlock
275
	 *
276
	 * @throws \InvalidArgumentException
277
	 *
278
	 * @return HeaderBox
279
	 */
280
	public function appendFontBox($childDomElement, $element, $style, $parentBlock)
0 ignored issues
show
Unused Code introduced by
The parameter $parentBlock is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

280
	public function appendFontBox($childDomElement, $element, $style, /** @scrutinizer ignore-unused */ $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
281
	{
282
		if ($this->getCurrentLineBox()) {
283
			$this->closeLine();
284
		}
285
		$box = (new FontBox())
286
			->setDocument($this->document)
287
			->setParent($this)
288
			->setElement($element)
289
			->setStyle($style)
290
			->init();
291
		$this->appendChild($box);
292
		$box->getStyle()->init();
293
		$box->setFontFamily($childDomElement->getAttribute('data-family'));
294
		$box->setFontWeight($childDomElement->getAttribute('data-weight'));
295
		$box->setFontStyle($childDomElement->getAttribute('data-style'));
296
		$box->setFontFile($childDomElement->getAttribute('data-file'));
297
		$box->loadFont();
298
		$box->buildTree($box);
299
		$box->setDisplayable(false)->setRenderable(false)->setForMeasurement(false);
300
301
		return $box;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $box returns the type YetiForcePDF\Layout\FontBox which is incompatible with the documented return type YetiForcePDF\Layout\HeaderBox.
Loading history...
302
	}
303
304
	public function appendStyleBox($childDomElement, $element, $style, $parentBlock)
0 ignored issues
show
Unused Code introduced by
The parameter $childDomElement is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

304
	public function appendStyleBox(/** @scrutinizer ignore-unused */ $childDomElement, $element, $style, $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parentBlock is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

304
	public function appendStyleBox($childDomElement, $element, $style, /** @scrutinizer ignore-unused */ $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
305
	{
306
		$box = (new StyleBox())
307
			->setDocument($this->document)
308
			->setParent($this)
309
			->setElement($element)
310
			->setStyle($style)
311
			->init();
312
		$this->appendChild($box);
313
		$box->setDisplayable(false)->setRenderable(false)->setForMeasurement(false);
314
		return $box;
315
	}
316
317
	/**
318
	 * Append barcode box.
319
	 *
320
	 * @param $childDomElement
321
	 * @param $element
322
	 * @param $style
323
	 * @param $parentBlock
324
	 *
325
	 * @throws \InvalidArgumentException
326
	 *
327
	 * @return HeaderBox
328
	 */
329
	public function appendBarcodeBox($childDomElement, $element, $style, $parentBlock)
0 ignored issues
show
Unused Code introduced by
The parameter $parentBlock is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

329
	public function appendBarcodeBox($childDomElement, $element, $style, /** @scrutinizer ignore-unused */ $parentBlock)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
330
	{
331
		if ($this->getCurrentLineBox()) {
332
			$currentLineBox = $this->getCurrentLineBox();
333
		} else {
334
			$currentLineBox = $this->getNewLineBox();
335
		}
336
337
		return $currentLineBox->appendBarcode($childDomElement, $element, $style, $this);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $currentLineBox->...element, $style, $this) returns the type YetiForcePDF\Layout\BarcodeBox which is incompatible with the documented return type YetiForcePDF\Layout\HeaderBox.
Loading history...
338
	}
339
340
	/**
341
	 * {@inheritdoc}
342
	 */
343
	public function appendTableWrapperBox($childDomElement, $element, $style, $parentBlock)
344
	{
345
		if ($this->getCurrentLineBox()) {
346
			$this->closeLine();
347
		}
348
		$box = (new TableWrapperBox())
349
			->setDocument($this->document)
350
			->setParent($this)
351
			->setElement($element)
352
			->setStyle($style)
353
			->init();
354
		$this->appendChild($box);
355
		$box->getStyle()->init()->setRule('display', 'block');
356
		// we wan't to build tree from here - we will build it from TableBox
357
		return $box;
358
	}
359
360
	/**
361
	 * {@inheritdoc}
362
	 */
363
	public function appendInlineBlockBox($childDomElement, $element, $style, $parentBlock)
364
	{
365
		if ($this->getCurrentLineBox()) {
366
			$currentLineBox = $this->getCurrentLineBox();
367
		} else {
368
			$currentLineBox = $this->getNewLineBox();
369
		}
370
371
		return $currentLineBox->appendInlineBlock($childDomElement, $element, $style, $this);
372
	}
373
374
	/**
375
	 * {@inheritdoc}
376
	 */
377
	public function appendInlineBox($childDomElement, $element, $style, $parentBlock)
378
	{
379
		if ($this->getCurrentLineBox()) {
380
			$currentLineBox = $this->getCurrentLineBox();
381
		} else {
382
			$currentLineBox = $this->getNewLineBox();
383
		}
384
385
		return $currentLineBox->appendInline($childDomElement, $element, $style, $this);
386
	}
387
388
	/**
389
	 * Measure width of this block.
390
	 *
391
	 * @param bool $afterPageDividing
392
	 *
393
	 * @return $this
394
	 */
395
	public function measureWidth(bool $afterPageDividing = false)
396
	{
397
		$dimensions = $this->getDimensions();
398
		$parent = $this->getParent();
399
		if ($parent) {
0 ignored issues
show
introduced by
$parent is of type YetiForcePDF\Layout\Box, thus it always evaluated to true.
Loading history...
400
			if ($this->parentWidth === $parent->getDimensions()->getWidth()) {
401
				$this->divideLines();
402
				return $this;
403
			}
404
			$this->parentWidth = $parent->getDimensions()->getWidth();
405
			$oldWidth = $this->getDimensions()->getWidth();
406
			if (null !== $parent->getDimensions()->getWidth()) {
0 ignored issues
show
introduced by
The condition null !== $parent->getDimensions()->getWidth() is always true.
Loading history...
407
				$dimensions->setWidth(Math::sub($parent->getDimensions()->getInnerWidth(), $this->getStyle()->getHorizontalMarginsWidth()));
408
				$this->applyStyleWidth();
409
				if ($oldWidth !== $this->getDimensions()->getWidth()) {
410
					foreach ($this->getChildren() as $child) {
411
						$child->measureWidth($afterPageDividing);
0 ignored issues
show
Bug introduced by
The method measureWidth() does not exist on YetiForcePDF\Layout\Box. It seems like you code against a sub-type of said class. However, the method does not exist in YetiForcePDF\Layout\ElementBox. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

411
						$child->/** @scrutinizer ignore-call */ 
412
              measureWidth($afterPageDividing);
Loading history...
412
					}
413
					$this->divideLines();
414
				}
415
416
				return $this;
417
			}
418
			// if parent doesn't have a width specified
419
			foreach ($this->getChildren() as $child) {
420
				$child->measureWidth($afterPageDividing);
421
			}
422
			$this->divideLines();
423
			$maxWidth = '0';
424
			foreach ($this->getChildren() as $child) {
425
				$maxWidth = Math::max($maxWidth, $child->getDimensions()->getOuterWidth());
426
			}
427
			$style = $this->getStyle();
428
			$maxWidth = Math::add($maxWidth, $style->getHorizontalBordersWidth(), $style->getHorizontalPaddingsWidth());
429
			$maxWidth = Math::sub($maxWidth, $style->getHorizontalMarginsWidth());
430
			$dimensions->setWidth($maxWidth);
431
			$this->applyStyleWidth();
432
433
			return $this;
434
		}
435
		$width = $this->document->getCurrentPage()->getDimensions()->getWidth();
436
		if ($this->getDimensions()->getWidth() !== $width) {
437
			$dimensions->setWidth($width);
438
			$this->applyStyleWidth();
439
			foreach ($this->getChildren() as $child) {
440
				$child->measureWidth($afterPageDividing);
441
			}
442
			$this->divideLines();
443
		}
444
445
		return $this;
446
	}
447
448
	/**
449
	 * Group sibling line boxes into two dimensional array.
450
	 *
451
	 * @return array
452
	 */
453
	public function groupLines()
454
	{
455
		$lineGroups = [];
456
		$currentGroup = 0;
457
		foreach ($this->getChildren() as $child) {
458
			if ($child instanceof LineBox) {
459
				$lineGroups[$currentGroup][] = $child;
460
			} else {
461
				if (isset($lineGroups[$currentGroup])) {
462
					++$currentGroup;
463
				}
464
			}
465
		}
466
467
		return $lineGroups;
468
	}
469
470
	/**
471
	 * Merge line groups into one line (reverse divide - reorganize).
472
	 *
473
	 * @param array $lineGroups
474
	 *
475
	 * @return LineBox[]
476
	 */
477
	public function mergeLineGroups(array $lineGroups)
478
	{
479
		$lines = [];
480
		foreach ($lineGroups as $lines) {
481
			if (!empty($lines)) {
482
				$currentLine = $this->getNewLineBox($lines[0]);
483
				foreach ($lines as $line) {
484
					foreach ($line->getChildren() as $child) {
485
						$child->setForMeasurement(true);
486
						$currentLine->appendChild($line->removeChild($child));
487
					}
488
					$this->removeChild($line);
489
				}
490
				$lines[] = $currentLine;
491
			}
492
		}
493
494
		return $lines;
495
	}
496
497
	/**
498
	 * Hide empty lines.
499
	 *
500
	 * @return $this
501
	 */
502
	protected function hideEmptyLines()
503
	{
504
		foreach ($this->getChildren() as $child) {
505
			if ($child instanceof LineBox) {
506
				if ($child->isEmpty() && !$child->getStyle()->haveSpacing()) {
507
					$child->setRenderable(false);
508
				} else {
509
					$child->setRenderable();
510
				}
511
			}
512
		}
513
514
		return $this;
515
	}
516
517
	/**
518
	 * Save source lines before any dividing process (to get maximal width of the block for tables later).
519
	 *
520
	 * @param LineBox $line
521
	 *
522
	 * @return $this
523
	 */
524
	protected function saveSourceLine(LineBox $line)
525
	{
526
		$this->sourceLines[] = $line->clone();
527
528
		return $this;
529
	}
530
531
	/**
532
	 * Get initial lines before any process was applied.
533
	 *
534
	 * @return LineBox[]
535
	 */
536
	public function getSourceLines()
537
	{
538
		return $this->sourceLines;
539
	}
540
541
	/**
542
	 * Divide lines.
543
	 *
544
	 * @return $this
545
	 */
546
	public function divideLines()
547
	{
548
		$this->mergeLineGroups($this->groupLines());
549
		foreach ($this->getChildren() as $child) {
550
			if ($child instanceof LineBox) {
551
				$lines = $child->divide();
552
				foreach ($lines as $line) {
553
					$this->insertBefore($line, $child);
554
					$line->getStyle()->init();
555
					if (!$this instanceof InlineBlockBox) {
556
						$line->removeWhiteSpaces();
557
					}
558
					$line->measureWidth();
559
				}
560
				$this->removeChild($child);
561
			}
562
		}
563
		$this->hideEmptyLines();
564
		unset($lines);
565
566
		return $this;
567
	}
568
569
	/**
570
	 * Measure height.
571
	 *
572
	 * @param bool $afterPageDividing
573
	 *
574
	 * @return $this
575
	 */
576
	public function measureHeight(bool $afterPageDividing = false)
577
	{
578
		if (\YetiForcePDF\Page::CUT_BELOW === $this->wasCut()) {
0 ignored issues
show
introduced by
The condition YetiForcePDF\Page::CUT_BELOW === $this->wasCut() is always false.
Loading history...
579
			return $this;
580
		}
581
		$this->applyStyleHeight();
582
		foreach ($this->getChildren() as $child) {
583
			$child->measureHeight($afterPageDividing);
0 ignored issues
show
Bug introduced by
The method measureHeight() does not exist on YetiForcePDF\Layout\Box. It seems like you code against a sub-type of said class. However, the method does not exist in YetiForcePDF\Layout\ElementBox. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

583
			$child->/** @scrutinizer ignore-call */ 
584
           measureHeight($afterPageDividing);
Loading history...
584
		}
585
		$height = '0';
586
		foreach ($this->getChildren() as $child) {
587
			$height = Math::add($height, $child->getDimensions()->getHeight(), $child->getStyle()->getVerticalMarginsWidth());
0 ignored issues
show
Bug introduced by
It seems like $child->getDimensions()->getHeight() can also be of type null; however, parameter $numbers of YetiForcePDF\Math::add() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

587
			$height = Math::add($height, /** @scrutinizer ignore-type */ $child->getDimensions()->getHeight(), $child->getStyle()->getVerticalMarginsWidth());
Loading history...
588
		}
589
		$style = $this->getStyle();
590
		$height = Math::add($height, $style->getVerticalPaddingsWidth(), $style->getVerticalBordersWidth());
591
		$this->getDimensions()->setHeight($height);
592
		$this->applyStyleHeight();
593
594
		return $this;
595
	}
596
597
	/**
598
	 * Offset elements.
599
	 *
600
	 * @param bool $afterPageDividing
601
	 *
602
	 * @return $this
603
	 */
604
	public function measureOffset(bool $afterPageDividing = false)
605
	{
606
		if (\YetiForcePDF\Page::CUT_BELOW === $this->wasCut()) {
0 ignored issues
show
introduced by
The condition YetiForcePDF\Page::CUT_BELOW === $this->wasCut() is always false.
Loading history...
607
			return $this;
608
		}
609
		$top = $this->document->getCurrentPage()->getCoordinates()->getY();
610
		$left = $this->document->getCurrentPage()->getCoordinates()->getX();
611
		$marginTop = $this->getStyle()->getRules('margin-top');
612
		if ($parent = $this->getParent()) {
613
			$parentStyle = $parent->getStyle();
614
			$top = $parentStyle->getOffsetTop();
615
			$left = $parentStyle->getOffsetLeft();
616
			if ($previous = $this->getPrevious()) {
617
				if ($previous->getOffset()->getTop()) {
618
					$top = Math::add($previous->getOffset()->getTop(), $previous->getDimensions()->getHeight());
0 ignored issues
show
Bug introduced by
It seems like $previous->getDimensions()->getHeight() can also be of type null; however, parameter $numbers of YetiForcePDF\Math::add() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

618
					$top = Math::add($previous->getOffset()->getTop(), /** @scrutinizer ignore-type */ $previous->getDimensions()->getHeight());
Loading history...
619
				}
620
				if ('block' === $previous->getStyle()->getRules('display')) {
621
					$marginTop = Math::comp($marginTop, $previous->getStyle()->getRules('margin-bottom')) > 0 ? $marginTop : $previous->getStyle()->getRules('margin-bottom');
0 ignored issues
show
Bug introduced by
It seems like $previous->getStyle()->getRules('margin-bottom') can also be of type array; however, parameter $right of YetiForcePDF\Math::comp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

621
					$marginTop = Math::comp($marginTop, /** @scrutinizer ignore-type */ $previous->getStyle()->getRules('margin-bottom')) > 0 ? $marginTop : $previous->getStyle()->getRules('margin-bottom');
Loading history...
Bug introduced by
It seems like $marginTop can also be of type array; however, parameter $left of YetiForcePDF\Math::comp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

621
					$marginTop = Math::comp(/** @scrutinizer ignore-type */ $marginTop, $previous->getStyle()->getRules('margin-bottom')) > 0 ? $marginTop : $previous->getStyle()->getRules('margin-bottom');
Loading history...
622
				} elseif (!$previous instanceof LineBox) {
623
					$marginTop = Math::add($marginTop, $previous->getStyle()->getRules('margin-bottom'));
0 ignored issues
show
Bug introduced by
It seems like $marginTop can also be of type array; however, parameter $numbers of YetiForcePDF\Math::add() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

623
					$marginTop = Math::add(/** @scrutinizer ignore-type */ $marginTop, $previous->getStyle()->getRules('margin-bottom'));
Loading history...
624
				}
625
			}
626
		}
627
		$top = Math::add($top, (string) $marginTop);
628
		$left = Math::add($left, $this->getStyle()->getRules('margin-left'));
629
		$this->getOffset()->setTop($top);
630
		$this->getOffset()->setLeft($left);
631
		foreach ($this->getChildren() as $child) {
632
			$child->measureOffset($afterPageDividing);
0 ignored issues
show
Bug introduced by
The method measureOffset() does not exist on YetiForcePDF\Layout\Box. It seems like you code against a sub-type of said class. However, the method does not exist in YetiForcePDF\Layout\ElementBox. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

632
			$child->/** @scrutinizer ignore-call */ 
633
           measureOffset($afterPageDividing);
Loading history...
633
		}
634
635
		return $this;
636
	}
637
638
	/**
639
	 * Position.
640
	 *
641
	 * @param bool $afterPageDividing
642
	 *
643
	 * @return $this
644
	 */
645
	public function measurePosition(bool $afterPageDividing = false)
646
	{
647
		if (\YetiForcePDF\Page::CUT_BELOW === $this->wasCut()) {
0 ignored issues
show
introduced by
The condition YetiForcePDF\Page::CUT_BELOW === $this->wasCut() is always false.
Loading history...
648
			return $this;
649
		}
650
		$x = $this->document->getCurrentPage()->getCoordinates()->getX();
651
		$y = $this->document->getCurrentPage()->getCoordinates()->getY();
652
		if ($parent = $this->getParent()) {
653
			$x = Math::add($parent->getCoordinates()->getX(), $this->getOffset()->getLeft());
654
			$y = Math::add($parent->getCoordinates()->getY(), $this->getOffset()->getTop());
655
		}
656
		$this->getCoordinates()->setX($x);
657
		$this->getCoordinates()->setY($y);
658
		foreach ($this->getChildren() as $child) {
659
			$child->measurePosition($afterPageDividing);
0 ignored issues
show
Bug introduced by
The method measurePosition() does not exist on YetiForcePDF\Layout\Box. It seems like you code against a sub-type of said class. However, the method does not exist in YetiForcePDF\Layout\ElementBox. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

659
			$child->/** @scrutinizer ignore-call */ 
660
           measurePosition($afterPageDividing);
Loading history...
660
		}
661
662
		return $this;
663
	}
664
665
	/**
666
	 * Layout elements.
667
	 *
668
	 * @param bool $afterPageDividing
669
	 *
670
	 * @return $this
671
	 */
672
	public function layout(bool $afterPageDividing = false)
673
	{
674
		if (!$afterPageDividing) {
675
			$this->measureWidth();
676
		}
677
		$this->measureHeight($afterPageDividing);
678
		$this->measureOffset($afterPageDividing);
679
		$this->alignText();
680
		$this->measurePosition($afterPageDividing);
681
682
		return $this;
683
	}
684
685
	/**
686
	 * Replace page numbers.
687
	 *
688
	 * @return $this
689
	 */
690
	public function replacePageNumbers()
691
	{
692
		$allChildren = [];
693
		$this->getAllChildren($allChildren);
694
		foreach ($allChildren as $child) {
695
			if ($child instanceof TextBox) {
696
				if (false !== mb_stripos($child->getTextContent(), '{p}')) {
697
					$pageNumber = $this->document->getCurrentPage()->getPageNumber();
698
					$child->setText(preg_replace('/{p}/ui', (string) $pageNumber, $child->getTextContent()));
699
					$child->getClosestByType('BlockBox')->layout();
0 ignored issues
show
Bug introduced by
The method layout() does not exist on YetiForcePDF\Layout\TextBox. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

699
					$child->getClosestByType('BlockBox')->/** @scrutinizer ignore-call */ layout();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
700
				}
701
				if (false !== mb_stripos($child->getTextContent(), '{a}')) {
702
					$pages = (string) $this->document->getCurrentPage()->getPageCount();
703
					$child->setText(preg_replace('/{a}/ui', $pages, $child->getTextContent()));
704
					$child->getClosestByType('BlockBox')->layout();
705
				}
706
			}
707
		}
708
		unset($allChildren);
709
710
		return $this;
711
	}
712
713
	/**
714
	 * Add background color instructions.
715
	 *
716
	 * @param array $element
717
	 * @param $pdfX
718
	 * @param $pdfY
719
	 * @param $width
720
	 * @param $height
721
	 *
722
	 * @return array
723
	 */
724
	public function addBackgroundColorInstructions(array $element, $pdfX, $pdfY, $width, $height)
725
	{
726
		if ('none' === $this->getStyle()->getRules('display')) {
727
			return $element;
728
		}
729
		$rules = $this->style->getRules();
730
		$graphicState = $this->style->getGraphicState();
731
		$graphicStateStr = '/' . $graphicState->getNumber() . ' gs';
732
		if ('transparent' !== $rules['background-color']) {
733
			$bgColor = [
734
				'q',
735
				$graphicStateStr,
736
				"1 0 0 1 $pdfX $pdfY cm",
737
				"{$rules['background-color'][0]} {$rules['background-color'][1]} {$rules['background-color'][2]} rg",
738
				"0 0 $width $height re",
739
				'f',
740
				'Q',
741
			];
742
			$element = array_merge($element, $bgColor);
743
		}
744
745
		return $element;
746
	}
747
748
	/**
749
	 * Add background color instructions.
750
	 *
751
	 * @param array $element
752
	 * @param $pdfX
753
	 * @param $pdfY
754
	 * @param $width
755
	 * @param $height
756
	 *
757
	 * @return array
758
	 */
759
	public function addBackgroundImageInstructions(array $element, $pdfX, $pdfY, $width, $height)
760
	{
761
		if (null === $this->getStyle()->getBackgroundImageStream()) {
762
			return $element;
763
		}
764
		$rules = $this->style->getRules();
765
		$graphicState = $this->style->getGraphicState();
766
		$graphicStateStr = '/' . $graphicState->getNumber() . ' gs';
767
		if ('transparent' !== $rules['background-image']) {
768
			$bgColor = [
769
				'q',
770
				$graphicStateStr,
771
				"1 0 0 1 $pdfX $pdfY cm",
772
				"$width 0 0 $height 0 0 cm",
773
				'/' . $this->getStyle()->getBackgroundImageStream()->getImageName() . ' Do',
774
				'Q',
775
			];
776
			$element = array_merge($element, $bgColor);
777
		}
778
779
		return $element;
780
	}
781
782
	/**
783
	 * Divide content into pages.
784
	 *
785
	 * @return $this
786
	 */
787
	public function breakPageAfter()
788
	{
789
		$breakAfter = [];
790
		$allChildren = [];
791
		$this->getAllChildren($allChildren);
792
		foreach ($allChildren as $child) {
793
			if ('always' === $child->getStyle()->getRules('page-break-after')) {
794
				$breakAfter[] = $child;
795
			}
796
		}
797
		foreach ($breakAfter as $breakBox) {
798
			$this->document->getCurrentPage()->breakAfter($breakBox);
799
		}
800
801
		return $this;
802
	}
803
804
	/**
805
	 * Get element PDF instructions to use in content stream.
806
	 *
807
	 * @return string
808
	 */
809
	public function getInstructions(): string
810
	{
811
		if ('none' === $this->getStyle()->getRules('display')) {
812
			return '';
813
		}
814
		$coordinates = $this->getCoordinates();
815
		$pdfX = $coordinates->getPdfX();
816
		$pdfY = $coordinates->getPdfY();
817
		$dimensions = $this->getDimensions();
818
		$width = $dimensions->getWidth();
819
		$height = $dimensions->getHeight();
820
		$element = [];
821
		$element = $this->addBackgroundColorInstructions($element, $pdfX, $pdfY, $width, $height);
822
		$element = $this->addBackgroundImageInstructions($element, $pdfX, $pdfY, $width, $height);
823
		$element = $this->addBorderInstructions($element, $pdfX, $pdfY, $width, $height);
0 ignored issues
show
Bug introduced by
It seems like $height can also be of type null; however, parameter $height of YetiForcePDF\Layout\Box::addBorderInstructions() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

823
		$element = $this->addBorderInstructions($element, $pdfX, $pdfY, $width, /** @scrutinizer ignore-type */ $height);
Loading history...
824
825
		return implode("\n", $element);
826
	}
827
}
828