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