xref: /plugin/panoview/syntax.php (revision 4212171fe7f2b1921678f62360b91f713cfa7d8a)
1<?php
2/**
3 * Embed an image gallery
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Andreas Gohr <andi@splitbrain.org>
7 */
8
9if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
10if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
11require_once(DOKU_PLUGIN.'syntax.php');
12require_once(DOKU_INC.'inc/search.php');
13require_once(DOKU_INC.'inc/JpegMeta.php');
14
15class syntax_plugin_panoview extends DokuWiki_Syntax_Plugin {
16    /**
17     * return some info
18     */
19    function getInfo(){
20        return confToHash(dirname(__FILE__).'/info.txt');
21    }
22
23    /**
24     * What kind of syntax are we?
25     */
26    function getType(){
27        return 'substition';
28    }
29
30    /**
31     * What about paragraphs?
32     */
33    function getPType(){
34        return 'block';
35    }
36
37    /**
38     * Where to sort in?
39     */
40    function getSort(){
41        return 301;
42    }
43
44
45    /**
46     * Connect pattern to lexer
47     */
48    function connectTo($mode) {
49        $this->Lexer->addSpecialPattern('\{\{panoview>[^}]*\}\}',$mode,'plugin_panoview');
50    }
51
52    /**
53     * Handle the match
54     */
55    function handle($match, $state, $pos, &$handler){
56        global $ID;
57
58        $data = array(
59            'width'         => 500,
60            'height'        => 250,
61            'align'         => 0,
62            'initialZoom'   => 1,
63            'tileBaseUri'   => DOKU_BASE.'lib/plugins/panoview/tiles.php',
64            'tileSize'      => 256,
65            'maxZoom'       => 10,
66            'blankTile'     => DOKU_BASE.'lib/plugins/panoview/gfx/blank.gif',
67            'loadingTile'   => DOKU_BASE.'lib/plugins/panoview/gfx/progress.gif',
68        );
69
70        $match = substr($match,11,-2); //strip markup from start and end
71
72        // alignment
73        $data['align'] = 0;
74        if(substr($match,0,1) == ' ') $data['align'] += 1;
75        if(substr($match,-1,1) == ' ') $data['align'] += 2;
76
77        // extract params
78        list($img,$params) = explode('?',$match,2);
79        $img = trim($img);
80
81        // resolving relatives
82        $data['image'] = resolve_id(getNS($ID),$img);
83
84        $file = mediaFN($data['image']);
85        list($data['imageWidth'],$data['imageHeight']) = @getimagesize($file);
86
87        // calculate maximum zoom
88        $data['maxZoom'] = floor(sqrt(max($data['imageWidth'],$data['imageHeight'])/$data['tileSize']))-1;
89
90        // size
91        if(preg_match('/\b(\d+)[xX](\d+)\b/',$params,$match)){
92            $data['width']  = $match[1];
93            $data['height'] = $match[2];
94        }
95
96        // initial zoom
97        if(preg_match('/\b[zZ](\d+)\b/',$params,$match)){
98            $data['initialZoom'] = $match[1];
99        }
100        if($data['initialZoom'] < 0) $data['initialZoom'] = 0;
101        if($data['initialZoom'] > $data['maxZoom']) = $data['maxZoom'];
102
103        return $data;
104    }
105
106    /**
107     * Create output
108     */
109    function render($mode, &$R, $data) {
110        if($mode != 'xhtml') return false;
111        global $ID;
112        require_once(DOKU_INC.'inc/JSON.php');
113        $json = new JSON();
114
115        $img = '<a href="'.ml($data['image'],array('id'=>$ID),false).'"><img src="'.
116                    ml($data['image'], array('w'=>$data['width'],'h'=>$data['height'])).'" width="'.
117                    $data['width'].'" height="'.$data['height'].'" alt="" /></a>';
118
119        if($data['align'] == 1){
120            $align = 'medialeft';
121        }elseif($data['align'] == 2){
122            $align = 'mediaright';
123        }else{
124            $align = 'mediacenter';
125        }
126
127
128        $R->doc .= '
129            <div class="panoview_plugin '.$align.'" style="width: '.$data['width'].'px; height: '.$data['height'].'px;">
130              <div class="well"><!-- --></div>
131              <div class="surface">'.$img.'</div>
132              <p class="controls" style="display: none">
133                <span class="zoomIn" title="Zoom In">+</span>
134                <span class="zoomOut" title="Zoom Out">-</span>
135              </p>
136                <div class="options" style="display:none">'.hsc($json->encode($data)).'</div>
137            </div>
138        ';
139
140
141        return true;
142    }
143
144    // ----------- Tile Generator below ---------------
145
146    /**
147     * Create a tile using libGD
148     */
149    function tile_gd($d){
150        global $conf;
151
152        $img = null;
153        if(preg_match('/\.jpe?g$/',$d['file'])){
154            $img   = @imagecreatefromjpeg($d['file']);
155        }elseif(preg_match('/\.png$/',$d['file'])){
156            $img   = @imagecreatefrompng($d['file']);
157        }elseif(preg_match('/\.gif$/',$d['file'])){
158            $img   = @imagecreatefromgif($d['file']);
159        }
160        if(!$img) $this->gfx_error('generic');
161
162        $crop  = $this->image_crop($img,$d['width'],$d['height'],$d['tlx'],$d['tly'],$d['brx'],$d['bry']);
163        imagedestroy($img);
164
165        $scale = $this->image_scale($crop,abs($d['brx'] - $d['tlx']),abs($d['bry'] - $d['tly']),$d['ts'],$d['ts']);
166        imagedestroy($crop);
167
168        imagejpeg($scale,$d['cache'],$conf['jpg_quality']);
169        imagedestroy($scale);
170
171        if($conf['fperm']) chmod($d['cache'], $conf['fperm']);
172    }
173
174    /**
175     * Create a tile using Image Magick
176     */
177    function tile_im($d){
178        global $conf;
179
180        $cmd  = $this->getConf('nice');
181        $cmd .= ' '.$conf['im_convert'];
182        $cmd .= ' '.escapeshellarg($d['file']);
183        $cmd .= ' -crop \''.abs($d['brx'] - $d['tlx']).'x'.abs($d['bry'] - $d['tly']).'!+'.$d['tlx'].'+'.$d['tly'].'\'';
184        $cmd .= ' -background black';
185        $cmd .= ' -extent \''.abs($d['brx'] - $d['tlx']).'x'.abs($d['bry'] - $d['tly']).'!\'';
186        $cmd .= ' -resize \''.$d['ts'].'x'.$d['ts'].'!\'';
187
188        $cmd .= ' -quality '.$conf['jpg_quality'];
189        $cmd .= ' '.escapeshellarg($d['cache']);
190
191    #    dbg($cmd); exit;
192
193        @exec($cmd,$out,$retval);
194        if ($retval == 0) return true;
195        $this->gfx_error('generic');
196    }
197
198    /**
199     * Scale an image with libGD
200     */
201    function image_scale($image,$x,$y,$w,$h){
202        $scale=imagecreatetruecolor($w,$h);
203        imagecopyresampled($scale,$image,0,0,0,0,$w,$h,$x,$y);
204        return $scale;
205    }
206
207    /**
208     * Crop an image with libGD
209     */
210    function image_crop($image,$x,$y,$left,$upper,$right,$lower) {
211        $w=abs($right-$left);
212        $h=abs($lower-$upper);
213        $crop = imagecreatetruecolor($w,$h);
214        imagecopy($crop,$image,0,0,$left,$upper,$w,$h);
215        return $crop;
216    }
217
218    /**
219     * Send a graphical error message and stop script
220     */
221    function gfx_error($type){
222        $file = dirname(__FILE__).'/gfx/'.$type.'.gif';
223        $time = filemtime($file);
224        header('Content-type: image/gif');
225
226        http_conditionalRequest($time);
227        http_sendfile($file);
228        readfile($file);
229        exit;
230    }
231
232    /**
233     * Acquire a lock for the tile generator
234     */
235    function tile_lock($d){
236        global $conf;
237
238        $lockDir = $conf['lockdir'].'/'.md5($d['id']).'.panoview';
239        @ignore_user_abort(1);
240
241        $timeStart = time();
242        do {
243            //waited longer than 25 seconds? -> stale lock?
244            if ((time() - $timeStart) > 25){
245                if(time() - filemtime($lockDir) > 30) $this->tile_unlock($d);
246                send_redirect(DOKU_URL.'lib/plugins/panaoview/tiles.php?tile='.$d['zoom'].'-'.$d['col'].'-'.$d['row'].'&image='.rawurlencode($d['id']));
247                exit;
248            }
249            $locked = @mkdir($lockDir, $conf['dmode']);
250            if($locked){
251              if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']);
252              break;
253            }
254            usleep(rand(500,3000));
255        } while ($locked === false);
256    }
257
258    /**
259     * Unlock the tile generator
260     */
261    function tile_unlock($d){
262        global $conf;
263
264        $lockDir = $conf['lockdir'].'/'.md5($d['id']).'.panoview';
265        @rmdir($lockDir);
266        @ignore_user_abort(0);
267    }
268
269
270}
271
272//Setup VIM: ex: et ts=4 enc=utf-8 :
273