xref: /plugin/ditaa/syntax.php (revision 95615d6c9d0f4f656d9f64521d7ea9c14a40d45e)
1<?php
2/**
3 * Ditaa-Plugin: Converts Ascii-Flowcharts into a png-File
4 *
5 * @license     GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author      Dennis Ploeger <develop [at] dieploegers [dot] de>
7 * @author      Christoph Mertins <c [dot] mertins [at] gmail [dot] com>
8 * @author      Andreas Gohr <andi@splitbrain.org>
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_ditaa 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
42    function connectTo($mode) {
43        $this->Lexer->addSpecialPattern('<ditaa.*?>\n.*?\n</ditaa>',$mode,'plugin_ditaa');
44    }
45
46    /**
47     * Handle the match
48     */
49    function handle($match, $state, $pos, &$handler) {
50        $info = $this->getInfo();
51
52        // prepare default data
53        $return = array(
54                        'width'     => 0,
55                        'height'    => 0,
56                        'antialias' => true,
57                        'edgesep'   => true,
58                        'round'     => false,
59                        'shadow'    => true,
60                        'scale'     => 1,
61                        'align'     => '',
62                        'version'   => $info['date'], //forece rebuild of images on update
63                       );
64
65
66        // prepare input
67        $lines = explode("\n",$match);
68        $conf = array_shift($lines);
69        array_pop($lines);
70
71        // match config options
72        if(preg_match('/\b(left|center|right)\b/i',$conf,$match)) $return['align'] = $match[1];
73        if(preg_match('/\b(\d+)x(\d+)\b/',$conf,$match)){
74            $return['width']  = $match[1];
75            $return['height'] = $match[2];
76        }
77        if(preg_match('/\b(\d+(\.\d+)?)X\b/',$conf,$match)) $return['scale']  = $match[1];
78        if(preg_match('/\bwidth=([0-9]+)\b/i', $conf,$match)) $return['width'] = $match[1];
79        if(preg_match('/\bheight=([0-9]+)\b/i', $conf,$match)) $return['height'] = $match[1];
80        // match boolean toggles
81        if(preg_match_all('/\b(no)?(antialias|edgesep|round|shadow)\b/i',$conf,$matches,PREG_SET_ORDER)){
82            foreach($matches as $match){
83                $return[$match[2]] = ! $match[1];
84            }
85        }
86
87        $input = join("\n",$lines);
88        $return['md5'] = md5($input); // we only pass a hash around
89
90        // store input for later use
91        io_saveFile($this->_cachename($return,'txt'),$input);
92
93        return $return;
94    }
95
96    /**
97     * Cache file is based on parameters that influence the result image
98     */
99    function _cachename($data,$ext){
100        unset($data['width']);
101        unset($data['height']);
102        unset($data['align']);
103        return getcachename(join('x',array_values($data)),'.ditaa.'.$ext);
104    }
105
106    /**
107     * Create output
108     */
109    function render($format, &$R, $data) {
110        if($format == 'xhtml'){
111            $img = DOKU_BASE.'lib/plugins/ditaa/img.php?'.buildURLparams($data);
112            $R->doc .= '<img src="'.$img.'" class="media'.$data['align'].'" alt=""';
113            if($data['width'])  $R->doc .= ' width="'.$data['width'].'"';
114            if($data['height']) $R->doc .= ' height="'.$data['height'].'"';
115            if($data['align'] == 'right') $ret .= ' align="right"';
116            if($data['align'] == 'left')  $ret .= ' align="left"';
117            $R->doc .= '/>';
118            return true;
119        }elseif($format == 'odt'){
120            $src = $this->_imgfile($data);
121            $R->_odtAddImage($src,$data['width'],$data['height'],$data['align']);
122            return true;
123        }
124        return false;
125    }
126
127
128    /**
129     * Return path to the rendered image on our local system
130     */
131    function _imgfile($data){
132        $cache  = $this->_cachename($data,'png');
133
134        // create the file if needed
135        if(!file_exists($cache)){
136            $in = $this->_cachename($data,'txt');
137            if($this->getConf('java')){
138                $ok = $this->_run($data,$in,$cache);
139            }else{
140                $ok = $this->_remote($data,$in,$cache);
141            }
142            if(!$ok) return false;
143            clearstatcache();
144        }
145
146        // resized version
147        if($data['width']){
148            $cache = media_resize_image($cache,'png',$data['width'],$data['height']);
149        }
150
151        // something went wrong, we're missing the file
152        if(!file_exists($cache)) return false;
153
154        return $cache;
155    }
156
157    /**
158     * Render the output remotely at ditaa.org
159     */
160    function _remote($data,$in,$out){
161        if(!file_exists($in)){
162            if($conf['debug']){
163                dbglog($in,'no such ditaa input file');
164            }
165            return false;
166        }
167
168        $http = new DokuHTTPClient();
169        $http->timeout=30;
170
171        $pass = array();
172        $pass['scale']   = $data['scale'];
173        $pass['timeout'] = 25;
174        $pass['grid']    = io_readFile($in);
175        if(!$data['antialias']) $pass['A'] = 'on';
176        if(!$data['shadow'])    $pass['S'] = 'on';
177        if($data['round'])      $pass['r'] = 'on';
178        if(!$data['edgesep'])   $pass['E'] = 'on';
179
180        $img = $http->post('http://ditaa.org/ditaa/render',$pass);
181        if(!$img) return false;
182
183        return io_saveFile($out,$img);
184    }
185
186    /**
187     * Run the ditaa Java program
188     */
189    function _run($data,$in,$out) {
190        global $conf;
191
192        if(!file_exists($in)){
193            if($conf['debug']){
194                dbglog($in,'no such ditaa input file');
195            }
196            return false;
197        }
198
199        $cmd  = $this->getConf('java');
200        $cmd .= ' -Djava.awt.headless=true -Dfile.encoding=UTF-8 -jar';
201        $cmd .= ' '.escapeshellarg(dirname(__FILE__).'/ditaa/ditaa0_9.jar'); //ditaa jar
202        $cmd .= ' --encoding UTF-8';
203        $cmd .= ' '.escapeshellarg($in); //input
204        $cmd .= ' '.escapeshellarg($out); //output
205        $cmd .= ' -s '.escapeshellarg($data['scale']);
206        if(!$data['antialias']) $cmd .= ' -A';
207        if(!$data['shadow'])    $cmd .= ' -S';
208        if($data['round'])      $cmd .= ' -r';
209        if(!$data['edgesep'])   $cmd .= ' -E';
210
211        exec($cmd, $output, $error);
212
213        if ($error != 0){
214            if($conf['debug']){
215                dbglog(join("\n",$output),'ditaa command failed: '.$cmd);
216            }
217            return false;
218        }
219
220        return true;
221    }
222
223}
224
225