1<?php 2/** 3 * graphviz-Plugin: Parses graphviz-blocks 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Carl-Christian Salvesen <calle@ioslo.net> 7 * @author Andreas Gohr <andi@splitbrain.org> 8 */ 9 10 11if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 13require_once(DOKU_PLUGIN.'syntax.php'); 14 15class syntax_plugin_graphviz extends DokuWiki_Syntax_Plugin { 16 17 /** 18 * What about paragraphs? 19 */ 20 function getPType(){ 21 return 'normal'; 22 } 23 24 /** 25 * What kind of syntax are we? 26 */ 27 function getType(){ 28 return 'substition'; 29 } 30 31 /** 32 * Where to sort in? 33 */ 34 function getSort(){ 35 return 200; 36 } 37 38 /** 39 * Connect pattern to lexer 40 */ 41 function connectTo($mode) { 42 $this->Lexer->addSpecialPattern('<graphviz.*?>\n.*?\n</graphviz>',$mode,'plugin_graphviz'); 43 } 44 45 /** 46 * Handle the match 47 */ 48 function handle($match, $state, $pos, Doku_Handler $handler) { 49 $info = $this->getInfo(); 50 51 // prepare default data 52 $return = array( 53 'width' => 0, 54 'height' => 0, 55 'layout' => 'dot', 56 'align' => '', 57 'version' => $info['date'], //force rebuild of images on update 58 ); 59 60 // prepare input 61 $lines = explode("\n",$match); 62 $conf = array_shift($lines); 63 array_pop($lines); 64 65 // match config options 66 if(preg_match('/\b(left|center|right)\b/i',$conf,$match)) $return['align'] = $match[1]; 67 if(preg_match('/\b(\d+)x(\d+)\b/',$conf,$match)){ 68 $return['width'] = $match[1]; 69 $return['height'] = $match[2]; 70 } 71 if(preg_match('/\b(dot|neato|twopi|circo|fdp)\b/i',$conf,$match)){ 72 $return['layout'] = strtolower($match[1]); 73 } 74 if(preg_match('/\bwidth=([0-9]+)\b/i', $conf,$match)) $return['width'] = $match[1]; 75 if(preg_match('/\bheight=([0-9]+)\b/i', $conf,$match)) $return['height'] = $match[1]; 76 77 78 $input = join("\n",$lines); 79 $return['md5'] = md5($input); // we only pass a hash around 80 81 // store input for later use 82 io_saveFile($this->_cachename($return,'txt'),$input); 83 84 return $return; 85 } 86 87 /** 88 * Cache file is based on parameters that influence the result image 89 */ 90 function _cachename($data,$ext){ 91 unset($data['width']); 92 unset($data['height']); 93 unset($data['align']); 94 return getcachename(join('x',array_values($data)),'.graphviz.'.$ext); 95 } 96 97 /** 98 * Create output 99 */ 100 function render($format, Doku_Renderer $R, $data) { 101 if($format == 'xhtml'){ 102 $img = DOKU_BASE.'lib/plugins/graphviz/img.php?'.buildURLparams($data); 103 $R->doc .= '<img src="'.$img.'" class="media'.$data['align'].'" alt=""'; 104 if($data['width']) $R->doc .= ' width="'.$data['width'].'"'; 105 if($data['height']) $R->doc .= ' height="'.$data['height'].'"'; 106 if($data['align'] == 'right') $R->doc .= ' align="right"'; 107 if($data['align'] == 'left') $R->doc .= ' align="left"'; 108 $R->doc .= '/>'; 109 return true; 110 }elseif($format == 'odt'){ 111 $src = $this->_imgfile($data); 112 $R->_odtAddImage($src,$data['width'],$data['height'],$data['align']); 113 return true; 114 } 115 return false; 116 } 117 118 /** 119 * Return path to the rendered image on our local system 120 */ 121 function _imgfile($data){ 122 $cache = $this->_cachename($data,'png'); 123 124 // create the file if needed 125 if(!file_exists($cache)){ 126 $in = $this->_cachename($data,'txt'); 127 if($this->getConf('path')){ 128 $ok = $this->_run($data,$in,$cache); 129 }else{ 130 $ok = $this->_remote($data,$in,$cache); 131 } 132 if(!$ok) return false; 133 clearstatcache(); 134 } 135 136 // resized version 137 if($data['width']){ 138 $cache = media_resize_image($cache,'png',$data['width'],$data['height']); 139 } 140 141 // something went wrong, we're missing the file 142 if(!file_exists($cache)) return false; 143 144 return $cache; 145 } 146 147 /** 148 * Render the output remotely at google 149 */ 150 function _remote($data,$in,$out){ 151 if(!file_exists($in)){ 152 if($conf['debug']){ 153 dbglog($in,'no such graphviz input file'); 154 } 155 return false; 156 } 157 158 $http = new DokuHTTPClient(); 159 $http->timeout=30; 160 161 $pass = array(); 162 $pass['cht'] = 'gv:'.$data['layout']; 163 $pass['chl'] = io_readFile($in); 164 165 $img = $http->post('http://chart.apis.google.com/chart',$pass,'&'); 166 if(!$img) return false; 167 168 return io_saveFile($out,$img); 169 } 170 171 /** 172 * Run the graphviz program 173 */ 174 function _run($data,$in,$out) { 175 global $conf; 176 177 if(!file_exists($in)){ 178 if($conf['debug']){ 179 dbglog($in,'no such graphviz input file'); 180 } 181 return false; 182 } 183 184 $cmd = $this->getConf('path'); 185 $cmd .= ' -Tpng'; 186 $cmd .= ' -K'.$data['layout']; 187 $cmd .= ' -o'.escapeshellarg($out); //output 188 $cmd .= ' '.escapeshellarg($in); //input 189 190 exec($cmd, $output, $error); 191 192 if ($error != 0){ 193 if($conf['debug']){ 194 dbglog(join("\n",$output),'graphviz command failed: '.$cmd); 195 } 196 return false; 197 } 198 return true; 199 } 200 201} 202 203 204 205