1903f28d7SAndreas Gohr<?php 2*f3f6ad44SAndreas Gohr 3*f3f6ad44SAndreas Gohruse dokuwiki\Extension\SyntaxPlugin; 4*f3f6ad44SAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient; 5*f3f6ad44SAndreas Gohr 6903f28d7SAndreas Gohr/** 7903f28d7SAndreas Gohr * graphviz-Plugin: Parses graphviz-blocks 8903f28d7SAndreas Gohr * 9903f28d7SAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 10903f28d7SAndreas Gohr * @author Carl-Christian Salvesen <calle@ioslo.net> 11517bd836SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 12903f28d7SAndreas Gohr */ 13903f28d7SAndreas Gohr 14*f3f6ad44SAndreas Gohrif (!defined('DOKU_INC')) define('DOKU_INC', realpath(__DIR__ . '/../../') . '/'); 15903f28d7SAndreas Gohrif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 16903f28d7SAndreas Gohrrequire_once(DOKU_PLUGIN . 'syntax.php'); 17903f28d7SAndreas Gohr 18*f3f6ad44SAndreas Gohrclass syntax_plugin_graphviz extends SyntaxPlugin 19*f3f6ad44SAndreas Gohr{ 20517bd836SAndreas Gohr /** 21517bd836SAndreas Gohr * What about paragraphs? 22517bd836SAndreas Gohr */ 23*f3f6ad44SAndreas Gohr public function getPType() 24*f3f6ad44SAndreas Gohr { 25517bd836SAndreas Gohr return 'normal'; 26903f28d7SAndreas Gohr } 27903f28d7SAndreas Gohr 28903f28d7SAndreas Gohr /** 29903f28d7SAndreas Gohr * What kind of syntax are we? 30903f28d7SAndreas Gohr */ 31*f3f6ad44SAndreas Gohr public function getType() 32*f3f6ad44SAndreas Gohr { 33517bd836SAndreas Gohr return 'substition'; 34903f28d7SAndreas Gohr } 35903f28d7SAndreas Gohr 36903f28d7SAndreas Gohr /** 37903f28d7SAndreas Gohr * Where to sort in? 38903f28d7SAndreas Gohr */ 39*f3f6ad44SAndreas Gohr public function getSort() 40*f3f6ad44SAndreas Gohr { 41fb9a529dSAndreas Gohr return 200; 42903f28d7SAndreas Gohr } 43903f28d7SAndreas Gohr 44903f28d7SAndreas Gohr /** 45903f28d7SAndreas Gohr * Connect pattern to lexer 46903f28d7SAndreas Gohr */ 47*f3f6ad44SAndreas Gohr public function connectTo($mode) 48*f3f6ad44SAndreas Gohr { 49517bd836SAndreas Gohr $this->Lexer->addSpecialPattern('<graphviz.*?>\n.*?\n</graphviz>', $mode, 'plugin_graphviz'); 50903f28d7SAndreas Gohr } 51903f28d7SAndreas Gohr 52903f28d7SAndreas Gohr /** 53903f28d7SAndreas Gohr * Handle the match 54903f28d7SAndreas Gohr */ 55*f3f6ad44SAndreas Gohr public function handle($match, $state, $pos, Doku_Handler $handler) 56*f3f6ad44SAndreas Gohr { 57517bd836SAndreas Gohr $info = $this->getInfo(); 58903f28d7SAndreas Gohr 5967682e69SAndreas Gohr // prepare default data 60*f3f6ad44SAndreas Gohr $return = ['width' => 0, 'height' => 0, 'layout' => 'dot', 'align' => '', 'version' => $info['date']]; 61903f28d7SAndreas Gohr 62517bd836SAndreas Gohr // prepare input 63517bd836SAndreas Gohr $lines = explode("\n", $match); 64517bd836SAndreas Gohr $conf = array_shift($lines); 65517bd836SAndreas Gohr array_pop($lines); 66517bd836SAndreas Gohr 67517bd836SAndreas Gohr // match config options 68517bd836SAndreas Gohr if (preg_match('/\b(left|center|right)\b/i', $conf, $match)) $return['align'] = $match[1]; 69517bd836SAndreas Gohr if (preg_match('/\b(\d+)x(\d+)\b/', $conf, $match)) { 70517bd836SAndreas Gohr $return['width'] = $match[1]; 71517bd836SAndreas Gohr $return['height'] = $match[2]; 72903f28d7SAndreas Gohr } 73517bd836SAndreas Gohr if (preg_match('/\b(dot|neato|twopi|circo|fdp)\b/i', $conf, $match)) { 74517bd836SAndreas Gohr $return['layout'] = strtolower($match[1]); 75903f28d7SAndreas Gohr } 76517bd836SAndreas Gohr if (preg_match('/\bwidth=([0-9]+)\b/i', $conf, $match)) $return['width'] = $match[1]; 77517bd836SAndreas Gohr if (preg_match('/\bheight=([0-9]+)\b/i', $conf, $match)) $return['height'] = $match[1]; 78517bd836SAndreas Gohr 79fb9a529dSAndreas Gohr 80*f3f6ad44SAndreas Gohr $input = implode("\n", $lines); 81fb9a529dSAndreas Gohr $return['md5'] = md5($input); // we only pass a hash around 82fb9a529dSAndreas Gohr 83fb9a529dSAndreas Gohr // store input for later use 84fb9a529dSAndreas Gohr io_saveFile($this->_cachename($return, 'txt'), $input); 85517bd836SAndreas Gohr 86517bd836SAndreas Gohr return $return; 87517bd836SAndreas Gohr } 88517bd836SAndreas Gohr 89903f28d7SAndreas Gohr /** 90fb9a529dSAndreas Gohr * Cache file is based on parameters that influence the result image 91fb9a529dSAndreas Gohr */ 92*f3f6ad44SAndreas Gohr public function _cachename($data, $ext) 93*f3f6ad44SAndreas Gohr { 94fb9a529dSAndreas Gohr unset($data['width']); 95fb9a529dSAndreas Gohr unset($data['height']); 96fb9a529dSAndreas Gohr unset($data['align']); 97*f3f6ad44SAndreas Gohr return getcachename(implode('x', array_values($data)), '.graphviz.' . $ext); 98fb9a529dSAndreas Gohr } 99fb9a529dSAndreas Gohr 100fb9a529dSAndreas Gohr /** 101903f28d7SAndreas Gohr * Create output 102903f28d7SAndreas Gohr */ 103*f3f6ad44SAndreas Gohr public function render($format, Doku_Renderer $R, $data) 104*f3f6ad44SAndreas Gohr { 105eb42ed8aSAndreas Gohr if ($format == 'xhtml') { 106fb9a529dSAndreas Gohr $img = DOKU_BASE . 'lib/plugins/graphviz/img.php?' . buildURLparams($data); 107517bd836SAndreas Gohr $R->doc .= '<img src="' . $img . '" class="media' . $data['align'] . '" alt=""'; 108517bd836SAndreas Gohr if ($data['width']) $R->doc .= ' width="' . $data['width'] . '"'; 109517bd836SAndreas Gohr if ($data['height']) $R->doc .= ' height="' . $data['height'] . '"'; 110cea668ddSAndreas Gohr if ($data['align'] == 'right') $R->doc .= ' align="right"'; 111cea668ddSAndreas Gohr if ($data['align'] == 'left') $R->doc .= ' align="left"'; 112517bd836SAndreas Gohr $R->doc .= '/>'; 113eb42ed8aSAndreas Gohr return true; 114eb42ed8aSAndreas Gohr } elseif ($format == 'odt') { 115eb42ed8aSAndreas Gohr $src = $this->_imgfile($data); 116eb42ed8aSAndreas Gohr $R->_odtAddImage($src, $data['width'], $data['height'], $data['align']); 117eb42ed8aSAndreas Gohr return true; 118eb42ed8aSAndreas Gohr } 119eb42ed8aSAndreas Gohr return false; 120517bd836SAndreas Gohr } 121517bd836SAndreas Gohr 12267682e69SAndreas Gohr /** 123fb9a529dSAndreas Gohr * Return path to the rendered image on our local system 1249d954370SAndreas Gohr */ 125*f3f6ad44SAndreas Gohr public function _imgfile($data) 126*f3f6ad44SAndreas Gohr { 127fb9a529dSAndreas Gohr $cache = $this->_cachename($data, 'png'); 1289d954370SAndreas Gohr 1299d954370SAndreas Gohr // create the file if needed 1309d954370SAndreas Gohr if (!file_exists($cache)) { 131fb9a529dSAndreas Gohr $in = $this->_cachename($data, 'txt'); 132fb9a529dSAndreas Gohr if ($this->getConf('path')) { 133fb9a529dSAndreas Gohr $ok = $this->_run($data, $in, $cache); 134fb9a529dSAndreas Gohr } else { 135fb9a529dSAndreas Gohr $ok = $this->_remote($data, $in, $cache); 136fb9a529dSAndreas Gohr } 137fb9a529dSAndreas Gohr if (!$ok) return false; 1389d954370SAndreas Gohr clearstatcache(); 1399d954370SAndreas Gohr } 1409d954370SAndreas Gohr 1419d954370SAndreas Gohr // resized version 142fb9a529dSAndreas Gohr if ($data['width']) { 143fb9a529dSAndreas Gohr $cache = media_resize_image($cache, 'png', $data['width'], $data['height']); 144fb9a529dSAndreas Gohr } 145fb9a529dSAndreas Gohr 146fb9a529dSAndreas Gohr // something went wrong, we're missing the file 147fb9a529dSAndreas Gohr if (!file_exists($cache)) return false; 1489d954370SAndreas Gohr 1499d954370SAndreas Gohr return $cache; 1509d954370SAndreas Gohr } 1519d954370SAndreas Gohr 1529d954370SAndreas Gohr /** 153fb9a529dSAndreas Gohr * Render the output remotely at google 154fb9a529dSAndreas Gohr */ 155*f3f6ad44SAndreas Gohr public function _remote($data, $in, $out) 156*f3f6ad44SAndreas Gohr { 157fb9a529dSAndreas Gohr if (!file_exists($in)) { 158fb9a529dSAndreas Gohr if ($conf['debug']) { 159fb9a529dSAndreas Gohr dbglog($in, 'no such graphviz input file'); 160fb9a529dSAndreas Gohr } 161fb9a529dSAndreas Gohr return false; 162fb9a529dSAndreas Gohr } 163fb9a529dSAndreas Gohr 164fb9a529dSAndreas Gohr $http = new DokuHTTPClient(); 165fb9a529dSAndreas Gohr $http->timeout = 30; 166fb9a529dSAndreas Gohr 167*f3f6ad44SAndreas Gohr $pass = []; 168fb9a529dSAndreas Gohr $pass['cht'] = 'gv:' . $data['layout']; 169fb9a529dSAndreas Gohr $pass['chl'] = io_readFile($in); 170fb9a529dSAndreas Gohr 171fb9a529dSAndreas Gohr $img = $http->post('http://chart.apis.google.com/chart', $pass, '&'); 172fb9a529dSAndreas Gohr if (!$img) return false; 173fb9a529dSAndreas Gohr 174fb9a529dSAndreas Gohr return io_saveFile($out, $img); 175fb9a529dSAndreas Gohr } 176fb9a529dSAndreas Gohr 177fb9a529dSAndreas Gohr /** 178517bd836SAndreas Gohr * Run the graphviz program 179517bd836SAndreas Gohr */ 180*f3f6ad44SAndreas Gohr public function _run($data, $in, $out) 181*f3f6ad44SAndreas Gohr { 182903f28d7SAndreas Gohr global $conf; 183903f28d7SAndreas Gohr 184fb9a529dSAndreas Gohr if (!file_exists($in)) { 185fb9a529dSAndreas Gohr if ($conf['debug']) { 186fb9a529dSAndreas Gohr dbglog($in, 'no such graphviz input file'); 187fb9a529dSAndreas Gohr } 188fb9a529dSAndreas Gohr return false; 189fb9a529dSAndreas Gohr } 190903f28d7SAndreas Gohr 191517bd836SAndreas Gohr $cmd = $this->getConf('path'); 192517bd836SAndreas Gohr $cmd .= ' -Tpng'; 193517bd836SAndreas Gohr $cmd .= ' -K' . $data['layout']; 194fb9a529dSAndreas Gohr $cmd .= ' -o' . escapeshellarg($out); //output 195fb9a529dSAndreas Gohr $cmd .= ' ' . escapeshellarg($in); //input 196903f28d7SAndreas Gohr 197517bd836SAndreas Gohr exec($cmd, $output, $error); 198903f28d7SAndreas Gohr 199517bd836SAndreas Gohr if ($error != 0) { 200517bd836SAndreas Gohr if ($conf['debug']) { 201*f3f6ad44SAndreas Gohr dbglog(implode("\n", $output), 'graphviz command failed: ' . $cmd); 202903f28d7SAndreas Gohr } 203903f28d7SAndreas Gohr return false; 204903f28d7SAndreas Gohr } 205517bd836SAndreas Gohr return true; 206903f28d7SAndreas Gohr } 207903f28d7SAndreas Gohr} 208