xref: /plugin/graphviz/syntax.php (revision f3f6ad44d3cc9569c09b2b3e83b13418547e8a4b)
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