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