Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
8 / 8 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
| CounterStyleDefinition | |
100.00% |
8 / 8 |
|
100.00% |
3 / 3 |
9 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| effectiveRange | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
6 | |||
| usesNegativeSign | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Phpdftk\HtmlToPdf\Layout; |
| 6 | |
| 7 | /** |
| 8 | * One CSS Counter Styles 3 counter style — the resolved form of an |
| 9 | * `@counter-style` rule (§4) or of a predefined style (§6, §7). |
| 10 | * |
| 11 | * `system: extends …` is resolved away before a definition is built: the |
| 12 | * extending style copies every descriptor it did not itself specify, so by |
| 13 | * the time the formatter sees a definition it is always one of the five |
| 14 | * concrete systems. |
| 15 | */ |
| 16 | final class CounterStyleDefinition |
| 17 | { |
| 18 | public const string CYCLIC = 'cyclic'; |
| 19 | public const string NUMERIC = 'numeric'; |
| 20 | public const string ALPHABETIC = 'alphabetic'; |
| 21 | public const string SYMBOLIC = 'symbolic'; |
| 22 | public const string ADDITIVE = 'additive'; |
| 23 | public const string FIXED = 'fixed'; |
| 24 | |
| 25 | /** |
| 26 | * Not a CSS `system` value — the internal marker for the East Asian |
| 27 | * styles of CSS Counter Styles 3 §7.1 (Chinese / Japanese / Korean, |
| 28 | * informal and formal). |
| 29 | * |
| 30 | * The spec presents those styles as `additive`, but a greedy additive |
| 31 | * pass cannot produce the Chinese zero filler: 101 must be 一百零一, |
| 32 | * and additive gives 一百一. They are generated place-by-place instead. |
| 33 | */ |
| 34 | public const string CJK_IDEOGRAPHIC = 'cjk-ideographic'; |
| 35 | |
| 36 | /** Never drop a `1` before a place multiplier — 10 is 壹拾. */ |
| 37 | public const string ELIDE_NONE = 'none'; |
| 38 | /** Always drop it — 10 is 十, 100 is 百, 1000 is 千. */ |
| 39 | public const string ELIDE_ALL = 'all'; |
| 40 | /** Drop it only in a leading tens place — 10 is 十 but 100 is 一百. */ |
| 41 | public const string ELIDE_LEADING_TENS = 'leading-tens'; |
| 42 | |
| 43 | /** |
| 44 | * @param list<string> $symbols `symbols` descriptor, in order. |
| 45 | * @param list<array{0: int, 1: string}> $additiveSymbols `additive-symbols`, weight-descending. |
| 46 | */ |
| 47 | public function __construct( |
| 48 | public readonly string $system, |
| 49 | public readonly array $symbols = [], |
| 50 | public readonly array $additiveSymbols = [], |
| 51 | public readonly string $negativePrefix = '-', |
| 52 | public readonly string $negativeSuffix = '', |
| 53 | public readonly string $prefix = '', |
| 54 | /** |
| 55 | * CSS Counter Styles 3 §4.7 — the initial `suffix` is a full stop |
| 56 | * followed by a space, which is why every list marker in the CSS 2.1 |
| 57 | * reference files reads `1. ` and not `1.`. |
| 58 | */ |
| 59 | public readonly string $suffix = '. ', |
| 60 | /** Null means `range: auto`, which each system resolves for itself. */ |
| 61 | public readonly ?int $rangeMin = null, |
| 62 | public readonly ?int $rangeMax = null, |
| 63 | public readonly int $padLength = 0, |
| 64 | public readonly string $padSymbol = '', |
| 65 | public readonly ?string $fallback = 'decimal', |
| 66 | /** `system: fixed <first>` — the counter value the first symbol represents. */ |
| 67 | public readonly int $fixedFirst = 1, |
| 68 | /** |
| 69 | * {@see CJK_IDEOGRAPHIC} only — the tens / hundreds / thousands place |
| 70 | * markers (十 百 千, or their formal 拾 佰 仟 variants). |
| 71 | * |
| 72 | * @var list<string> |
| 73 | */ |
| 74 | public readonly array $cjkMultipliers = [], |
| 75 | /** {@see CJK_IDEOGRAPHIC} only — one of the `ELIDE_*` constants. */ |
| 76 | public readonly string $cjkElideOne = self::ELIDE_NONE, |
| 77 | /** |
| 78 | * {@see CJK_IDEOGRAPHIC} only — insert the zero digit where an |
| 79 | * interior place was skipped. True for Chinese, false for Japanese |
| 80 | * and Korean: 1005 is 一千零五 but 千五. |
| 81 | */ |
| 82 | public readonly bool $cjkZeroFiller = false, |
| 83 | ) {} |
| 84 | |
| 85 | /** |
| 86 | * CSS Counter Styles 3 §4.4 — the `range: auto` resolution. Each system |
| 87 | * knows which counter values it can represent at all; outside that the |
| 88 | * style must defer to its `fallback`. |
| 89 | * |
| 90 | * @return array{0: int, 1: int} |
| 91 | */ |
| 92 | public function effectiveRange(): array |
| 93 | { |
| 94 | if ($this->rangeMin !== null || $this->rangeMax !== null) { |
| 95 | return [$this->rangeMin ?? PHP_INT_MIN, $this->rangeMax ?? PHP_INT_MAX]; |
| 96 | } |
| 97 | return match ($this->system) { |
| 98 | self::ALPHABETIC, self::SYMBOLIC => [1, PHP_INT_MAX], |
| 99 | self::ADDITIVE => [0, PHP_INT_MAX], |
| 100 | default => [PHP_INT_MIN, PHP_INT_MAX], |
| 101 | }; |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Does this system spell negative values with the `negative` descriptor? |
| 106 | * |
| 107 | * Cyclic and fixed index their symbol list by the counter value itself, |
| 108 | * so a negative value is a position in the cycle rather than a signed |
| 109 | * magnitude; every other system represents magnitudes and needs the sign. |
| 110 | */ |
| 111 | public function usesNegativeSign(): bool |
| 112 | { |
| 113 | return $this->system !== self::CYCLIC && $this->system !== self::FIXED; |
| 114 | } |
| 115 | } |