Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
91 / 91
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
LayoutContext
100.00% covered (success)
100.00%
91 / 91
100.00% covered (success)
100.00%
8 / 8
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withOrigin
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 withContainingBlock
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 withContainingBlockHeightDefinite
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 withLengthContext
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 withFloatContext
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 withPositionedAncestor
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 withParentWritingMode
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\HtmlToPdf\Layout;
6
7use Phpdftk\Css\Cascade\LengthContext;
8use Phpdftk\Css\Cascade\WritingMode;
9use Phpdftk\FontParser\FontFaceData;
10
11/**
12 * Per-layout-step context: the containing block's content width / height,
13 * the current X / Y origin where the next child will go, the
14 * `LengthContext` used by the CSS resolver for em / rem / vw / vh / %, and
15 * the default font supplied to {@see InlineLayout} for text measurement.
16 *
17 * Layout creates child contexts (via `with*()`) when it descends into a
18 * box, shifting origin and updating the containing-block measurements so
19 * `%` resolves correctly.
20 *
21 * `defaultFont` is null when no font is wired in yet — in that case
22 * inline layout falls back to producing zero-height placeholders so
23 * block layout can still run end-to-end. Hosts that want real typography
24 * provide a parsed `FontFaceData` here (see `phpdftk/font-parser`).
25 */
26final readonly class LayoutContext
27{
28    public function __construct(
29        public float $containingBlockWidth,
30        public float $containingBlockHeight,
31        public float $originX,
32        public float $originY,
33        public LengthContext $lengthContext,
34        public ?FontFaceData $defaultFont = null,
35        /**
36         * Optional multi-font selector. When set, `InlineLayout` picks the
37         * shaping font per box from this resolver — falling back to
38         * `defaultFont` when no `font-family` matches.
39         */
40        public ?FontResolver $fontResolver = null,
41        /**
42         * Tracks active floats per CSS 2.1 §9.5 for the current block
43         * formatting context. `InlineLayout` queries this to shorten
44         * line boxes that overlap a float's vertical extent. Null when
45         * no BFC has registered any floats yet.
46         */
47        public ?FloatContext $floatContext = null,
48        /**
49         * CSS 2.1 §10.1 — the containing block for `position:
50         * absolute` / `fixed` descendants is the nearest positioned
51         * ancestor's PADDING box, not the immediate parent. We thread
52         * that ancestor's padding-box rectangle here so abs-pos
53         * layout can read it directly. Null when no positioned
54         * ancestor is established yet — `BlockLayout` falls back to
55         * the initial containing block (the canvas).
56         */
57        public ?PositionedAncestor $positionedAncestor = null,
58        /**
59         * CSS 2.1 §10.5 + CSS Position 3 §3.4 — whether
60         * `$containingBlockHeight` is the spec's "definite height"
61         * (resolves percentage `top` / `bottom` / `height` directly)
62         * or "indefinite" (those percentages resolve to 0). The
63         * viewport at the root is always definite; descending through
64         * an auto-height intermediate block breaks the chain. The
65         * `<html>` and `<body>` elements are special-cased to inherit
66         * the viewport per the HTML rendering rules.
67         */
68        public bool $containingBlockHeightDefinite = true,
69        /**
70         * CSS Writing Modes 4 §7.4 — the containing block's writing
71         * mode determines which axis is the inline-axis for
72         * percentage resolution of margin / padding. In `horizontal-
73         * tb` the inline axis is x, so percentages resolve against
74         * `containingBlockWidth`; in `vertical-*` the inline axis is
75         * y, so they resolve against `containingBlockHeight`. Set
76         * by the parent when it dispatches children; null at the
77         * root (initial value = `horizontal-tb`, the default basis).
78         */
79        public ?WritingMode $parentWritingMode = null,
80    ) {}
81
82    public function withOrigin(float $x, float $y): self
83    {
84        return new self(
85            $this->containingBlockWidth,
86            $this->containingBlockHeight,
87            $x,
88            $y,
89            $this->lengthContext,
90            $this->defaultFont,
91            $this->fontResolver,
92            $this->floatContext,
93            $this->positionedAncestor,
94            $this->containingBlockHeightDefinite,
95            $this->parentWritingMode,
96        );
97    }
98
99    public function withContainingBlock(float $width, float $height): self
100    {
101        return new self(
102            $width,
103            $height,
104            $this->originX,
105            $this->originY,
106            $this->lengthContext,
107            $this->defaultFont,
108            $this->fontResolver,
109            $this->floatContext,
110            $this->positionedAncestor,
111            $this->containingBlockHeightDefinite,
112            $this->parentWritingMode,
113        );
114    }
115
116    public function withContainingBlockHeightDefinite(float $height, bool $definite): self
117    {
118        return new self(
119            $this->containingBlockWidth,
120            $height,
121            $this->originX,
122            $this->originY,
123            $this->lengthContext,
124            $this->defaultFont,
125            $this->fontResolver,
126            $this->floatContext,
127            $this->positionedAncestor,
128            $definite,
129        );
130    }
131
132    public function withLengthContext(LengthContext $ctx): self
133    {
134        return new self(
135            $this->containingBlockWidth,
136            $this->containingBlockHeight,
137            $this->originX,
138            $this->originY,
139            $ctx,
140            $this->defaultFont,
141            $this->fontResolver,
142            $this->floatContext,
143            $this->positionedAncestor,
144            $this->containingBlockHeightDefinite,
145            $this->parentWritingMode,
146        );
147    }
148
149    public function withFloatContext(?FloatContext $ctx): self
150    {
151        return new self(
152            $this->containingBlockWidth,
153            $this->containingBlockHeight,
154            $this->originX,
155            $this->originY,
156            $this->lengthContext,
157            $this->defaultFont,
158            $this->fontResolver,
159            $ctx,
160            $this->positionedAncestor,
161            $this->containingBlockHeightDefinite,
162            $this->parentWritingMode,
163        );
164    }
165
166    public function withPositionedAncestor(?PositionedAncestor $pa): self
167    {
168        return new self(
169            $this->containingBlockWidth,
170            $this->containingBlockHeight,
171            $this->originX,
172            $this->originY,
173            $this->lengthContext,
174            $this->defaultFont,
175            $this->fontResolver,
176            $this->floatContext,
177            $pa,
178            $this->containingBlockHeightDefinite,
179            $this->parentWritingMode,
180        );
181    }
182
183    public function withParentWritingMode(?WritingMode $wm): self
184    {
185        return new self(
186            $this->containingBlockWidth,
187            $this->containingBlockHeight,
188            $this->originX,
189            $this->originY,
190            $this->lengthContext,
191            $this->defaultFont,
192            $this->fontResolver,
193            $this->floatContext,
194            $this->positionedAncestor,
195            $this->containingBlockHeightDefinite,
196            $wm,
197        );
198    }
199}