1<?php
2/**
3 * seqdia-Plugin: Parses seqdia-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 * @author     Ruslan Popov <ruslan.popov@gmail.com>
9 * @author     Willi Schönborn <w.schoenborn@googlemail.com>
10 */
11
12if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
13if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
14require_once(DOKU_PLUGIN.'syntax.php');
15
16class syntax_plugin_seqdia extends DokuWiki_Syntax_Plugin {
17
18    /**
19     * What about paragraphs?
20     */
21    function getPType(){
22        return 'normal';
23    }
24
25    /**
26     * What kind of syntax are we?
27     */
28    function getType(){
29        return 'substition';
30    }
31
32    /**
33     * Where to sort in?
34     */
35    function getSort(){
36        return 200;
37    }
38
39    /**
40     * Connect pattern to lexer
41     */
42    function connectTo($mode) {
43        $this->Lexer->addSpecialPattern('<seqdia.*?>\n.*?\n</seqdia>',$mode,'plugin_seqdia');
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                        'data'      => '',
55                        'width'     => 0,
56                        'height'    => 0,
57                        'style'    => 'default',
58                        'align'     => '',
59                        'version'   => $info['date'], //force rebuild of images on update
60                       );
61
62        // prepare input
63        $lines = explode("\n",$match);
64        $conf = array_shift($lines);
65        array_pop($lines);
66
67        // match config options
68        if(preg_match('/\b(left|center|right)\b/i',$conf,$match)) $return['align'] = $match[1];
69        if(preg_match('/\b(\d+)x(\d+)\b/',$conf,$match)){
70            $return['width']  = $match[1];
71            $return['height'] = $match[2];
72        }
73        if(preg_match('/\b(default|rose|qsd|napkin|vs2010|mscgen|omegapple|modern-blue|earth|roundgreen)\b/i',$conf,$match)){
74             $return['style'] = strtolower($match[1]);
75        }
76        if(preg_match('/\bwidth=([0-9]+)\b/i', $conf,$match)) $return['width'] = $match[1];
77        if(preg_match('/\bheight=([0-9]+)\b/i', $conf,$match)) $return['height'] = $match[1];
78
79        $input = join("\n",$lines);
80        $return['md5'] = md5($input); // we only pass a hash around
81
82        // store input for later use
83        io_saveFile($this->_cachename($return,'txt'),$input);
84
85        return $return;
86    }
87
88    /**
89     * Cache file is based on parameters that influence the result image
90     */
91    function _cachename($data,$ext){
92        unset($data['width']);
93        unset($data['height']);
94        unset($data['align']);
95        return getcachename(join('x',array_values($data)),'.seqdia.'.$ext);
96    }
97
98    /**
99     * Create output
100     */
101    function render($format, &$R, $data) {
102        if($format == 'xhtml'){
103            $img = DOKU_BASE.'lib/plugins/seqdia/img.php?'.buildURLparams($data);
104            $R->doc .= '<img src="'.$img.'" class="media'.$data['align'].'" alt=""';
105            if($data['width'])  $R->doc .= ' width="'.$data['width'].'"';
106            if($data['height']) $R->doc .= ' height="'.$data['height'].'"';
107            if($data['align'] == 'right') $R->doc .= ' align="right"';
108            if($data['align'] == 'left')  $R->doc .= ' align="left"';
109            $R->doc .= '/>';
110            return true;
111        }elseif($format == 'odt'){
112            $src = $this->_imgfile($data);
113            $R->_odtAddImage($src,$data['width'],$data['height'],$data['align']);
114            return true;
115        }
116        return false;
117    }
118
119    /**
120     * Return path to the rendered image on our local system
121     */
122    function _imgfile($data){
123        $cache  = $this->_cachename($data,'png');
124
125        // create the file if needed
126        if(!file_exists($cache)){
127            $in = $this->_cachename($data,'txt');
128            $ok = $this->_remote($data,$in,$cache);
129            if(!$ok) return false;
130            clearstatcache();
131        }
132
133        // resized version
134        if($data['width']){
135            $cache = media_resize_image($cache,'png',$data['width'],$data['height']);
136        }
137
138        // something went wrong, we're missing the file
139        if(!file_exists($cache)) return false;
140
141        return $cache;
142    }
143
144    /**
145     * Render the output remotely at ditaa.org
146     */
147    function _remote($data,$in,$out){
148        if(!file_exists($in)){
149            if($conf['debug']){
150                dbglog($in,'no such seqdia input file');
151            }
152            return false;
153        }
154
155        $http = new DokuHTTPClient();
156        $http->timeout=30;
157
158        $pass = array();
159        $pass['style']   = $data['style'];
160        $pass['message'] = io_readFile($in);
161
162        $result = $http->post('http://www.websequencediagrams.com/index.php',$pass);
163        if(!$result) return false;
164
165        $json = new JSON(JSON_LOOSE_TYPE);
166        $json->skipnative = true;
167        $json_data = $json->decode($result);
168
169        $img = $http->get('http://www.websequencediagrams.com/index.php'.$json_data['img']);
170        if(!$img) return false;
171
172        return io_saveFile($out,$img);
173    }
174
175}
176