Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
51.61% covered (warning)
51.61%
16 / 31
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
LengthResolver
51.61% covered (warning)
51.61%
16 / 31
0.00% covered (danger)
0.00%
0 / 2
82.93
0.00% covered (danger)
0.00%
0 / 1
 toPx
66.67% covered (warning)
66.67%
16 / 24
0.00% covered (danger)
0.00%
0 / 1
32.37
 resolveValue
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Cascade;
6
7use Phpdftk\Css\Value\Length;
8use Phpdftk\Css\Value\LengthUnit;
9use Phpdftk\Css\Value\Percentage;
10use Phpdftk\Css\Value\Value;
11
12/**
13 * Converts relative-unit `Length` values into absolute `px` Lengths against
14 * a `LengthContext`. Used by the cascade in its computed-value pass.
15 *
16 * Mapping per CSS Values 4 §6:
17 *  - Absolute (px, pt, pc, cm, mm, q, in) — converted directly via the
18 *    CSS canonical relations (1in = 96px, 1pt = 96/72 px, etc.).
19 *  - em / rem / ex / ch / lh / rlh — multiplied against the appropriate
20 *    font-size reference from the context.
21 *  - vw / vh / vmin / vmax / svw / svh / lvw / lvh / dvw / dvh — viewport
22 *    references (the small/large/dynamic variants collapse onto the same
23 *    print-medium viewport since there's no UI chrome to subtract).
24 *  - Percentage — multiplied against the context's `percentageBasis`.
25 */
26final class LengthResolver
27{
28    /**
29     * 1 inch = 96 CSS pixels (CSS Values 4 §6.2). Derived conversions:
30     *  1 pt = 96 / 72  ≈ 1.3333 px
31     *  1 pc = 16       px
32     *  1 cm = 96 / 2.54 ≈ 37.7953 px
33     *  1 mm = 96 / 25.4 ≈ 3.7795 px
34     *  1 Q  = 96 / 101.6 ≈ 0.9449 px
35     */
36    public static function toPx(Length $length, LengthContext $ctx): float
37    {
38        $v = $length->value;
39        return match ($length->unit) {
40            LengthUnit::Px => $v,
41            LengthUnit::Pt => $v * (96.0 / 72.0),
42            LengthUnit::Pc => $v * 16.0,
43            LengthUnit::Cm => $v * (96.0 / 2.54),
44            LengthUnit::Mm => $v * (96.0 / 25.4),
45            LengthUnit::Q => $v * (96.0 / 101.6),
46            LengthUnit::In => $v * 96.0,
47            LengthUnit::Em => $v * $ctx->currentFontSize,
48            LengthUnit::Rem => $v * $ctx->rootFontSize,
49            LengthUnit::Ex => $v * $ctx->currentFontSize * 0.5,   // approx without font metrics
50            LengthUnit::Ch => $v * $ctx->currentFontSize * 0.5,   // approx without font metrics
51            LengthUnit::Lh, LengthUnit::Rlh => $v * $ctx->currentFontSize * 1.2,
52            LengthUnit::Vw, LengthUnit::Svw, LengthUnit::Lvw, LengthUnit::Dvw
53                => $v * ($ctx->viewportWidth / 100.0),
54            LengthUnit::Vh, LengthUnit::Svh, LengthUnit::Lvh, LengthUnit::Dvh
55                => $v * ($ctx->viewportHeight / 100.0),
56            LengthUnit::Vmin
57                => $v * (min($ctx->viewportWidth, $ctx->viewportHeight) / 100.0),
58            LengthUnit::Vmax
59                => $v * (max($ctx->viewportWidth, $ctx->viewportHeight) / 100.0),
60            LengthUnit::Vi => $v * ($ctx->viewportWidth / 100.0),  // assumes horizontal-tb
61            LengthUnit::Vb => $v * ($ctx->viewportHeight / 100.0),
62        };
63    }
64
65    /**
66     * Resolve a Value into an absolute-pixel Length when possible. Returns
67     * the original value untouched if it's not a Length or Percentage.
68     * Percentage requires a non-zero `percentageBasis` in the context; when
69     * the basis is unknown the value is left as a Percentage for layout
70     * to resolve later.
71     */
72    public static function resolveValue(Value $value, LengthContext $ctx): Value
73    {
74        if ($value instanceof Length) {
75            return new Length(self::toPx($value, $ctx), LengthUnit::Px);
76        }
77        if ($value instanceof Percentage) {
78            if ($ctx->percentageBasis === 0.0) {
79                return $value;
80            }
81            return new Length($value->value / 100.0 * $ctx->percentageBasis, LengthUnit::Px);
82        }
83        return $value;
84    }
85}