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 } 2780d784e1SMichael Grosse 2880d784e1SMichael Grosse /** 2980d784e1SMichael Grosse * @param \SimpleXMLElement $node the node to be added 3080d784e1SMichael Grosse * @return \SimpleXMLElement 3180d784e1SMichael Grosse */ 3280d784e1SMichael Grosse public function appendNode(\SimpleXMLElement $node) { 3380d784e1SMichael Grosse $dom = dom_import_simplexml($this); 3480d784e1SMichael Grosse $domNode = dom_import_simplexml($node); 3580d784e1SMichael Grosse 3680d784e1SMichael Grosse $newNode = $dom->appendChild($domNode); 3780d784e1SMichael Grosse return simplexml_import_dom($newNode, get_class($this)); 3880d784e1SMichael Grosse } 3980d784e1SMichael Grosse 4080d784e1SMichael Grosse /** 4180d784e1SMichael Grosse * @param \SimpleXMLElement $node the child to remove 4280d784e1SMichael Grosse * @return \SimpleXMLElement 4380d784e1SMichael Grosse */ 4480d784e1SMichael Grosse public function removeChild(\SimpleXMLElement $node) { 4580d784e1SMichael Grosse $dom = dom_import_simplexml($node); 4680d784e1SMichael Grosse $dom->parentNode->removeChild($dom); 4780d784e1SMichael Grosse return $node; 4880d784e1SMichael Grosse } 49*24ab1f72SAndreas Gohr 50*24ab1f72SAndreas Gohr /** 51*24ab1f72SAndreas Gohr * Wraps all elements of $this in a `<g>` tag 52*24ab1f72SAndreas Gohr * 53*24ab1f72SAndreas Gohr * @return SvgNode 54*24ab1f72SAndreas Gohr */ 55*24ab1f72SAndreas Gohr public function groupChildren() { 56*24ab1f72SAndreas Gohr $dom = dom_import_simplexml($this); 57*24ab1f72SAndreas Gohr 58*24ab1f72SAndreas Gohr $g = $dom->ownerDocument->createElement('g'); 59*24ab1f72SAndreas Gohr while ($dom->childNodes->length > 0) { 60*24ab1f72SAndreas Gohr $child = $dom->childNodes->item(0); 61*24ab1f72SAndreas Gohr $dom->removeChild($child); 62*24ab1f72SAndreas Gohr $g->appendChild($child); 63*24ab1f72SAndreas Gohr } 64*24ab1f72SAndreas Gohr $g = $dom->appendChild($g); 65*24ab1f72SAndreas Gohr 66*24ab1f72SAndreas Gohr return simplexml_import_dom($g, get_class($this)); 67*24ab1f72SAndreas Gohr } 68*24ab1f72SAndreas Gohr 69*24ab1f72SAndreas Gohr /** 70*24ab1f72SAndreas Gohr * Add new style definitions to this element 71*24ab1f72SAndreas Gohr * @param string $style 72*24ab1f72SAndreas Gohr */ 73*24ab1f72SAndreas Gohr public function addStyle($style) { 74*24ab1f72SAndreas Gohr $defs = $this->defs; 75*24ab1f72SAndreas Gohr if(!$defs) { 76*24ab1f72SAndreas Gohr $defs = $this->prependChild('defs'); 77*24ab1f72SAndreas Gohr } 78*24ab1f72SAndreas Gohr $defs->addChild('style', $style); 79*24ab1f72SAndreas Gohr } 801072ee52SAndreas Gohr} 811072ee52SAndreas Gohr 821072ee52SAndreas Gohr/** 831072ee52SAndreas Gohr * Manage SVG recoloring 841072ee52SAndreas Gohr */ 851072ee52SAndreas Gohrclass SVG { 861072ee52SAndreas Gohr 871072ee52SAndreas Gohr const IMGDIR = __DIR__ . '/img/'; 8880d784e1SMichael Grosse const BACKGROUNDCLASS = 'sprintdoc-background'; 891072ee52SAndreas Gohr 90*24ab1f72SAndreas Gohr protected $file; 911072ee52SAndreas Gohr 921072ee52SAndreas Gohr /** 931072ee52SAndreas Gohr * SVG constructor 941072ee52SAndreas Gohr */ 951072ee52SAndreas Gohr public function __construct() { 961072ee52SAndreas Gohr global $INPUT; 971072ee52SAndreas Gohr 981072ee52SAndreas Gohr $svg = cleanID($INPUT->str('svg')); 991072ee52SAndreas Gohr if(blank($svg)) $this->abort(404); 1001072ee52SAndreas Gohr 1011072ee52SAndreas Gohr // try local file first 1021072ee52SAndreas Gohr $file = self::IMGDIR . $svg; 1031072ee52SAndreas Gohr if(!file_exists($file)) { 1041072ee52SAndreas Gohr // media files are ACL protected 1053ec07d58SAndreas Gohr if(auth_quickaclcheck($svg) < AUTH_READ) $this->abort(403); 1061072ee52SAndreas Gohr $file = mediaFN($svg); 1071072ee52SAndreas Gohr } 1081072ee52SAndreas Gohr // check if media exists 1091072ee52SAndreas Gohr if(!file_exists($file)) $this->abort(404); 1101072ee52SAndreas Gohr 111*24ab1f72SAndreas Gohr $this->file = $file; 1121072ee52SAndreas Gohr } 1131072ee52SAndreas Gohr 1141072ee52SAndreas Gohr /** 1151072ee52SAndreas Gohr * Generate and output 1161072ee52SAndreas Gohr */ 1171072ee52SAndreas Gohr public function out() { 118*24ab1f72SAndreas Gohr $file = $this->file; 119*24ab1f72SAndreas Gohr $params = $this->getParameters(); 120*24ab1f72SAndreas Gohr 1214fd6492bSAndreas Gohr header('Content-Type: image/svg+xml'); 122*24ab1f72SAndreas Gohr $cachekey = md5($file . serialize($params)); 123*24ab1f72SAndreas Gohr $cache = new \cache($cachekey, '.svg'); 124*24ab1f72SAndreas Gohr $cache->_event = 'SVG_CACHE'; 125*24ab1f72SAndreas Gohr 126*24ab1f72SAndreas Gohr http_cached($cache->cache, $cache->useCache(array('files' => array($file, __FILE__)))); 127*24ab1f72SAndreas Gohr http_cached_finish($cache->cache, $this->generateSVG($file, $params)); 1281072ee52SAndreas Gohr } 1291072ee52SAndreas Gohr 1301072ee52SAndreas Gohr /** 131*24ab1f72SAndreas Gohr * Generate a new SVG based on the input file and the parameters 1321072ee52SAndreas Gohr * 133*24ab1f72SAndreas Gohr * @param string $file the SVG file to load 134*24ab1f72SAndreas Gohr * @param array $params the parameters as returned by getParameters() 135*24ab1f72SAndreas Gohr * @return string the new XML contents 1361072ee52SAndreas Gohr */ 137*24ab1f72SAndreas Gohr protected function generateSVG($file, $params) { 138*24ab1f72SAndreas Gohr /** @var SvgNode $xml */ 139*24ab1f72SAndreas Gohr $xml = simplexml_load_file($file, SvgNode::class); 140*24ab1f72SAndreas Gohr $xml->addStyle($this->makeStyle($params)); 141*24ab1f72SAndreas Gohr $this->createBackground($xml); 142*24ab1f72SAndreas Gohr $xml->groupChildren(); 143*24ab1f72SAndreas Gohr 144*24ab1f72SAndreas Gohr return $xml->asXML(); 145*24ab1f72SAndreas Gohr } 146*24ab1f72SAndreas Gohr 147*24ab1f72SAndreas Gohr /** 148*24ab1f72SAndreas Gohr * Get the supported parameters from request 149*24ab1f72SAndreas Gohr * 150*24ab1f72SAndreas Gohr * @return array 151*24ab1f72SAndreas Gohr */ 152*24ab1f72SAndreas Gohr protected function getParameters() { 1531072ee52SAndreas Gohr global $INPUT; 1541072ee52SAndreas Gohr 155*24ab1f72SAndreas Gohr $params = array( 1561072ee52SAndreas Gohr 's' => $this->fixColor($INPUT->str('s')), 1571072ee52SAndreas Gohr 'f' => $this->fixColor($INPUT->str('f')), 15880d784e1SMichael Grosse 'b' => $this->fixColor($INPUT->str('b')), 1591072ee52SAndreas Gohr 'sh' => $this->fixColor($INPUT->str('sh')), 1601072ee52SAndreas Gohr 'fh' => $this->fixColor($INPUT->str('fh')), 16180d784e1SMichael Grosse 'bh' => $this->fixColor($INPUT->str('bh')), 1621072ee52SAndreas Gohr ); 1631072ee52SAndreas Gohr 164*24ab1f72SAndreas Gohr return $params; 16580d784e1SMichael Grosse } 16680d784e1SMichael Grosse 167*24ab1f72SAndreas Gohr /** 168*24ab1f72SAndreas Gohr * Generate a style setting from the input variables 169*24ab1f72SAndreas Gohr * 170*24ab1f72SAndreas Gohr * @param array $params associative array with the given parameters 171*24ab1f72SAndreas Gohr * @return string 172*24ab1f72SAndreas Gohr */ 173*24ab1f72SAndreas Gohr protected function makeStyle($params) { 174*24ab1f72SAndreas Gohr $element = 'path'; // FIXME configurable? 17580d784e1SMichael Grosse 176*24ab1f72SAndreas Gohr if(empty($params['b'])) { 177*24ab1f72SAndreas Gohr $params['b'] = $this->fixColor('00000000'); 17880d784e1SMichael Grosse } 17980d784e1SMichael Grosse 180*24ab1f72SAndreas Gohr $style = 'g rect.' . self::BACKGROUNDCLASS . '{fill:' . $params['b'] . ';}'; 181*24ab1f72SAndreas Gohr 182*24ab1f72SAndreas Gohr if($params['bh']) { 183*24ab1f72SAndreas Gohr $style .= 'g:hover rect.' . self::BACKGROUNDCLASS . '{fill:' . $params['bh'] . ';}'; 184*24ab1f72SAndreas Gohr } 185*24ab1f72SAndreas Gohr 186*24ab1f72SAndreas Gohr if($params['s'] || $params['f']) { 18780d784e1SMichael Grosse $style .= 'g ' . $element . '{'; 188*24ab1f72SAndreas Gohr if($params['s']) $style .= 'stroke:' . $params['s'] . ';'; 189*24ab1f72SAndreas Gohr if($params['f']) $style .= 'fill:' . $params['f'] . ';'; 1901072ee52SAndreas Gohr $style .= '}'; 1911072ee52SAndreas Gohr } 1921072ee52SAndreas Gohr 193*24ab1f72SAndreas Gohr if($params['sh'] || $params['fh']) { 19480d784e1SMichael Grosse $style .= 'g:hover ' . $element . '{'; 195*24ab1f72SAndreas Gohr if($params['sh']) $style .= 'stroke:' . $params['sh'] . ';'; 196*24ab1f72SAndreas Gohr if($params['fh']) $style .= 'fill:' . $params['fh'] . ';'; 1971072ee52SAndreas Gohr $style .= '}'; 1981072ee52SAndreas Gohr } 1991072ee52SAndreas Gohr 2001072ee52SAndreas Gohr return $style; 2011072ee52SAndreas Gohr } 2021072ee52SAndreas Gohr 2031072ee52SAndreas Gohr /** 2041072ee52SAndreas Gohr * Takes a hexadecimal color string in the following forms: 2051072ee52SAndreas Gohr * 2061072ee52SAndreas Gohr * RGB 2071072ee52SAndreas Gohr * RRGGBB 2081072ee52SAndreas Gohr * RRGGBBAA 2091072ee52SAndreas Gohr * 2101072ee52SAndreas Gohr * Converts it to rgba() form 2111072ee52SAndreas Gohr * 2121072ee52SAndreas Gohr * @param string $color 2131072ee52SAndreas Gohr * @return string 2141072ee52SAndreas Gohr */ 2151072ee52SAndreas Gohr protected function fixColor($color) { 2161072ee52SAndreas Gohr if(preg_match('/^([0-9a-f])([0-9a-f])([0-9a-f])$/i', $color, $m)) { 2171072ee52SAndreas Gohr $r = hexdec($m[1] . $m[1]); 2181072ee52SAndreas Gohr $g = hexdec($m[2] . $m[2]); 2191072ee52SAndreas Gohr $b = hexdec($m[3] . $m[3]); 2201072ee52SAndreas Gohr $a = hexdec('ff'); 2211072ee52SAndreas Gohr } elseif(preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', $color, $m)) { 2221072ee52SAndreas Gohr $r = hexdec($m[1]); 2231072ee52SAndreas Gohr $g = hexdec($m[2]); 2241072ee52SAndreas Gohr $b = hexdec($m[3]); 2251072ee52SAndreas Gohr if(isset($m[4])) { 2261072ee52SAndreas Gohr $a = hexdec($m[4]); 2271072ee52SAndreas Gohr } else { 2281072ee52SAndreas Gohr $a = hexdec('ff'); 2291072ee52SAndreas Gohr } 2301072ee52SAndreas Gohr } else { 2311072ee52SAndreas Gohr return ''; 2321072ee52SAndreas Gohr } 2331072ee52SAndreas Gohr 2341072ee52SAndreas Gohr return "rgba($r,$g,$b,$a)"; 2351072ee52SAndreas Gohr } 2361072ee52SAndreas Gohr 2371072ee52SAndreas Gohr /** 23880d784e1SMichael Grosse * sets a rectangular background of the size of the svg/this itself 23980d784e1SMichael Grosse * 24080d784e1SMichael Grosse * @param SvgNode $g 24180d784e1SMichael Grosse * @return SvgNode 24280d784e1SMichael Grosse */ 243*24ab1f72SAndreas Gohr protected function createBackground(SvgNode $g) { 24480d784e1SMichael Grosse $rect = $g->prependChild('rect'); 24580d784e1SMichael Grosse $rect->addAttribute('class', self::BACKGROUNDCLASS); 24680d784e1SMichael Grosse 24780d784e1SMichael Grosse $rect->addAttribute('x', '0'); 24880d784e1SMichael Grosse $rect->addAttribute('y', '0'); 249*24ab1f72SAndreas Gohr $rect->addAttribute('height', '100%'); 250*24ab1f72SAndreas Gohr $rect->addAttribute('width', '100%'); 25180d784e1SMichael Grosse return $rect; 25280d784e1SMichael Grosse } 25380d784e1SMichael Grosse 25480d784e1SMichael Grosse /** 2551072ee52SAndreas Gohr * Abort processing with given status code 2561072ee52SAndreas Gohr * 2571072ee52SAndreas Gohr * @param int $status 2581072ee52SAndreas Gohr */ 2591072ee52SAndreas Gohr protected function abort($status) { 2601072ee52SAndreas Gohr http_status($status); 2611072ee52SAndreas Gohr exit; 2621072ee52SAndreas Gohr } 2631072ee52SAndreas Gohr 2641072ee52SAndreas Gohr} 2651072ee52SAndreas Gohr 2661072ee52SAndreas Gohr// main 2671072ee52SAndreas Gohr$svg = new SVG(); 2681072ee52SAndreas Gohr$svg->out(); 2691072ee52SAndreas Gohr 270