xref: /template/sprintdoc/svg.php (revision 1072ee525dad533b3be798f70f2f831f47df46e1)
1<?php
2
3namespace dokuwiki\template\sprintdoc;
4
5if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__) . '/../../../');
6require_once(DOKU_INC . 'inc/init.php');
7
8/**
9 * Custom XML node that allows prepending
10 */
11class SvgNode extends \SimpleXMLElement {
12    /**
13     * @param string $name Name of the new node
14     * @param null|string $value
15     * @return SvgNode
16     */
17    public function prependChild($name, $value = null) {
18        $dom = dom_import_simplexml($this);
19
20        $new = $dom->insertBefore(
21            $dom->ownerDocument->createElement($name, $value),
22            $dom->firstChild
23        );
24
25        return simplexml_import_dom($new, get_class($this));
26    }
27}
28
29/**
30 * Manage SVG recoloring
31 */
32class SVG {
33
34    const IMGDIR = __DIR__ . '/img/';
35
36    /** @var SvgNode */
37    protected $xml;
38
39    /**
40     * SVG constructor
41     */
42    public function __construct() {
43        global $INPUT;
44
45        $svg = cleanID($INPUT->str('svg'));
46        if(blank($svg)) $this->abort(404);
47
48        // try local file first
49        $file = self::IMGDIR . $svg;
50        if(!file_exists($file)) {
51            // media files are ACL protected
52            if(auth_quickaclcheck($svg)) $this->abort(403);
53            $file = mediaFN($svg);
54        }
55        // check if media exists
56        if(!file_exists($file)) $this->abort(404);
57
58        $this->xml = simplexml_load_file($file, SvgNode::class);
59    }
60
61    /**
62     * Generate and output
63     */
64    public function out() {
65        $this->setStyle();
66        header('image/svg+xml');
67        echo $this->xml->asXML();
68    }
69
70    /**
71     * Generate a style setting from the input variables
72     *
73     * @return string
74     */
75    protected function makeStyle() {
76        global $INPUT;
77
78        $element = 'path'; // FIXME configurable?
79
80        $colors = array(
81            's' => $this->fixColor($INPUT->str('s')),
82            'f' => $this->fixColor($INPUT->str('f')),
83            'sh' => $this->fixColor($INPUT->str('sh')),
84            'fh' => $this->fixColor($INPUT->str('fh')),
85        );
86
87        $style = '';
88        if($colors['s'] || $colors['f']) {
89            $style .= $element . '{';
90            if($colors['s']) $style .= 'stroke:' . $colors['s'] . ';';
91            if($colors['f']) $style .= 'fill:' . $colors['f'] . ';';
92            $style .= '}';
93        }
94
95        if($colors['sh'] || $colors['fh']) {
96            $style .= $element . ':hover{';
97            if($colors['sh']) $style .= 'stroke:' . $colors['sh'] . ';';
98            if($colors['fh']) $style .= 'fill:' . $colors['fh'] . ';';
99            $style .= '}';
100        }
101
102        return $style;
103    }
104
105    /**
106     * Takes a hexadecimal color string in the following forms:
107     *
108     * RGB
109     * RRGGBB
110     * RRGGBBAA
111     *
112     * Converts it to rgba() form
113     *
114     * @param string $color
115     * @return string
116     */
117    protected function fixColor($color) {
118        if(preg_match('/^([0-9a-f])([0-9a-f])([0-9a-f])$/i', $color, $m)) {
119            $r = hexdec($m[1] . $m[1]);
120            $g = hexdec($m[2] . $m[2]);
121            $b = hexdec($m[3] . $m[3]);
122            $a = hexdec('ff');
123        } elseif(preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', $color, $m)) {
124            $r = hexdec($m[1]);
125            $g = hexdec($m[2]);
126            $b = hexdec($m[3]);
127            if(isset($m[4])) {
128                $a = hexdec($m[4]);
129            } else {
130                $a = hexdec('ff');
131            }
132        } else {
133            return '';
134        }
135
136        return "rgba($r,$g,$b,$a)";
137    }
138
139    /**
140     * Apply the style to the SVG
141     */
142    protected function setStyle() {
143        $defs = $this->xml->defs;
144        if(!$defs) {
145            $defs = $this->xml->prependChild('defs');
146        }
147        $defs->addChild('style', $this->makeStyle());
148    }
149
150    /**
151     * Abort processing with given status code
152     *
153     * @param int $status
154     */
155    protected function abort($status) {
156        http_status($status);
157        exit;
158    }
159
160}
161
162// main
163$svg = new SVG();
164$svg->out();
165
166