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