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