Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
91 / 91
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
LengthContext
100.00% covered (success)
100.00%
91 / 91
100.00% covered (success)
100.00%
7 / 7
7
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
 withRootFontSize
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 withCurrentFontSize
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 withPercentageBasis
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 withFontMetrics
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 withContainerSize
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 withLineHeight
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Cascade;
6
7/**
8 * Context needed to resolve relative lengths (em/rem/%/vw/vh/ex/ch/lh/rlh)
9 * to absolute pixels.
10 *
11 * - `parentFontSize` — used when resolving `em` on the `font-size` property
12 *   itself (parent's font-size is the reference).
13 * - `currentFontSize` — used for everything else; the resolved font-size of
14 *   the element being computed. Two-pass cascade: resolve font-size first
15 *   with `parentFontSize`, then resolve other lengths using the result.
16 * - `rootFontSize` — `rem` reference; the document root's font-size.
17 * - `viewportWidth` / `viewportHeight` — `vw` / `vh` references in CSS pixels.
18 * - `percentageBasis` — the basis a `%` length resolves against; depends on
19 *   the property (width: containing-block width; line-height: own font-size,
20 *   etc.). Callers pass per-property as needed.
21 */
22final readonly class LengthContext
23{
24    public function __construct(
25        public float $parentFontSize = 16.0,
26        public float $currentFontSize = 16.0,
27        public float $rootFontSize = 16.0,
28        public float $viewportWidth = 816.0,    // 8.5in × 96 DPI
29        public float $viewportHeight = 1056.0,  // 11in × 96 DPI
30        public float $percentageBasis = 0.0,
31        /**
32         * x-height as a fraction of the em-square. CSS Values 4 §6.1.1
33         * defines `ex` as the x-height of the element's first
34         * available font; without font metrics we approximate with
35         * `0.5em` (a sensible fallback for sans-serif designs).
36         * Layout code that has access to the resolved font passes
37         * `font.xHeight / font.unitsPerEm` here so `ex` for fonts
38         * like Ahem (xHeight = full em) resolves correctly.
39         */
40        public float $xHeightRatio = 0.5,
41        /**
42         * Width of the `0` (ZERO) glyph as a fraction of the em-square.
43         * CSS Values 4 §6.1.1 — `ch` is the advance of `0`. The same
44         * 0.5 fallback applies when font metrics aren't reachable.
45         */
46        public float $chWidthRatio = 0.5,
47        /**
48         * Cap-height as a fraction of the em-square. CSS Values 4 §6.1.1 —
49         * `cap` is the cap-height of the element's first available font.
50         * Without font metrics we approximate with `0.7em`; layout code with
51         * the resolved font passes `font.capHeight / font.unitsPerEm`.
52         */
53        public float $capHeightRatio = 0.7,
54        /**
55         * Inline / block size of the nearest size-query container
56         * (`container-type: size` for both; `inline-size` populates
57         * only the inline axis). CSS Containment 3 §6 — `cqw` / `cqi`
58         * resolve against `containerInlineSize`; `cqh` / `cqb` against
59         * `containerBlockSize`; `cqmin` / `cqmax` against the smaller
60         * / larger of the two. Zero by default so cq* units resolve
61         * to 0 outside any size container per §6.3.
62         */
63        public float $containerInlineSize = 0.0,
64        public float $containerBlockSize = 0.0,
65        /**
66         * The element's used `line-height` in CSS pixels — the reference
67         * for the `lh` unit (CSS Values 4 §6.1.1). Zero means "not yet
68         * resolved / `normal`", in which case {@see LengthResolver} falls
69         * back to the `normal` approximation (`currentFontSize × 1.2`).
70         */
71        public float $lineHeight = 0.0,
72        /**
73         * CSS Sizing 3 §5.1 — when computing a box's intrinsic-size
74         * contribution, percentage padding / margin (and any `%` term
75         * inside a `calc()`) resolve to ZERO rather than deferring against
76         * an unknown basis. Set true only by intrinsic-sizing callers; the
77         * default keeps the normal "zero basis = unknown → NaN" behaviour so
78         * ordinary percentage resolution is untouched.
79         */
80        public bool $percentagesAsZero = false,
81    ) {}
82
83    /**
84     * CSS Values 4 §6.1 — the `rem` reference. Set once from the
85     * document root's own resolved `font-size` and then propagated
86     * unchanged down the tree.
87     */
88    public function withRootFontSize(float $px): self
89    {
90        return new self(
91            $this->parentFontSize,
92            $this->currentFontSize,
93            $px,
94            $this->viewportWidth,
95            $this->viewportHeight,
96            $this->percentageBasis,
97            $this->xHeightRatio,
98            $this->chWidthRatio,
99            $this->capHeightRatio,
100            $this->containerInlineSize,
101            $this->containerBlockSize,
102            $this->lineHeight,
103            $this->percentagesAsZero,
104        );
105    }
106
107    public function withCurrentFontSize(float $px): self
108    {
109        return new self(
110            $this->parentFontSize,
111            $px,
112            $this->rootFontSize,
113            $this->viewportWidth,
114            $this->viewportHeight,
115            $this->percentageBasis,
116            $this->xHeightRatio,
117            $this->chWidthRatio,
118            $this->capHeightRatio,
119            $this->containerInlineSize,
120            $this->containerBlockSize,
121            $this->lineHeight,
122            $this->percentagesAsZero,
123        );
124    }
125
126    public function withPercentageBasis(float $px): self
127    {
128        return new self(
129            $this->parentFontSize,
130            $this->currentFontSize,
131            $this->rootFontSize,
132            $this->viewportWidth,
133            $this->viewportHeight,
134            $px,
135            $this->xHeightRatio,
136            $this->chWidthRatio,
137            $this->capHeightRatio,
138            $this->containerInlineSize,
139            $this->containerBlockSize,
140            $this->lineHeight,
141            $this->percentagesAsZero,
142        );
143    }
144
145    public function withFontMetrics(float $xHeightRatio, float $chWidthRatio, float $capHeightRatio = 0.7): self
146    {
147        return new self(
148            $this->parentFontSize,
149            $this->currentFontSize,
150            $this->rootFontSize,
151            $this->viewportWidth,
152            $this->viewportHeight,
153            $this->percentageBasis,
154            $xHeightRatio,
155            $chWidthRatio,
156            $capHeightRatio,
157            $this->containerInlineSize,
158            $this->containerBlockSize,
159            $this->lineHeight,
160            $this->percentagesAsZero,
161        );
162    }
163
164    public function withContainerSize(float $inlineSize, float $blockSize): self
165    {
166        return new self(
167            $this->parentFontSize,
168            $this->currentFontSize,
169            $this->rootFontSize,
170            $this->viewportWidth,
171            $this->viewportHeight,
172            $this->percentageBasis,
173            $this->xHeightRatio,
174            $this->chWidthRatio,
175            $this->capHeightRatio,
176            $inlineSize,
177            $blockSize,
178            $this->lineHeight,
179            $this->percentagesAsZero,
180        );
181    }
182
183    public function withLineHeight(float $px): self
184    {
185        return new self(
186            $this->parentFontSize,
187            $this->currentFontSize,
188            $this->rootFontSize,
189            $this->viewportWidth,
190            $this->viewportHeight,
191            $this->percentageBasis,
192            $this->xHeightRatio,
193            $this->chWidthRatio,
194            $this->capHeightRatio,
195            $this->containerInlineSize,
196            $this->containerBlockSize,
197            $px,
198            $this->percentagesAsZero,
199        );
200    }
201}