* @author Andreas Gohr */ if (!defined('DOKU_INC')) define('DOKU_INC', realpath(__DIR__ . '/../../') . '/'); if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); require_once(DOKU_PLUGIN . 'syntax.php'); class syntax_plugin_graphviz extends SyntaxPlugin { /** * What about paragraphs? */ public function getPType() { return 'normal'; } /** * What kind of syntax are we? */ public function getType() { return 'substition'; } /** * Where to sort in? */ public function getSort() { return 200; } /** * Connect pattern to lexer */ public function connectTo($mode) { $this->Lexer->addSpecialPattern('\n.*?\n', $mode, 'plugin_graphviz'); } /** * Handle the match */ public function handle($match, $state, $pos, Doku_Handler $handler) { $info = $this->getInfo(); // prepare default data $return = ['width' => 0, 'height' => 0, 'layout' => 'dot', 'align' => '', 'version' => $info['date']]; // prepare input $lines = explode("\n", $match); $conf = array_shift($lines); array_pop($lines); // match config options if (preg_match('/\b(left|center|right)\b/i', $conf, $match)) $return['align'] = $match[1]; if (preg_match('/\b(\d+)x(\d+)\b/', $conf, $match)) { $return['width'] = $match[1]; $return['height'] = $match[2]; } if (preg_match('/\b(dot|neato|twopi|circo|fdp)\b/i', $conf, $match)) { $return['layout'] = strtolower($match[1]); } if (preg_match('/\bwidth=([0-9]+)\b/i', $conf, $match)) $return['width'] = $match[1]; if (preg_match('/\bheight=([0-9]+)\b/i', $conf, $match)) $return['height'] = $match[1]; $input = implode("\n", $lines); $return['md5'] = md5($input); // we only pass a hash around // store input for later use io_saveFile($this->_cachename($return, 'txt'), $input); return $return; } /** * Cache file is based on parameters that influence the result image */ public function _cachename($data, $ext) { unset($data['width']); unset($data['height']); unset($data['align']); return getcachename(implode('x', array_values($data)), '.graphviz.' . $ext); } /** * Create output */ public function render($format, Doku_Renderer $R, $data) { if ($format == 'xhtml') { $img = DOKU_BASE . 'lib/plugins/graphviz/img.php?' . buildURLparams($data); $R->doc .= 'doc .= ' width="' . $data['width'] . '"'; if ($data['height']) $R->doc .= ' height="' . $data['height'] . '"'; if ($data['align'] == 'right') $R->doc .= ' align="right"'; if ($data['align'] == 'left') $R->doc .= ' align="left"'; $R->doc .= '/>'; return true; } elseif ($format == 'odt') { $src = $this->_imgfile($data); $R->_odtAddImage($src, $data['width'], $data['height'], $data['align']); return true; } return false; } /** * Return path to the rendered image on our local system */ public function _imgfile($data) { $cache = $this->_cachename($data, 'png'); // create the file if needed if (!file_exists($cache)) { $in = $this->_cachename($data, 'txt'); if ($this->getConf('path')) { $ok = $this->_run($data, $in, $cache); } else { $ok = $this->_remote($data, $in, $cache); } if (!$ok) return false; clearstatcache(); } // resized version if ($data['width']) { $cache = media_resize_image($cache, 'png', $data['width'], $data['height']); } // something went wrong, we're missing the file if (!file_exists($cache)) return false; return $cache; } /** * Render the output remotely at google */ public function _remote($data, $in, $out) { if (!file_exists($in)) { if ($conf['debug']) { dbglog($in, 'no such graphviz input file'); } return false; } $http = new DokuHTTPClient(); $http->timeout = 30; $pass = []; $pass['cht'] = 'gv:' . $data['layout']; $pass['chl'] = io_readFile($in); $img = $http->post('http://chart.apis.google.com/chart', $pass, '&'); if (!$img) return false; return io_saveFile($out, $img); } /** * Run the graphviz program */ public function _run($data, $in, $out) { global $conf; if (!file_exists($in)) { if ($conf['debug']) { dbglog($in, 'no such graphviz input file'); } return false; } $cmd = $this->getConf('path'); $cmd .= ' -Tpng'; $cmd .= ' -K' . $data['layout']; $cmd .= ' -o' . escapeshellarg($out); //output $cmd .= ' ' . escapeshellarg($in); //input exec($cmd, $output, $error); if ($error != 0) { if ($conf['debug']) { dbglog(implode("\n", $output), 'graphviz command failed: ' . $cmd); } return false; } return true; } }