Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
Paint
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
8
100.00% covered (success)
100.00%
1 / 1
 parse
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Svg\Value;
6
7use Phpdftk\Svg\Value\Paint\CurrentColor;
8use Phpdftk\Svg\Value\Paint\None_;
9use Phpdftk\Svg\Value\Paint\SolidColor;
10use Phpdftk\Svg\Value\Paint\Url;
11
12/**
13 * Parsed SVG paint value (SVG 2 ยง13.2 grammar):
14 *
15 *     <paint> = none | currentColor | <color> | <url> [ none | <color> ]?
16 *
17 * The implementation set is closed: `Paint\None_`, `Paint\CurrentColor`,
18 * `Paint\SolidColor`, `Paint\Url`. The painter pattern-matches on the
19 * concrete type to choose the right PDF emit path (fill/stroke colour vs
20 * gradient/pattern reference).
21 *
22 * Color parsing delegates to `Phpdftk\Svg\Value\Color::parse()` which in
23 * turn produces a `Phpdftk\Color\ColorInterface` instance โ€” the SVG package
24 * depends on `phpdftk/color` for the typed colour model.
25 */
26abstract class Paint
27{
28    /**
29     * Parse an SVG paint attribute value. Returns null on absent / empty /
30     * malformed input โ€” SVG 2's "invalid โ†’ ignored" semantics.
31     */
32    public static function parse(string $raw): ?self
33    {
34        $trimmed = trim($raw);
35        if ($trimmed === '') {
36            return null;
37        }
38        if (strcasecmp($trimmed, 'none') === 0) {
39            return new None_();
40        }
41        if (strcasecmp($trimmed, 'currentColor') === 0) {
42            return new CurrentColor();
43        }
44        // `url(#id) [fallback]` โ€” the fallback is itself a paint, but
45        // restricted by SVG 2 to `none | <color>`. We re-enter parse()
46        // on the tail and reject anything that comes back as a Url.
47        if (preg_match('/^url\(\s*#([^)\s]+)\s*\)\s*(.*)$/i', $trimmed, $m) === 1) {
48            $fallback = trim($m[2]);
49            $fallbackPaint = $fallback === '' ? null : self::parse($fallback);
50            if ($fallbackPaint instanceof Url) {
51                // url(#a) url(#b) isn't a legal SVG fallback chain.
52                $fallbackPaint = null;
53            }
54            return new Url($m[1], $fallbackPaint);
55        }
56        $color = Color::parse($trimmed);
57        return $color === null ? null : new SolidColor($color);
58    }
59}