Box::setDisplayable()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * Box 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 Box.
25
 */
26
class Box extends \YetiForcePDF\Base
27
{
28
	/**
29
	 * Id of this box (should be cloned to track inline wrapped elements).
30
	 *
31
	 * @var string
32
	 */
33
	protected $id;
34
	/**
35
	 * @var Box
36
	 */
37
	protected $parent;
38
	/**
39
	 * @var Box[]
40
	 */
41
	protected $children = [];
42
	/**
43
	 * @var Box
44
	 */
45
	protected $next;
46
	/**
47
	 * @var Box
48
	 */
49
	protected $previous;
50
	/** @var box dimensions */
51
	protected $dimensions;
52
	/**
53
	 * @var Coordinates
54
	 */
55
	protected $coordinates;
56
	/**
57
	 * @var Offset
58
	 */
59
	protected $offset;
60
	/**
61
	 * @var bool
62
	 */
63
	protected $root = false;
64
	/**
65
	 * @var Style
66
	 */
67
	protected $style;
68
	/**
69
	 * Anonymous inline element is created to wrap TextBox.
70
	 *
71
	 * @var bool
72
	 */
73
	protected $anonymous = false;
74
	/**
75
	 * @var bool do we need to measure this box ?
76
	 */
77
	protected $forMeasurement = true;
78
	/**
79
	 * @var bool is this element show up in view? take space?
80
	 */
81
	protected $renderable = true;
82
	/**
83
	 * @var array save state before it was unrenderable
84
	 */
85
	protected $renderableState = [];
86
	/**
87
	 * Is this box absolute positioned?
88
	 *
89
	 * @var bool
90
	 */
91
	protected $absolute = false;
92
	/**
93
	 * @var bool
94
	 */
95
	protected $cut = false;
96
	/**
97
	 * @var bool
98
	 */
99
	protected $displayable = true;
100
	/**
101
	 * Is this new group of pages?
102
	 *
103
	 * @var bool
104
	 */
105
	protected $pageGroup = false;
106
	/**
107
	 * @var array
108
	 */
109
	protected $options = [];
110
111
	/**
112
	 * {@inheritdoc}
113
	 */
114
	public function init()
115
	{
116
		parent::init();
117
		$this->id = uniqid();
118
		$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...
119
			->setDocument($this->document)
120
			->setBox($this)
121
			->init();
122
		$this->coordinates = (new Coordinates())
123
			->setDocument($this->document)
124
			->setBox($this)
125
			->init();
126
		$this->offset = (new Offset())
127
			->setDocument($this->document)
128
			->setBox($this)
129
			->init();
130
131
		return $this;
132
	}
133
134
	/**
135
	 * Get box id (id might be cloned and then we can track cloned elements).
136
	 *
137
	 * @return string
138
	 */
139
	public function getId()
140
	{
141
		return $this->id;
142
	}
143
144
	/**
145
	 * Get current box dom tree structure.
146
	 *
147
	 * @return \DOMElement
148
	 */
149
	public function getDOMTree()
150
	{
151
		return $this->domTree;
0 ignored issues
show
Bug Best Practice introduced by
The property domTree does not exist on YetiForcePDF\Layout\Box. Did you maybe forget to declare it?
Loading history...
152
	}
153
154
	/**
155
	 * Set parent.
156
	 *
157
	 * @param \YetiForcePDF\Layout\Box|null $parent
158
	 *
159
	 * @return $this
160
	 */
161
	public function setParent(self $parent = null)
162
	{
163
		$this->parent = $parent;
164
165
		return $this;
166
	}
167
168
	/**
169
	 * Get parent.
170
	 *
171
	 * @return \YetiForcePDF\Layout\Box
172
	 */
173
	public function getParent()
174
	{
175
		return $this->parent;
176
	}
177
178
	/**
179
	 * Set next.
180
	 *
181
	 * @param \YetiForcePDF\Layout\Box|null $next
182
	 *
183
	 * @return $this
184
	 */
185
	public function setNext(self $next = null)
186
	{
187
		$this->next = $next;
188
189
		return $this;
190
	}
191
192
	/**
193
	 * Get next.
194
	 *
195
	 * @return \YetiForcePDF\Layout\Box
196
	 */
197
	public function getNext()
198
	{
199
		return $this->next;
200
	}
201
202
	/**
203
	 * Set previous.
204
	 *
205
	 * @param \YetiForcePDF\Layout\Box|null $previous
206
	 *
207
	 * @return $this
208
	 */
209
	public function setPrevious(self $previous = null)
210
	{
211
		$this->previous = $previous;
212
213
		return $this;
214
	}
215
216
	/**
217
	 * Get previous.
218
	 *
219
	 * @return \YetiForcePDF\Layout\Box
220
	 */
221
	public function getPrevious()
222
	{
223
		return $this->previous;
224
	}
225
226
	/**
227
	 * Set root - is this root element?
228
	 *
229
	 * @param bool $isRoot
230
	 *
231
	 * @return $this
232
	 */
233
	public function setRoot(bool $isRoot)
234
	{
235
		$this->root = $isRoot;
236
237
		return $this;
238
	}
239
240
	/**
241
	 * Is this root element?
242
	 *
243
	 * @return bool
244
	 */
245
	public function isRoot(): bool
246
	{
247
		return $this->root;
248
	}
249
250
	/**
251
	 * Is this box absolute positioned?
252
	 *
253
	 * @return bool
254
	 */
255
	public function isAbsolute()
256
	{
257
		return $this->absolute;
258
	}
259
260
	/**
261
	 * Set absolute - this box will be absolute positioned.
262
	 *
263
	 * @param bool $absolute
264
	 *
265
	 * @return $this
266
	 */
267
	public function setAbsolute(bool $absolute)
268
	{
269
		$this->absolute = $absolute;
270
271
		return $this;
272
	}
273
274
	/**
275
	 * Box was cut to next page.
276
	 *
277
	 * @return bool
278
	 */
279
	public function wasCut()
280
	{
281
		return $this->cut;
282
	}
283
284
	/**
285
	 * Set cut.
286
	 *
287
	 * @param int $cut
288
	 *
289
	 * @return $this
290
	 */
291
	public function setCut(int $cut)
292
	{
293
		$this->cut = $cut;
0 ignored issues
show
Documentation Bug introduced by
The property $cut was declared of type boolean, but $cut is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
294
295
		return $this;
296
	}
297
298
	/**
299
	 * Is this element displayable.
300
	 *
301
	 * @return bool
302
	 */
303
	public function isDisplayable()
304
	{
305
		return $this->displayable;
306
	}
307
308
	/**
309
	 * Set displayable.
310
	 *
311
	 * @param bool $displayable
312
	 *
313
	 * @return $this
314
	 */
315
	public function setDisplayable(bool $displayable)
316
	{
317
		$allChildren = [];
318
		$this->getAllChildren($allChildren);
319
		foreach ($allChildren as $child) {
320
			$child->displayable = $displayable;
321
		}
322
323
		return $this;
324
	}
325
326
	/**
327
	 * Set style.
328
	 *
329
	 * @param \YetiForcePDF\Style\Style $style
330
	 * @param bool                      $init
331
	 *
332
	 * @return $this
333
	 */
334
	public function setStyle(Style $style, bool $init = true)
335
	{
336
		$this->style = $style;
337
		if ($element = $style->getElement()) {
338
			$element->setBox($this);
339
		}
340
		$style->setBox($this);
341
		if ($init) {
342
			$style->init();
343
		}
344
345
		return $this;
346
	}
347
348
	/**
349
	 * Get style.
350
	 *
351
	 * @return Style
352
	 */
353
	public function getStyle()
354
	{
355
		return $this->style;
356
	}
357
358
	/**
359
	 * Is this box anonymous.
360
	 *
361
	 * @return bool
362
	 */
363
	public function isAnonymous()
364
	{
365
		return $this->anonymous;
366
	}
367
368
	/**
369
	 * Set anonymous field.
370
	 *
371
	 * @param bool $anonymous
372
	 *
373
	 * @return $this
374
	 */
375
	public function setAnonymous(bool $anonymous)
376
	{
377
		$this->anonymous = $anonymous;
378
379
		return $this;
380
	}
381
382
	/**
383
	 * Set for measurement - enable or disable this box measurement.
384
	 *
385
	 * @param bool $forMeasure
386
	 * @param bool $forMeasurement
387
	 *
388
	 * @return $this
389
	 */
390
	public function setForMeasurement(bool $forMeasurement)
391
	{
392
		$allChildren = [];
393
		$this->getAllChildren($allChildren);
394
		foreach ($allChildren as $child) {
395
			$child->forMeasurement = $forMeasurement;
396
		}
397
398
		return $this;
399
	}
400
401
	/**
402
	 * Is this box available for measurement? or it should now have any width?
403
	 *
404
	 * @return bool
405
	 */
406
	public function isForMeasurement()
407
	{
408
		return $this->forMeasurement;
409
	}
410
411
	/**
412
	 * Save renderable state.
413
	 *
414
	 * @return $this
415
	 */
416
	protected function saveRenderableState()
417
	{
418
		$this->renderableState['styleRules'] = $this->getStyle()->getRules();
419
		$this->renderableState['width'] = $this->getDimensions()->getRawWidth();
420
		$this->renderableState['height'] = $this->getDimensions()->getRawHeight();
421
422
		return $this;
423
	}
424
425
	/**
426
	 * Restore renderable state.
427
	 *
428
	 * @return $this
429
	 */
430
	protected function restoreRenderableState()
431
	{
432
		$this->getStyle()->setRules($this->renderableState['styleRules']);
433
		if (isset($this->renderableState['width'])) {
434
			$this->getDimensions()->setWidth($this->renderableState['width']);
435
		}
436
		if (isset($this->renderableState['height'])) {
437
			$this->getDimensions()->setHeight($this->renderableState['height']);
438
		}
439
440
		return $this;
441
	}
442
443
	/**
444
	 * Hide this element - set width / height to 0 and remove all styles that will change width / height.
445
	 *
446
	 * @return $this
447
	 */
448
	protected function hide()
449
	{
450
		$this->getStyle()->setRules([
451
			'padding-top' => '0',
452
			'padding-right' => '0',
453
			'padding-bottom' => '0',
454
			'padding-left' => '0',
455
			'margin-top' => '0',
456
			'margin-right' => '0',
457
			'margin-bottom' => '0',
458
			'margin-left' => '0',
459
			'border-top-width' => '0',
460
			'border-right-width' => '0',
461
			'border-bottom-width' => '0',
462
			'border-left-width' => '0',
463
		]);
464
		$this->getDimensions()->setWidth('0');
465
		$this->getDimensions()->setHeight('0');
466
467
		return $this;
468
	}
469
470
	/**
471
	 * Set renderable.
472
	 *
473
	 * @param bool $renderable
474
	 * @param bool $forceUpdate
475
	 *
476
	 * @return $this
477
	 */
478
	public function setRenderable(bool $renderable = true, bool $forceUpdate = false)
479
	{
480
		$changed = $this->renderable !== $renderable;
481
		if (!$this->renderable && $renderable) {
482
			$this->restoreRenderableState();
483
		}
484
		if ($this->renderable && !$renderable) {
485
			$this->saveRenderableState();
486
			$this->hide();
487
		}
488
		if ($changed || $forceUpdate) {
489
			$this->renderable = $renderable;
490
			foreach ($this->getChildren() as $child) {
491
				$child->setRenderable($renderable, $forceUpdate);
492
			}
493
		}
494
495
		return $this;
496
	}
497
498
	/**
499
	 * Contain content - do we have some elements that are not whitespace characters?
500
	 *
501
	 * @return bool
502
	 */
503
	public function containContent()
504
	{
505
		if ($this instanceof TextBox && '' === trim($this->getTextContent())) {
506
			return false;
507
		}
508
		// we are not text node - traverse further
509
		$children = $this->getChildren();
510
		if (0 === \count($children) && !$this instanceof LineBox) {
511
			return true; // we are the content
512
		}
513
		if (0 === \count($children) && $this instanceof LineBox) {
514
			return false; // we are the content
515
		}
516
		$childrenContent = false;
517
		foreach ($children as $child) {
518
			$childrenContent = $childrenContent || $child->containContent();
519
		}
520
521
		return $childrenContent;
522
	}
523
524
	/**
525
	 * Is this element renderable?
526
	 *
527
	 * @return bool
528
	 */
529
	public function isRenderable()
530
	{
531
		return $this->renderable;
532
	}
533
534
	/**
535
	 * Append child box - line box can have only inline/block boxes - not line boxes!
536
	 *
537
	 * @param Box $box
538
	 *
539
	 * @return $this
540
	 */
541
	public function appendChild(self $box)
542
	{
543
		$box->setParent($this);
544
		$childrenCount = \count($this->children);
545
		if ($childrenCount > 0) {
546
			$previous = $this->children[$childrenCount - 1];
547
			$box->setPrevious($previous);
548
			$previous->setNext($box);
549
		} else {
550
			$box->setPrevious()->setNext();
551
		}
552
		$this->children[] = $box;
553
554
		return $this;
555
	}
556
557
	/**
558
	 * Remove child.
559
	 *
560
	 * @param $child
561
	 *
562
	 * @return Box
563
	 */
564
	public function removeChild(self $child)
565
	{
566
		$oldChildren = $this->children; // copy children
567
		$this->children = [];
568
		foreach ($oldChildren as $currentChild) {
569
			if ($currentChild !== $child) {
570
				$this->appendChild($currentChild);
571
			}
572
		}
573
		if ($child->getPrevious()) {
574
			if ($child->getNext()) {
575
				$child->getPrevious()->setNext($child->getNext());
576
			} else {
577
				$child->getPrevious()->setNext();
578
			}
579
		}
580
		if ($child->getNext()) {
581
			if ($child->getPrevious()) {
582
				$child->getNext()->setPrevious($child->getPrevious());
583
			} else {
584
				$child->getNext()->setPrevious();
585
			}
586
		}
587
		$child->setParent()->setPrevious()->setNext();
588
589
		return $child;
590
	}
591
592
	/**
593
	 * Just clear children without changing associations for them.
594
	 *
595
	 * @return $this
596
	 */
597
	public function clearChildren()
598
	{
599
		$this->children = [];
600
601
		return $this;
602
	}
603
604
	/**
605
	 * Remove all children.
606
	 *
607
	 * @return $this
608
	 */
609
	public function removeChildren()
610
	{
611
		foreach ($this->getChildren() as $child) {
612
			$child->removeChildren();
613
			$this->removeChild($child);
614
		}
615
616
		return $this;
617
	}
618
619
	/**
620
	 * Insert box before other box.
621
	 *
622
	 * @param \YetiForcePDF\Layout\Box $child
623
	 * @param \YetiForcePDF\Layout\Box $before
624
	 *
625
	 * @return $this
626
	 */
627
	public function insertBefore(self $child, self $before)
628
	{
629
		$currentChildren = $this->children; // copy children
630
		$this->children = [];
631
		foreach ($currentChildren as $currentChild) {
632
			if ($currentChild === $before) {
633
				$this->appendChild($child);
634
				$this->appendChild($currentChild);
635
			} else {
636
				$this->appendChild($currentChild);
637
			}
638
		}
639
640
		return $this;
641
	}
642
643
	/**
644
	 * Get children.
645
	 *
646
	 * @param bool $onlyRenderable
647
	 * @param bool $onlyForMeasurment
648
	 *
649
	 * @return Box[]
650
	 */
651
	public function getChildren(bool $onlyRenderable = false, bool $onlyForMeasurment = false): array
652
	{
653
		if (!$onlyRenderable && !$onlyForMeasurment) {
654
			return $this->children;
655
		}
656
		$children = [];
657
		foreach ($this->children as $box) {
658
			if ($onlyRenderable && $onlyForMeasurment && !($box->isRenderable() && $box->isForMeasurement())) {
659
				continue;
660
			}
661
			if ($onlyRenderable && !$box->isRenderable()) {
662
				continue;
663
			}
664
			if (!$box->isForMeasurement()) {
665
				continue;
666
			}
667
			$children[] = $box;
668
		}
669
670
		return $children;
671
	}
672
673
	/**
674
	 * Get all children.
675
	 *
676
	 * @param Box[] $allChildren
677
	 * @param bool  $withCurrent
678
	 *
679
	 * @return Box[]
680
	 */
681
	public function getAllChildren(&$allChildren = [], bool $withCurrent = true)
682
	{
683
		if ($withCurrent) {
684
			$allChildren[] = $this;
685
		}
686
		foreach ($this->getChildren() as $child) {
687
			$child->getAllChildren($allChildren);
688
		}
689
690
		return $allChildren;
691
	}
692
693
	/**
694
	 * Iterate all children.
695
	 *
696
	 * @param callable $fn
697
	 * @param bool     $reverse
698
	 * @param bool     $deep
699
	 *
700
	 * @return $this
701
	 */
702
	public function iterateChildren(callable $fn, bool $reverse = false, bool $deep = true)
703
	{
704
		$allChildren = [];
705
		if ($deep) {
706
			$this->getAllChildren($allChildren);
707
		} else {
708
			$allChildren = $this->getChildren();
709
		}
710
		if ($reverse) {
711
			$allChildren = array_reverse($allChildren);
712
		}
713
		foreach ($allChildren as $child) {
714
			if (false === $fn($child)) {
715
				break;
716
			}
717
		}
718
719
		return $this;
720
	}
721
722
	/**
723
	 * Get boxes by type.
724
	 *
725
	 * @param string      $shortClassName
726
	 * @param string|null $until
727
	 *
728
	 * @return array
729
	 */
730
	public function getBoxesByType(string $shortClassName, string $until = '')
731
	{
732
		$boxes = [];
733
		$untilWas = 0;
734
		$allChildren = [];
735
		$this->getAllChildren($allChildren);
736
		foreach ($allChildren as $child) {
737
			$reflectShortClassName = (new \ReflectionClass($child))->getShortName();
738
			if ($reflectShortClassName === $shortClassName) {
739
				$boxes[] = $child;
740
			}
741
			if ($reflectShortClassName === $until) {
742
				++$untilWas;
743
			}
744
			if ($reflectShortClassName === $until && 2 === $untilWas) {
745
				break;
746
			}
747
		}
748
749
		return $boxes;
750
	}
751
752
	/**
753
	 * Get closest parent box by type.
754
	 *
755
	 * @param string $className
756
	 *
757
	 * @return Box
758
	 */
759
	public function getClosestByType(string $className)
760
	{
761
		if ('YetiForce' !== substr($className, 0, 9)) {
762
			$className = 'YetiForcePDF\\Layout\\' . $className;
763
		}
764
		if ($this instanceof $className) {
765
			return $this;
766
		}
767
		if ($this->getParent() instanceof $className) {
768
			return $this->getParent();
769
		}
770
		if ($this->getParent()->isRoot()) {
771
			return null;
772
		}
773
774
		return $this->getParent()->getClosestByType($className);
775
	}
776
777
	/**
778
	 * Do we have children?
779
	 *
780
	 * @return bool
781
	 */
782
	public function hasChildren()
783
	{
784
		return isset($this->children[0]); // faster than count
785
	}
786
787
	/**
788
	 * Get first child.
789
	 *
790
	 * @return \YetiForcePDF\Layout\Box|null
791
	 */
792
	public function getFirstChild()
793
	{
794
		if (isset($this->children[0])) {
795
			return $this->children[0];
796
		}
797
	}
798
799
	/**
800
	 * Get last child.
801
	 *
802
	 * @return \YetiForcePDF\Layout\Box|null
803
	 */
804
	public function getLastChild()
805
	{
806
		if ($count = \count($this->children)) {
807
			return $this->children[$count - 1];
808
		}
809
	}
810
811
	/**
812
	 * Get closest line box.
813
	 *
814
	 * @return \YetiForcePDF\Layout\LineBox
815
	 */
816
	public function getClosestLineBox()
817
	{
818
		$parent = $this->getParent();
819
		if ($parent instanceof LineBox) {
820
			return $parent;
821
		}
822
823
		return $parent->getClosestLineBox();
824
	}
825
826
	/**
827
	 * Get closet box that is not a LineBox.
828
	 *
829
	 * @return \YetiForcePDF\Layout\Box
830
	 */
831
	public function getClosestBox()
832
	{
833
		$parent = $this->getParent();
834
		if (!$parent instanceof LineBox) {
835
			return $parent;
836
		}
837
838
		return $parent->getClosestBox();
839
	}
840
841
	/**
842
	 * Get dimensions.
843
	 *
844
	 * @return BoxDimensions
845
	 */
846
	public function getDimensions()
847
	{
848
		return $this->dimensions;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->dimensions returns the type YetiForcePDF\Layout\box which is incompatible with the documented return type YetiForcePDF\Layout\Dimensions\BoxDimensions.
Loading history...
849
	}
850
851
	/**
852
	 * Get coordinates.
853
	 *
854
	 * @return Coordinates
855
	 */
856
	public function getCoordinates()
857
	{
858
		return $this->coordinates;
859
	}
860
861
	/**
862
	 * Shorthand for offset.
863
	 *
864
	 * @return Offset
865
	 */
866
	public function getOffset(): Offset
867
	{
868
		return $this->offset;
869
	}
870
871
	/**
872
	 * Get first root child - closest parent that are child of root node.
873
	 *
874
	 * @return Box
875
	 */
876
	public function getFirstRootChild()
877
	{
878
		if ($this->isRoot()) {
879
			return $this;
880
		}
881
		if (null !== $this->getParent()) {
882
			$box = $this->getParent();
883
			if ($box->isRoot()) {
884
				return $this;
885
			}
886
887
			return $box->getFirstRootChild();
888
		}
889
	}
890
891
	/**
892
	 * Get text content from current and all nested boxes.
893
	 *
894
	 * @return string
895
	 */
896
	public function getTextContent()
897
	{
898
		$textContent = '';
899
		$allChildren = [];
900
		$this->getAllChildren($allChildren);
901
		foreach ($allChildren as $box) {
902
			if ($box instanceof TextBox) {
903
				$textContent .= $box->getText();
904
			}
905
		}
906
907
		return $textContent;
908
	}
909
910
	/**
911
	 * Get first child text box.
912
	 *
913
	 * @return \YetiForcePDF\Layout\TextBox|null
914
	 */
915
	public function getFirstTextBox()
916
	{
917
		if ($this instanceof TextBox) {
918
			return $this;
919
		}
920
		foreach ($this->getChildren() as $child) {
921
			if ($child instanceof TextBox) {
922
				return $child;
923
			}
924
925
			return $child->getFirstTextBox();
926
		}
927
	}
928
929
	/**
930
	 * Should we break page after this element?
931
	 *
932
	 * @return bool
933
	 */
934
	public function shouldBreakPage()
935
	{
936
		return false; // only block boxes should break the page
937
	}
938
939
	/**
940
	 * Set marker that this is a new group of pages and should have new numbering.
941
	 *
942
	 * @param bool $pageGroup
943
	 *
944
	 * @return $this
945
	 */
946
	public function setPageGroup(bool $pageGroup)
947
	{
948
		$this->pageGroup = true;
949
950
		return $this;
951
	}
952
953
	/**
954
	 * Get force page number.
955
	 *
956
	 * @return int
957
	 */
958
	public function getPageGroup()
959
	{
960
		return $this->pageGroup;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->pageGroup returns the type boolean which is incompatible with the documented return type integer.
Loading history...
961
	}
962
963
	/**
964
	 * Set option (page group for example).
965
	 *
966
	 * @param string $name
967
	 * @param string $value
968
	 *
969
	 * @return $this
970
	 */
971
	public function setOption(string $name, string $value)
972
	{
973
		$this->options[$name] = $value;
974
975
		return $this;
976
	}
977
978
	/**
979
	 * Get option.
980
	 *
981
	 * @param string $name
982
	 *
983
	 * @return string
984
	 */
985
	public function getOption(string $name)
986
	{
987
		if (isset($this->options[$name])) {
988
			return $this->options[$name];
989
		}
990
991
		return '';
992
	}
993
994
	/**
995
	 * Get height from style.
996
	 *
997
	 * @return $this
998
	 */
999
	public function applyStyleWidth()
1000
	{
1001
		$styleWidth = $this->getDimensions()->getStyleWidth();
1002
		if (null !== $styleWidth) {
1003
			$this->getDimensions()->setWidth($styleWidth);
0 ignored issues
show
Bug introduced by
It seems like $styleWidth can also be of type array; however, parameter $width of YetiForcePDF\Layout\Dime...\Dimensions::setWidth() does only seem to accept null|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

1003
			$this->getDimensions()->setWidth(/** @scrutinizer ignore-type */ $styleWidth);
Loading history...
1004
		}
1005
1006
		return $this;
1007
	}
1008
1009
	/**
1010
	 * Get height from style.
1011
	 *
1012
	 * @return $this
1013
	 */
1014
	public function applyStyleHeight()
1015
	{
1016
		$height = $this->getStyle()->getRules('height');
1017
		if ('auto' === $height) {
1018
			return $this;
1019
		}
1020
		$percentPos = strpos($height, '%');
0 ignored issues
show
Bug introduced by
It seems like $height can also be of type array; however, parameter $haystack of strpos() 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

1020
		$percentPos = strpos(/** @scrutinizer ignore-type */ $height, '%');
Loading history...
1021
		if (false !== $percentPos) {
1022
			$heightInPercent = substr($height, 0, $percentPos);
0 ignored issues
show
Bug introduced by
It seems like $height can also be of type array; however, parameter $string of substr() 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

1022
			$heightInPercent = substr(/** @scrutinizer ignore-type */ $height, 0, $percentPos);
Loading history...
1023
			$parentDimensions = $this->getParent()->getDimensions();
1024
			if (null !== $parentDimensions->getHeight()) {
1025
				$parentHeight = $this->getParent()->getDimensions()->getInnerHeight();
1026
				if ($parentHeight) {
1027
					$calculatedHeight = Math::mul(Math::div($parentHeight, '100'), $heightInPercent);
1028
					$this->getDimensions()->setHeight($calculatedHeight);
1029
1030
					return $this;
1031
				}
1032
			}
1033
1034
			return $this;
1035
		}
1036
		$this->getDimensions()->setHeight($height);
0 ignored issues
show
Bug introduced by
It seems like $height can also be of type array; however, parameter $height of YetiForcePDF\Layout\Dime...Dimensions::setHeight() does only seem to accept null|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

1036
		$this->getDimensions()->setHeight(/** @scrutinizer ignore-type */ $height);
Loading history...
1037
1038
		return $this;
1039
	}
1040
1041
	/**
1042
	 * Fix offsets inside lines where text-align !== 'left'.
1043
	 *
1044
	 * @return $this
1045
	 */
1046
	public function alignText()
1047
	{
1048
		if ($this instanceof LineBox) {
1049
			$textAlign = $this->getParent()->getStyle()->getRules('text-align');
1050
			if ('right' === $textAlign) {
1051
				$offset = Math::sub($this->getDimensions()->computeAvailableSpace(), $this->getChildrenWidth());
1052
				foreach ($this->getChildren() as $childBox) {
1053
					$childBox->getOffset()->setLeft(Math::add($childBox->getOffset()->getLeft(), $offset));
1054
				}
1055
			} elseif ('center' === $textAlign) {
1056
				$offset = Math::sub(Math::div($this->getDimensions()->computeAvailableSpace(), '2'), Math::div($this->getChildrenWidth(), '2'));
1057
				foreach ($this->getChildren() as $childBox) {
1058
					$childBox->getOffset()->setLeft(Math::add($childBox->getOffset()->getLeft(), $offset));
1059
				}
1060
			}
1061
		} else {
1062
			foreach ($this->getChildren() as $child) {
1063
				$child->alignText();
1064
			}
1065
		}
1066
1067
		return $this;
1068
	}
1069
1070
	/**
1071
	 * Add border instructions.
1072
	 *
1073
	 * @param array  $element
1074
	 * @param string $pdfX
1075
	 * @param string $pdfY
1076
	 * @param string $width
1077
	 * @param string $height
1078
	 *
1079
	 * @return array
1080
	 */
1081
	protected function addBorderInstructions(array $element, string $pdfX, string $pdfY, string $width, string $height)
1082
	{
1083
		if ('none' === $this->getStyle()->getRules('display')) {
1084
			return $element;
1085
		}
1086
		$style = $this->style;
1087
		$graphicState = $this->style->getGraphicState();
1088
		$graphicStateStr = '/' . $graphicState->getNumber() . ' gs';
1089
		$x1 = '0';
1090
		$x2 = $width;
1091
		$y1 = $height;
1092
		$y2 = '0';
1093
		$element[] = '% start border';
1094
		if ($style->getRules('border-top-width') && 'none' !== $style->getRules('border-top-style') && 'transparent' !== $style->getRules('border-top-color')) {
1095
			$path = implode(" l\n", [
1096
				implode(' ', [$x2, $y1]),
1097
				implode(' ', [Math::sub($x2, $style->getRules('border-right-width')), Math::sub($y1, $style->getRules('border-top-width'))]),
0 ignored issues
show
Bug introduced by
It seems like $style->getRules('border-right-width') can also be of type array; however, parameter $numbers of YetiForcePDF\Math::sub() 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

1097
				implode(' ', [Math::sub($x2, /** @scrutinizer ignore-type */ $style->getRules('border-right-width')), Math::sub($y1, $style->getRules('border-top-width'))]),
Loading history...
1098
				implode(' ', [Math::add($x1, $style->getRules('border-left-width')), Math::sub($y1, $style->getRules('border-top-width'))]),
0 ignored issues
show
Bug introduced by
It seems like $style->getRules('border-left-width') 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

1098
				implode(' ', [Math::add($x1, /** @scrutinizer ignore-type */ $style->getRules('border-left-width')), Math::sub($y1, $style->getRules('border-top-width'))]),
Loading history...
1099
				implode(' ', [$x1, $y1]),
1100
			]);
1101
			$borderTop = [
1102
				'q',
1103
				$graphicStateStr,
1104
				"{$style->getRules('border-top-color')[0]} {$style->getRules('border-top-color')[1]} {$style->getRules('border-top-color')[2]} rg",
1105
				"1 0 0 1 $pdfX $pdfY cm",
1106
				"$x1 $y1 m", // move to start point
1107
				$path . ' l h',
1108
				'f',
1109
				'Q',
1110
			];
1111
			$element = array_merge($element, $borderTop);
1112
		}
1113
		if ($style->getRules('border-right-width') && 'none' !== $style->getRules('border-right-style') && 'transparent' !== $style->getRules('border-right-color')) {
1114
			$path = implode(" l\n", [
1115
				implode(' ', [$x2, $y2]),
1116
				implode(' ', [Math::sub($x2, $style->getRules('border-right-width')), Math::add($y2, $style->getRules('border-bottom-width'))]),
1117
				implode(' ', [Math::sub($x2, $style->getRules('border-right-width')), Math::sub($y1, $style->getRules('border-top-width'))]),
1118
				implode(' ', [$x2, $y1]),
1119
			]);
1120
			$borderTop = [
1121
				'q',
1122
				$graphicStateStr,
1123
				"1 0 0 1 $pdfX $pdfY cm",
1124
				"{$style->getRules('border-right-color')[0]} {$style->getRules('border-right-color')[1]} {$style->getRules('border-right-color')[2]} rg",
1125
				"$x2 $y1 m",
1126
				$path . ' l h',
1127
				'f',
1128
				'Q',
1129
			];
1130
			$element = array_merge($element, $borderTop);
1131
		}
1132
		if ($style->getRules('border-bottom-width') && 'none' !== $style->getRules('border-bottom-style') && 'transparent' !== $style->getRules('border-bottom-color')) {
1133
			$path = implode(" l\n", [
1134
				implode(' ', [$x2, $y2]),
1135
				implode(' ', [Math::sub($x2, $style->getRules('border-right-width')), Math::add($y2, $style->getRules('border-bottom-width'))]),
1136
				implode(' ', [Math::add($x1, $style->getRules('border-left-width')), Math::add($y2, $style->getRules('border-bottom-width'))]),
1137
				implode(' ', [$x1, $y2]),
1138
			]);
1139
			$borderTop = [
1140
				'q',
1141
				$graphicStateStr,
1142
				"1 0 0 1 $pdfX $pdfY cm",
1143
				"{$style->getRules('border-bottom-color')[0]} {$style->getRules('border-bottom-color')[1]} {$style->getRules('border-bottom-color')[2]} rg",
1144
				"$x1 $y2 m",
1145
				$path . ' l h',
1146
				'f',
1147
				'Q',
1148
			];
1149
			$element = array_merge($element, $borderTop);
1150
		}
1151
		if ($style->getRules('border-left-width') && 'none' !== $style->getRules('border-left-style') && 'transparent' !== $style->getRules('border-left-color')) {
1152
			$path = implode(" l\n", [
1153
				implode(' ', [Math::add($x1, $style->getRules('border-left-width')), Math::sub($y1, $style->getRules('border-top-width'))]),
1154
				implode(' ', [Math::add($x1, $style->getRules('border-left-width')), Math::add($y2, $style->getRules('border-bottom-width'))]),
1155
				implode(' ', [$x1, $y2]),
1156
				implode(' ', [$x1, $y1]),
1157
			]);
1158
			$borderTop = [
1159
				'q',
1160
				$graphicStateStr,
1161
				"1 0 0 1 $pdfX $pdfY cm",
1162
				"{$style->getRules('border-left-color')[0]} {$style->getRules('border-left-color')[1]} {$style->getRules('border-left-color')[2]} rg",
1163
				"$x1 $y1 m",
1164
				$path . ' l h',
1165
				'f',
1166
				'Q',
1167
			];
1168
			$element = array_merge($element, $borderTop);
1169
		}
1170
		$element[] = '% end border';
1171
1172
		return $element;
1173
	}
1174
1175
	/**
1176
	 * Clone this element along with the children elements.
1177
	 *
1178
	 * @throws \InvalidArgumentException
1179
	 *
1180
	 * @return Box
1181
	 */
1182
	public function cloneWithChildren()
1183
	{
1184
		$newBox = $this->clone();
1185
		$newBox->getCoordinates()->setBox($newBox);
1186
		$newBox->getDimensions()->setBox($newBox);
1187
		$newBox->getOffset()->setBox($newBox);
1188
		$newBox->getStyle()->setBox($newBox);
1189
		$newBox->clearChildren();
1190
		foreach ($this->getChildren() as $child) {
1191
			$clonedChild = $child->cloneWithChildren();
1192
			$newBox->appendChild($clonedChild->getParent()->removeChild($clonedChild));
1193
		}
1194
1195
		return $newBox;
1196
	}
1197
1198
	public function clone()
1199
	{
1200
		$newBox = clone $this;
1201
		$newBox->dimensions->setBox($newBox);
0 ignored issues
show
Bug introduced by
The method setBox() does not exist on YetiForcePDF\Layout\Box. ( Ignorable by Annotation )

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

1201
		$newBox->dimensions->/** @scrutinizer ignore-call */ 
1202
                       setBox($newBox);

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...
1202
		$newBox->coordinates->setBox($newBox);
1203
		$newBox->offset->setBox($newBox);
1204
		$newBox->style->setBox($newBox);
1205
1206
		return $newBox;
1207
	}
1208
1209
	public function __clone()
1210
	{
1211
		if (isset($this->element)) {
1212
			$this->element = clone $this->element;
0 ignored issues
show
Bug Best Practice introduced by
The property element does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1213
			$this->element->setBox($this);
1214
		}
1215
		$this->dimensions = clone $this->dimensions;
1216
		$this->dimensions->setBox($this);
1217
		$this->coordinates = clone $this->coordinates;
1218
		$this->coordinates->setBox($this);
1219
		$this->offset = clone $this->offset;
1220
		$this->offset->setBox($this);
1221
		$this->style = $this->style->clone($this);
1222
	}
1223
}
1224