xref: /plugin/graphviz/syntax.php (revision eb42ed8aa90dab943b4860b04e58c6d9017678d1)
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
10if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
11require_once(DOKU_INC.'inc/init.php');
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 100;
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, &$handler) {
49        $info = $this->getInfo();
50
51        // prepare default data
52        $return = array(
53                        'data'      => '',
54                        'width'     => 0,
55                        'height'    => 0,
56                        'layout'    => 'dot',
57                        'align'     => '',
58                        'version'   => $info['date'], //force rebuild of images on update
59                       );
60
61        // prepare input
62        $lines = explode("\n",$match);
63        $conf = array_shift($lines);
64        array_pop($lines);
65
66        // match config options
67        if(preg_match('/\b(left|center|right)\b/i',$conf,$match)) $return['align'] = $match[1];
68        if(preg_match('/\b(\d+)x(\d+)\b/',$conf,$match)){
69            $return['width']  = $match[1];
70            $return['height'] = $match[2];
71        }
72        if(preg_match('/\b(dot|neato|twopi|circo|fdp)\b/i',$conf,$match)){
73            $return['layout'] = strtolower($match[1]);
74        }
75        if(preg_match('/\bwidth=([0-9]+)\b/i', $conf,$match)) $return['width'] = $match[1];
76        if(preg_match('/\bheight=([0-9]+)\b/i', $conf,$match)) $return['height'] = $match[1];
77
78        $return['data'] = join("\n",$lines);
79
80        return $return;
81    }
82
83    /**
84     * Create output
85     *
86     * @todo latex support?
87     */
88    function render($format, &$R, $data) {
89        if($format == 'xhtml'){
90            $img = $this->_imgurl($data);
91            $R->doc .= '<img src="'.$img.'" class="media'.$data['align'].'" alt=""';
92            if($data['width'])  $R->doc .= ' width="'.$data['width'].'"';
93            if($data['height']) $R->doc .= ' height="'.$data['height'].'"';
94            if($data['align'] == 'right') $ret .= ' align="right"';
95            if($data['align'] == 'left')  $ret .= ' align="left"';
96            $R->doc .= '/>';
97            return true;
98        }elseif($format == 'odt'){
99            $src = $this->_imgfile($data);
100            $R->_odtAddImage($src,$data['width'],$data['height'],$data['align']);
101            return true;
102        }
103        return false;
104    }
105
106    /**
107     * Build the image URL using either our own generator or
108     * the Google Chart API
109     */
110    function _imgurl($data){
111        if($this->getConf('path')){
112            // run graphviz on our own server
113            $img = DOKU_BASE.'lib/plugins/graphviz/img.php?'.buildURLparams($data,'&');
114        }else{
115            // go through google
116            $pass = array(
117                'cht' => 'gv:'.$data['layout'],
118                'chl' => $data['data'],
119            );
120            if($data['width'] && $data['height']){
121                 $pass['chs'] = $data['width'].'x'.$data['height'];
122            }
123
124            $img = 'http://chart.apis.google.com/chart?'.buildURLparams($pass,'&');
125            $img = ml($img,array('w'=>$data['width'],'h'=>$data['height']));
126        }
127        return $img;
128    }
129
130    /**
131     * Return path to created graphviz graph (local only)
132     */
133    function _imgfile($data){
134        $w = (int) $data['width'];
135        $h = (int) $data['height'];
136        unset($data['width']);
137        unset($data['height']);
138        unset($data['align']);
139
140        $cache = getcachename(join('x',array_values($data)),'graphviz.png');
141
142        // create the file if needed
143        if(!file_exists($cache)){
144            $this->_run($data,$cache);
145            clearstatcache();
146        }
147
148        // resized version
149        if($w) $cache = media_resize_image($cache,'png',$w,$h);
150
151        return $cache;
152    }
153
154    /**
155     * Run the graphviz program
156     */
157    function _run($data,$cache) {
158        global $conf;
159
160        $temp = tempnam($conf['tmpdir'],'graphviz_');
161        io_saveFile($temp,$data['data']);
162
163        $cmd  = $this->getConf('path');
164        $cmd .= ' -Tpng';
165        $cmd .= ' -K'.$data['layout'];
166        $cmd .= ' -o'.escapeshellarg($cache); //output
167        $cmd .= ' '.escapeshellarg($temp); //input
168
169        exec($cmd, $output, $error);
170        @unlink($temp);
171
172        if ($error != 0){
173            if($conf['debug']){
174                dbglog(join("\n",$output),'graphviz command failed: '.$cmd);
175            }
176            return false;
177        }
178        return true;
179    }
180
181}
182
183
184
185