Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
Theme
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
5 / 5
6
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
 withFont
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
 withColor
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
 withMargin
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
 heading
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Pdf\Writer;
6
7/**
8 * Document-wide styling defaults for the high-level {@see Pdf} builder.
9 *
10 * Holds the page margins, body-text defaults (font, size, color, line
11 * height, paragraph spacing) and per-level heading styles (H1–H6).
12 * Individual {@see Pdf::addText()} calls can override parts of the
13 * theme via {@see TextStyle}; headings inherit from the theme's
14 * heading map.
15 *
16 * Themes are immutable. Use the `with*` helpers to derive a modified
17 * theme without mutating the original.
18 */
19final class Theme
20{
21    /**
22     * @param float $margin                 uniform page margin (points)
23     * @param string $family                body font family (Helvetica / Times / Courier)
24     * @param float $fontSize               body size
25     * @param array{float,float,float} $color body RGB (0–1)
26     * @param float $lineHeight             multiplier of font size
27     * @param float $paragraphSpacing       gap between paragraphs (points)
28     * @param array<int, array{size: float, bold: bool, spaceAbove: float, spaceBelow: float}> $headings
29     *        map from level (1..6) to heading style
30     */
31    public function __construct(
32        public readonly float $margin = 72.0,
33        public readonly string $family = 'Helvetica',
34        public readonly float $fontSize = 11.0,
35        public readonly array $color = [0.0, 0.0, 0.0],
36        public readonly float $lineHeight = 1.2,
37        public readonly float $paragraphSpacing = 6.0,
38        public readonly array $headings = [
39            1 => ['size' => 24.0, 'bold' => true, 'spaceAbove' => 18.0, 'spaceBelow' => 10.0],
40            2 => ['size' => 20.0, 'bold' => true, 'spaceAbove' => 14.0, 'spaceBelow' => 8.0],
41            3 => ['size' => 16.0, 'bold' => true, 'spaceAbove' => 12.0, 'spaceBelow' => 6.0],
42            4 => ['size' => 14.0, 'bold' => true, 'spaceAbove' => 10.0, 'spaceBelow' => 5.0],
43            5 => ['size' => 12.0, 'bold' => true, 'spaceAbove' => 8.0,  'spaceBelow' => 4.0],
44            6 => ['size' => 11.0, 'bold' => true, 'spaceAbove' => 6.0,  'spaceBelow' => 3.0],
45        ],
46        public readonly float $headerHeight = 0.0,
47        public readonly float $footerHeight = 0.0,
48        public readonly float $quoteIndent = 18.0,
49        public readonly float $quoteBarWidth = 2.0,
50        /** @var array{float,float,float} */
51        public readonly array $quoteBarColor = [0.7, 0.7, 0.7],
52    ) {}
53
54    public function withFont(string $family, float $size): self
55    {
56        return new self(
57            margin: $this->margin,
58            family: $family,
59            fontSize: $size,
60            color: $this->color,
61            lineHeight: $this->lineHeight,
62            paragraphSpacing: $this->paragraphSpacing,
63            headings: $this->headings,
64            headerHeight: $this->headerHeight,
65            footerHeight: $this->footerHeight,
66            quoteIndent: $this->quoteIndent,
67            quoteBarWidth: $this->quoteBarWidth,
68            quoteBarColor: $this->quoteBarColor,
69        );
70    }
71
72    /** @param array{float,float,float} $color */
73    public function withColor(array $color): self
74    {
75        return new self(
76            margin: $this->margin,
77            family: $this->family,
78            fontSize: $this->fontSize,
79            color: $color,
80            lineHeight: $this->lineHeight,
81            paragraphSpacing: $this->paragraphSpacing,
82            headings: $this->headings,
83            headerHeight: $this->headerHeight,
84            footerHeight: $this->footerHeight,
85            quoteIndent: $this->quoteIndent,
86            quoteBarWidth: $this->quoteBarWidth,
87            quoteBarColor: $this->quoteBarColor,
88        );
89    }
90
91    public function withMargin(float $margin): self
92    {
93        return new self(
94            margin: $margin,
95            family: $this->family,
96            fontSize: $this->fontSize,
97            color: $this->color,
98            lineHeight: $this->lineHeight,
99            paragraphSpacing: $this->paragraphSpacing,
100            headings: $this->headings,
101            headerHeight: $this->headerHeight,
102            footerHeight: $this->footerHeight,
103            quoteIndent: $this->quoteIndent,
104            quoteBarWidth: $this->quoteBarWidth,
105            quoteBarColor: $this->quoteBarColor,
106        );
107    }
108
109    /**
110     * @return array{size: float, bold: bool, spaceAbove: float, spaceBelow: float}
111     */
112    public function heading(int $level): array
113    {
114        if (!isset($this->headings[$level])) {
115            throw new \InvalidArgumentException("No heading style for level $level (expected 1–6)");
116        }
117        return $this->headings[$level];
118    }
119}