11072ee52SAndreas Gohr<?php 21072ee52SAndreas Gohr 31072ee52SAndreas Gohrnamespace dokuwiki\template\sprintdoc; 41072ee52SAndreas Gohr 51072ee52SAndreas Gohrif(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__) . '/../../../'); 61072ee52SAndreas Gohrrequire_once(DOKU_INC . 'inc/init.php'); 71072ee52SAndreas Gohr 81072ee52SAndreas Gohr/** 91072ee52SAndreas Gohr * Custom XML node that allows prepending 101072ee52SAndreas Gohr */ 111072ee52SAndreas Gohrclass SvgNode extends \SimpleXMLElement { 121072ee52SAndreas Gohr /** 131072ee52SAndreas Gohr * @param string $name Name of the new node 141072ee52SAndreas Gohr * @param null|string $value 151072ee52SAndreas Gohr * @return SvgNode 161072ee52SAndreas Gohr */ 171072ee52SAndreas Gohr public function prependChild($name, $value = null) { 181072ee52SAndreas Gohr $dom = dom_import_simplexml($this); 191072ee52SAndreas Gohr 201072ee52SAndreas Gohr $new = $dom->insertBefore( 211072ee52SAndreas Gohr $dom->ownerDocument->createElement($name, $value), 221072ee52SAndreas Gohr $dom->firstChild 231072ee52SAndreas Gohr ); 241072ee52SAndreas Gohr 251072ee52SAndreas Gohr return simplexml_import_dom($new, get_class($this)); 261072ee52SAndreas Gohr } 27*80d784e1SMichael Grosse 28*80d784e1SMichael Grosse /** 29*80d784e1SMichael Grosse * @param \SimpleXMLElement $node the node to be added 30*80d784e1SMichael Grosse * @return \SimpleXMLElement 31*80d784e1SMichael Grosse */ 32*80d784e1SMichael Grosse public function appendNode(\SimpleXMLElement $node) { 33*80d784e1SMichael Grosse $dom = dom_import_simplexml($this); 34*80d784e1SMichael Grosse $domNode = dom_import_simplexml($node); 35*80d784e1SMichael Grosse 36*80d784e1SMichael Grosse $newNode = $dom->appendChild($domNode); 37*80d784e1SMichael Grosse return simplexml_import_dom($newNode, get_class($this)); 38*80d784e1SMichael Grosse } 39*80d784e1SMichael Grosse 40*80d784e1SMichael Grosse /** 41*80d784e1SMichael Grosse * @param \SimpleXMLElement $node the child to remove 42*80d784e1SMichael Grosse * @return \SimpleXMLElement 43*80d784e1SMichael Grosse */ 44*80d784e1SMichael Grosse public function removeChild(\SimpleXMLElement $node) { 45*80d784e1SMichael Grosse $dom = dom_import_simplexml($node); 46*80d784e1SMichael Grosse $dom->parentNode->removeChild($dom); 47*80d784e1SMichael Grosse return $node; 48*80d784e1SMichael Grosse } 491072ee52SAndreas Gohr} 501072ee52SAndreas Gohr 511072ee52SAndreas Gohr/** 521072ee52SAndreas Gohr * Manage SVG recoloring 531072ee52SAndreas Gohr */ 541072ee52SAndreas Gohrclass SVG { 551072ee52SAndreas Gohr 561072ee52SAndreas Gohr const IMGDIR = __DIR__ . '/img/'; 57*80d784e1SMichael Grosse const BACKGROUNDCLASS = 'sprintdoc-background'; 581072ee52SAndreas Gohr 591072ee52SAndreas Gohr /** @var SvgNode */ 601072ee52SAndreas Gohr protected $xml; 611072ee52SAndreas Gohr 621072ee52SAndreas Gohr /** 631072ee52SAndreas Gohr * SVG constructor 641072ee52SAndreas Gohr */ 651072ee52SAndreas Gohr public function __construct() { 661072ee52SAndreas Gohr global $INPUT; 671072ee52SAndreas Gohr 681072ee52SAndreas Gohr $svg = cleanID($INPUT->str('svg')); 691072ee52SAndreas Gohr if(blank($svg)) $this->abort(404); 701072ee52SAndreas Gohr 711072ee52SAndreas Gohr // try local file first 721072ee52SAndreas Gohr $file = self::IMGDIR . $svg; 731072ee52SAndreas Gohr if(!file_exists($file)) { 741072ee52SAndreas Gohr // media files are ACL protected 751072ee52SAndreas Gohr if(auth_quickaclcheck($svg)) $this->abort(403); 761072ee52SAndreas Gohr $file = mediaFN($svg); 771072ee52SAndreas Gohr } 781072ee52SAndreas Gohr // check if media exists 791072ee52SAndreas Gohr if(!file_exists($file)) $this->abort(404); 801072ee52SAndreas Gohr 811072ee52SAndreas Gohr $this->xml = simplexml_load_file($file, SvgNode::class); 821072ee52SAndreas Gohr } 831072ee52SAndreas Gohr 841072ee52SAndreas Gohr /** 851072ee52SAndreas Gohr * Generate and output 861072ee52SAndreas Gohr */ 871072ee52SAndreas Gohr public function out() { 88*80d784e1SMichael Grosse $g = $this->wrapChildren(); 89*80d784e1SMichael Grosse $this->setBackground($g); 901072ee52SAndreas Gohr $this->setStyle(); 911072ee52SAndreas Gohr header('image/svg+xml'); 921072ee52SAndreas Gohr echo $this->xml->asXML(); 931072ee52SAndreas Gohr } 941072ee52SAndreas Gohr 951072ee52SAndreas Gohr /** 961072ee52SAndreas Gohr * Generate a style setting from the input variables 971072ee52SAndreas Gohr * 981072ee52SAndreas Gohr * @return string 991072ee52SAndreas Gohr */ 1001072ee52SAndreas Gohr protected function makeStyle() { 1011072ee52SAndreas Gohr global $INPUT; 1021072ee52SAndreas Gohr 1031072ee52SAndreas Gohr $element = 'path'; // FIXME configurable? 1041072ee52SAndreas Gohr 1051072ee52SAndreas Gohr $colors = array( 1061072ee52SAndreas Gohr 's' => $this->fixColor($INPUT->str('s')), 1071072ee52SAndreas Gohr 'f' => $this->fixColor($INPUT->str('f')), 108*80d784e1SMichael Grosse 'b' => $this->fixColor($INPUT->str('b')), 1091072ee52SAndreas Gohr 'sh' => $this->fixColor($INPUT->str('sh')), 1101072ee52SAndreas Gohr 'fh' => $this->fixColor($INPUT->str('fh')), 111*80d784e1SMichael Grosse 'bh' => $this->fixColor($INPUT->str('bh')), 1121072ee52SAndreas Gohr ); 1131072ee52SAndreas Gohr 114*80d784e1SMichael Grosse if (empty($colors['b'])) { 115*80d784e1SMichael Grosse $colors['b'] = $this->fixColor('00000000'); 116*80d784e1SMichael Grosse } 117*80d784e1SMichael Grosse 118*80d784e1SMichael Grosse $style = 'g rect.' . self::BACKGROUNDCLASS . '{fill:' . $colors['b'] . ';}'; 119*80d784e1SMichael Grosse 120*80d784e1SMichael Grosse if($colors['bh']) { 121*80d784e1SMichael Grosse $style .= 'g:hover rect.' . self::BACKGROUNDCLASS . '{fill:' . $colors['bh'] . ';}'; 122*80d784e1SMichael Grosse } 123*80d784e1SMichael Grosse 1241072ee52SAndreas Gohr if($colors['s'] || $colors['f']) { 125*80d784e1SMichael Grosse $style .= 'g ' . $element . '{'; 1261072ee52SAndreas Gohr if($colors['s']) $style .= 'stroke:' . $colors['s'] . ';'; 1271072ee52SAndreas Gohr if($colors['f']) $style .= 'fill:' . $colors['f'] . ';'; 1281072ee52SAndreas Gohr $style .= '}'; 1291072ee52SAndreas Gohr } 1301072ee52SAndreas Gohr 1311072ee52SAndreas Gohr if($colors['sh'] || $colors['fh']) { 132*80d784e1SMichael Grosse $style .= 'g:hover ' . $element . '{'; 1331072ee52SAndreas Gohr if($colors['sh']) $style .= 'stroke:' . $colors['sh'] . ';'; 1341072ee52SAndreas Gohr if($colors['fh']) $style .= 'fill:' . $colors['fh'] . ';'; 1351072ee52SAndreas Gohr $style .= '}'; 1361072ee52SAndreas Gohr } 1371072ee52SAndreas Gohr 1381072ee52SAndreas Gohr return $style; 1391072ee52SAndreas Gohr } 1401072ee52SAndreas Gohr 1411072ee52SAndreas Gohr /** 1421072ee52SAndreas Gohr * Takes a hexadecimal color string in the following forms: 1431072ee52SAndreas Gohr * 1441072ee52SAndreas Gohr * RGB 1451072ee52SAndreas Gohr * RRGGBB 1461072ee52SAndreas Gohr * RRGGBBAA 1471072ee52SAndreas Gohr * 1481072ee52SAndreas Gohr * Converts it to rgba() form 1491072ee52SAndreas Gohr * 1501072ee52SAndreas Gohr * @param string $color 1511072ee52SAndreas Gohr * @return string 1521072ee52SAndreas Gohr */ 1531072ee52SAndreas Gohr protected function fixColor($color) { 1541072ee52SAndreas Gohr if(preg_match('/^([0-9a-f])([0-9a-f])([0-9a-f])$/i', $color, $m)) { 1551072ee52SAndreas Gohr $r = hexdec($m[1] . $m[1]); 1561072ee52SAndreas Gohr $g = hexdec($m[2] . $m[2]); 1571072ee52SAndreas Gohr $b = hexdec($m[3] . $m[3]); 1581072ee52SAndreas Gohr $a = hexdec('ff'); 1591072ee52SAndreas Gohr } elseif(preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', $color, $m)) { 1601072ee52SAndreas Gohr $r = hexdec($m[1]); 1611072ee52SAndreas Gohr $g = hexdec($m[2]); 1621072ee52SAndreas Gohr $b = hexdec($m[3]); 1631072ee52SAndreas Gohr if(isset($m[4])) { 1641072ee52SAndreas Gohr $a = hexdec($m[4]); 1651072ee52SAndreas Gohr } else { 1661072ee52SAndreas Gohr $a = hexdec('ff'); 1671072ee52SAndreas Gohr } 1681072ee52SAndreas Gohr } else { 1691072ee52SAndreas Gohr return ''; 1701072ee52SAndreas Gohr } 1711072ee52SAndreas Gohr 1721072ee52SAndreas Gohr return "rgba($r,$g,$b,$a)"; 1731072ee52SAndreas Gohr } 1741072ee52SAndreas Gohr 1751072ee52SAndreas Gohr /** 176*80d784e1SMichael Grosse * sets a rectangular background of the size of the svg/this itself 177*80d784e1SMichael Grosse * 178*80d784e1SMichael Grosse * @param SvgNode $g 179*80d784e1SMichael Grosse * @return SvgNode 180*80d784e1SMichael Grosse */ 181*80d784e1SMichael Grosse protected function setBackground(SvgNode $g) { 182*80d784e1SMichael Grosse $attributes = $this->xml->attributes(); 183*80d784e1SMichael Grosse $rect = $g->prependChild('rect'); 184*80d784e1SMichael Grosse $rect->addAttribute('class', self::BACKGROUNDCLASS); 185*80d784e1SMichael Grosse 186*80d784e1SMichael Grosse $rect->addAttribute('x', '0'); 187*80d784e1SMichael Grosse $rect->addAttribute('y', '0'); 188*80d784e1SMichael Grosse $rect->addAttribute('height', $attributes['height']); 189*80d784e1SMichael Grosse $rect->addAttribute('width', $attributes['width']); 190*80d784e1SMichael Grosse return $rect; 191*80d784e1SMichael Grosse } 192*80d784e1SMichael Grosse 193*80d784e1SMichael Grosse /** 194*80d784e1SMichael Grosse * Wraps all elements of $this in a `<g>` tag 195*80d784e1SMichael Grosse * 196*80d784e1SMichael Grosse * @return SvgNode 197*80d784e1SMichael Grosse */ 198*80d784e1SMichael Grosse protected function wrapChildren() { 199*80d784e1SMichael Grosse $svgChildren = array(); 200*80d784e1SMichael Grosse foreach ($this->xml->children() as $child) { 201*80d784e1SMichael Grosse $svgChildren[] = $this->xml->removeChild($child); 202*80d784e1SMichael Grosse } 203*80d784e1SMichael Grosse $g = $this->xml->prependChild('g'); 204*80d784e1SMichael Grosse foreach ($svgChildren as $child) { 205*80d784e1SMichael Grosse $g->appendNode($child); 206*80d784e1SMichael Grosse } 207*80d784e1SMichael Grosse return $g; 208*80d784e1SMichael Grosse } 209*80d784e1SMichael Grosse 210*80d784e1SMichael Grosse /** 2111072ee52SAndreas Gohr * Apply the style to the SVG 2121072ee52SAndreas Gohr */ 2131072ee52SAndreas Gohr protected function setStyle() { 2141072ee52SAndreas Gohr $defs = $this->xml->defs; 2151072ee52SAndreas Gohr if(!$defs) { 2161072ee52SAndreas Gohr $defs = $this->xml->prependChild('defs'); 2171072ee52SAndreas Gohr } 2181072ee52SAndreas Gohr $defs->addChild('style', $this->makeStyle()); 2191072ee52SAndreas Gohr } 2201072ee52SAndreas Gohr 2211072ee52SAndreas Gohr /** 2221072ee52SAndreas Gohr * Abort processing with given status code 2231072ee52SAndreas Gohr * 2241072ee52SAndreas Gohr * @param int $status 2251072ee52SAndreas Gohr */ 2261072ee52SAndreas Gohr protected function abort($status) { 2271072ee52SAndreas Gohr http_status($status); 2281072ee52SAndreas Gohr exit; 2291072ee52SAndreas Gohr } 2301072ee52SAndreas Gohr 2311072ee52SAndreas Gohr} 2321072ee52SAndreas Gohr 2331072ee52SAndreas Gohr// main 2341072ee52SAndreas Gohr$svg = new SVG(); 2351072ee52SAndreas Gohr$svg->out(); 2361072ee52SAndreas Gohr 237