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 } 4924ab1f72SAndreas Gohr 5024ab1f72SAndreas Gohr /** 5124ab1f72SAndreas Gohr * Wraps all elements of $this in a `<g>` tag 5224ab1f72SAndreas Gohr * 5324ab1f72SAndreas Gohr * @return SvgNode 5424ab1f72SAndreas Gohr */ 5524ab1f72SAndreas Gohr public function groupChildren() { 5624ab1f72SAndreas Gohr $dom = dom_import_simplexml($this); 5724ab1f72SAndreas Gohr 5824ab1f72SAndreas Gohr $g = $dom->ownerDocument->createElement('g'); 5924ab1f72SAndreas Gohr while($dom->childNodes->length > 0) { 6024ab1f72SAndreas Gohr $child = $dom->childNodes->item(0); 6124ab1f72SAndreas Gohr $dom->removeChild($child); 6224ab1f72SAndreas Gohr $g->appendChild($child); 6324ab1f72SAndreas Gohr } 6424ab1f72SAndreas Gohr $g = $dom->appendChild($g); 6524ab1f72SAndreas Gohr 6624ab1f72SAndreas Gohr return simplexml_import_dom($g, get_class($this)); 6724ab1f72SAndreas Gohr } 6824ab1f72SAndreas Gohr 6924ab1f72SAndreas Gohr /** 7024ab1f72SAndreas Gohr * Add new style definitions to this element 7124ab1f72SAndreas Gohr * @param string $style 7224ab1f72SAndreas Gohr */ 7324ab1f72SAndreas Gohr public function addStyle($style) { 7424ab1f72SAndreas Gohr $defs = $this->defs; 7524ab1f72SAndreas Gohr if(!$defs) { 7624ab1f72SAndreas Gohr $defs = $this->prependChild('defs'); 7724ab1f72SAndreas Gohr } 7824ab1f72SAndreas Gohr $defs->addChild('style', $style); 7924ab1f72SAndreas 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'; 89c24a2e1eSAndreas Gohr const CDNBASE = 'https://cdn.rawgit.com/Templarian/MaterialDesign/master/icons/svg/'; 901072ee52SAndreas Gohr 9124ab1f72SAndreas Gohr protected $file; 92*94def893SAndreas Gohr protected $replacements; 931072ee52SAndreas Gohr 941072ee52SAndreas Gohr /** 951072ee52SAndreas Gohr * SVG constructor 961072ee52SAndreas Gohr */ 971072ee52SAndreas Gohr public function __construct() { 981072ee52SAndreas Gohr global $INPUT; 991072ee52SAndreas Gohr 1001072ee52SAndreas Gohr $svg = cleanID($INPUT->str('svg')); 1011072ee52SAndreas Gohr if(blank($svg)) $this->abort(404); 1021072ee52SAndreas Gohr 1031072ee52SAndreas Gohr // try local file first 1041072ee52SAndreas Gohr $file = self::IMGDIR . $svg; 1051072ee52SAndreas Gohr if(!file_exists($file)) { 106c24a2e1eSAndreas Gohr // try media file 107c24a2e1eSAndreas Gohr $file = mediaFN($svg); 108c24a2e1eSAndreas Gohr if(file_exists($file)) { 1091072ee52SAndreas Gohr // media files are ACL protected 1103ec07d58SAndreas Gohr if(auth_quickaclcheck($svg) < AUTH_READ) $this->abort(403); 111c24a2e1eSAndreas Gohr } else { 112c24a2e1eSAndreas Gohr // get it from material design icons 113c24a2e1eSAndreas Gohr $file = getCacheName($svg, '.svg'); 114c24a2e1eSAndreas Gohr io_download(self::CDNBASE . $svg, $file); 115c24a2e1eSAndreas Gohr } 116c24a2e1eSAndreas Gohr 1171072ee52SAndreas Gohr } 1181072ee52SAndreas Gohr // check if media exists 1191072ee52SAndreas Gohr if(!file_exists($file)) $this->abort(404); 1201072ee52SAndreas Gohr 12124ab1f72SAndreas Gohr $this->file = $file; 1221072ee52SAndreas Gohr } 1231072ee52SAndreas Gohr 1241072ee52SAndreas Gohr /** 1251072ee52SAndreas Gohr * Generate and output 1261072ee52SAndreas Gohr */ 1271072ee52SAndreas Gohr public function out() { 12824ab1f72SAndreas Gohr $file = $this->file; 12924ab1f72SAndreas Gohr $params = $this->getParameters(); 13024ab1f72SAndreas Gohr 1314fd6492bSAndreas Gohr header('Content-Type: image/svg+xml'); 13224ab1f72SAndreas Gohr $cachekey = md5($file . serialize($params)); 13324ab1f72SAndreas Gohr $cache = new \cache($cachekey, '.svg'); 13424ab1f72SAndreas Gohr $cache->_event = 'SVG_CACHE'; 13524ab1f72SAndreas Gohr 13624ab1f72SAndreas Gohr http_cached($cache->cache, $cache->useCache(array('files' => array($file, __FILE__)))); 13724ab1f72SAndreas Gohr http_cached_finish($cache->cache, $this->generateSVG($file, $params)); 1381072ee52SAndreas Gohr } 1391072ee52SAndreas Gohr 1401072ee52SAndreas Gohr /** 14124ab1f72SAndreas Gohr * Generate a new SVG based on the input file and the parameters 1421072ee52SAndreas Gohr * 14324ab1f72SAndreas Gohr * @param string $file the SVG file to load 14424ab1f72SAndreas Gohr * @param array $params the parameters as returned by getParameters() 14524ab1f72SAndreas Gohr * @return string the new XML contents 1461072ee52SAndreas Gohr */ 14724ab1f72SAndreas Gohr protected function generateSVG($file, $params) { 14824ab1f72SAndreas Gohr /** @var SvgNode $xml */ 14924ab1f72SAndreas Gohr $xml = simplexml_load_file($file, SvgNode::class); 15024ab1f72SAndreas Gohr $xml->addStyle($this->makeStyle($params)); 15124ab1f72SAndreas Gohr $this->createBackground($xml); 15224ab1f72SAndreas Gohr $xml->groupChildren(); 15324ab1f72SAndreas Gohr 15424ab1f72SAndreas Gohr return $xml->asXML(); 15524ab1f72SAndreas Gohr } 15624ab1f72SAndreas Gohr 15724ab1f72SAndreas Gohr /** 15824ab1f72SAndreas Gohr * Get the supported parameters from request 15924ab1f72SAndreas Gohr * 16024ab1f72SAndreas Gohr * @return array 16124ab1f72SAndreas Gohr */ 16224ab1f72SAndreas Gohr protected function getParameters() { 1631072ee52SAndreas Gohr global $INPUT; 1641072ee52SAndreas Gohr 16524ab1f72SAndreas Gohr $params = array( 1661072ee52SAndreas Gohr 's' => $this->fixColor($INPUT->str('s')), 1671072ee52SAndreas Gohr 'f' => $this->fixColor($INPUT->str('f')), 16880d784e1SMichael Grosse 'b' => $this->fixColor($INPUT->str('b')), 1691072ee52SAndreas Gohr 'sh' => $this->fixColor($INPUT->str('sh')), 1701072ee52SAndreas Gohr 'fh' => $this->fixColor($INPUT->str('fh')), 17180d784e1SMichael Grosse 'bh' => $this->fixColor($INPUT->str('bh')), 1721072ee52SAndreas Gohr ); 1731072ee52SAndreas Gohr 17424ab1f72SAndreas Gohr return $params; 17580d784e1SMichael Grosse } 17680d784e1SMichael Grosse 17724ab1f72SAndreas Gohr /** 17824ab1f72SAndreas Gohr * Generate a style setting from the input variables 17924ab1f72SAndreas Gohr * 18024ab1f72SAndreas Gohr * @param array $params associative array with the given parameters 18124ab1f72SAndreas Gohr * @return string 18224ab1f72SAndreas Gohr */ 18324ab1f72SAndreas Gohr protected function makeStyle($params) { 18424ab1f72SAndreas Gohr $element = 'path'; // FIXME configurable? 18580d784e1SMichael Grosse 18624ab1f72SAndreas Gohr if(empty($params['b'])) { 18724ab1f72SAndreas Gohr $params['b'] = $this->fixColor('00000000'); 18880d784e1SMichael Grosse } 18980d784e1SMichael Grosse 19024ab1f72SAndreas Gohr $style = 'g rect.' . self::BACKGROUNDCLASS . '{fill:' . $params['b'] . ';}'; 19124ab1f72SAndreas Gohr 19224ab1f72SAndreas Gohr if($params['bh']) { 19324ab1f72SAndreas Gohr $style .= 'g:hover rect.' . self::BACKGROUNDCLASS . '{fill:' . $params['bh'] . ';}'; 19424ab1f72SAndreas Gohr } 19524ab1f72SAndreas Gohr 19624ab1f72SAndreas Gohr if($params['s'] || $params['f']) { 19780d784e1SMichael Grosse $style .= 'g ' . $element . '{'; 19824ab1f72SAndreas Gohr if($params['s']) $style .= 'stroke:' . $params['s'] . ';'; 19924ab1f72SAndreas Gohr if($params['f']) $style .= 'fill:' . $params['f'] . ';'; 2001072ee52SAndreas Gohr $style .= '}'; 2011072ee52SAndreas Gohr } 2021072ee52SAndreas Gohr 20324ab1f72SAndreas Gohr if($params['sh'] || $params['fh']) { 20480d784e1SMichael Grosse $style .= 'g:hover ' . $element . '{'; 20524ab1f72SAndreas Gohr if($params['sh']) $style .= 'stroke:' . $params['sh'] . ';'; 20624ab1f72SAndreas Gohr if($params['fh']) $style .= 'fill:' . $params['fh'] . ';'; 2071072ee52SAndreas Gohr $style .= '}'; 2081072ee52SAndreas Gohr } 2091072ee52SAndreas Gohr 2101072ee52SAndreas Gohr return $style; 2111072ee52SAndreas Gohr } 2121072ee52SAndreas Gohr 2131072ee52SAndreas Gohr /** 2141072ee52SAndreas Gohr * Takes a hexadecimal color string in the following forms: 2151072ee52SAndreas Gohr * 2161072ee52SAndreas Gohr * RGB 2171072ee52SAndreas Gohr * RRGGBB 2181072ee52SAndreas Gohr * RRGGBBAA 2191072ee52SAndreas Gohr * 220*94def893SAndreas Gohr * Converts it to rgba() form. 221*94def893SAndreas Gohr * 222*94def893SAndreas Gohr * Alternatively takes a replacement name from the current template's style.ini 2231072ee52SAndreas Gohr * 2241072ee52SAndreas Gohr * @param string $color 2251072ee52SAndreas Gohr * @return string 2261072ee52SAndreas Gohr */ 227*94def893SAndreas Gohr protected function fixColor($color, $ini = true) { 228*94def893SAndreas Gohr 2291072ee52SAndreas Gohr if(preg_match('/^([0-9a-f])([0-9a-f])([0-9a-f])$/i', $color, $m)) { 2301072ee52SAndreas Gohr $r = hexdec($m[1] . $m[1]); 2311072ee52SAndreas Gohr $g = hexdec($m[2] . $m[2]); 2321072ee52SAndreas Gohr $b = hexdec($m[3] . $m[3]); 2331072ee52SAndreas Gohr $a = hexdec('ff'); 2341072ee52SAndreas Gohr } elseif(preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', $color, $m)) { 2351072ee52SAndreas Gohr $r = hexdec($m[1]); 2361072ee52SAndreas Gohr $g = hexdec($m[2]); 2371072ee52SAndreas Gohr $b = hexdec($m[3]); 2381072ee52SAndreas Gohr if(isset($m[4])) { 2391072ee52SAndreas Gohr $a = hexdec($m[4]); 2401072ee52SAndreas Gohr } else { 2411072ee52SAndreas Gohr $a = hexdec('ff'); 2421072ee52SAndreas Gohr } 2431072ee52SAndreas Gohr } else { 244*94def893SAndreas Gohr if($ini) { 245*94def893SAndreas Gohr if(!$this->replacements) $this->initReplacements(); 246*94def893SAndreas Gohr if(isset($this->replacements[$color])) { 247*94def893SAndreas Gohr return $this->replacements[$color]; 248*94def893SAndreas Gohr } 249*94def893SAndreas Gohr } 2501072ee52SAndreas Gohr return ''; 2511072ee52SAndreas Gohr } 2521072ee52SAndreas Gohr 2531072ee52SAndreas Gohr return "rgba($r,$g,$b,$a)"; 2541072ee52SAndreas Gohr } 2551072ee52SAndreas Gohr 2561072ee52SAndreas Gohr /** 25780d784e1SMichael Grosse * sets a rectangular background of the size of the svg/this itself 25880d784e1SMichael Grosse * 25980d784e1SMichael Grosse * @param SvgNode $g 26080d784e1SMichael Grosse * @return SvgNode 26180d784e1SMichael Grosse */ 26224ab1f72SAndreas Gohr protected function createBackground(SvgNode $g) { 26380d784e1SMichael Grosse $rect = $g->prependChild('rect'); 26480d784e1SMichael Grosse $rect->addAttribute('class', self::BACKGROUNDCLASS); 26580d784e1SMichael Grosse 26680d784e1SMichael Grosse $rect->addAttribute('x', '0'); 26780d784e1SMichael Grosse $rect->addAttribute('y', '0'); 26824ab1f72SAndreas Gohr $rect->addAttribute('height', '100%'); 26924ab1f72SAndreas Gohr $rect->addAttribute('width', '100%'); 27080d784e1SMichael Grosse return $rect; 27180d784e1SMichael Grosse } 27280d784e1SMichael Grosse 27380d784e1SMichael Grosse /** 2741072ee52SAndreas Gohr * Abort processing with given status code 2751072ee52SAndreas Gohr * 2761072ee52SAndreas Gohr * @param int $status 2771072ee52SAndreas Gohr */ 2781072ee52SAndreas Gohr protected function abort($status) { 2791072ee52SAndreas Gohr http_status($status); 2801072ee52SAndreas Gohr exit; 2811072ee52SAndreas Gohr } 2821072ee52SAndreas Gohr 283*94def893SAndreas Gohr /** 284*94def893SAndreas Gohr * Initialize the available replacement patterns 285*94def893SAndreas Gohr * 286*94def893SAndreas Gohr * Loads the style.ini from the template (and various local locations) 287*94def893SAndreas Gohr * via a core function only available through some hack. 288*94def893SAndreas Gohr */ 289*94def893SAndreas Gohr protected function initReplacements() { 290*94def893SAndreas Gohr global $conf; 291*94def893SAndreas Gohr define('SIMPLE_TEST', 1); // hacky shit 292*94def893SAndreas Gohr include DOKU_INC . 'lib/exe/css.php'; 293*94def893SAndreas Gohr $ini = css_styleini($conf['tpl']); 294*94def893SAndreas Gohr $this->replacements = $ini['replacements']; 295*94def893SAndreas Gohr } 2961072ee52SAndreas Gohr} 2971072ee52SAndreas Gohr 2981072ee52SAndreas Gohr// main 2991072ee52SAndreas Gohr$svg = new SVG(); 3001072ee52SAndreas Gohr$svg->out(); 3011072ee52SAndreas Gohr 302