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'] = ceil(sqrt(max($data['imageWidth'],$data['imageHeight'])/$data['tileSize'])); 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 <span class="maximize"><img src="'.DOKU_BASE.'/lib/plugins/panoview/gfx/window.gif" style="position: absolute; bottom: 4px; right: 5px;" title="Maximize"></span> 130 </p> 131 <div class="options" style="display:none">'.hsc($json->encode($data)).'</div> 132 </div> 133 '; 134 135 136 return true; 137 } 138 139 // ----------- Tile Generator below --------------- 140 141 /** 142 * Create a tile using libGD 143 */ 144 function tile_gd($d){ 145 global $conf; 146 147 $img = null; 148 if(preg_match('/\.jpe?g$/',$d['file'])){ 149 $img = @imagecreatefromjpeg($d['file']); 150 }elseif(preg_match('/\.png$/',$d['file'])){ 151 $img = @imagecreatefrompng($d['file']); 152 }elseif(preg_match('/\.gif$/',$d['file'])){ 153 $img = @imagecreatefromgif($d['file']); 154 } 155 if(!$img) $this->gfx_error('generic'); 156 157 $crop = $this->image_crop($img,$d['width'],$d['height'],$d['tlx'],$d['tly'],$d['brx'],$d['bry']); 158 imagedestroy($img); 159 160 $scale = $this->image_scale($crop,abs($d['brx'] - $d['tlx']),abs($d['bry'] - $d['tly']),$d['ts'],$d['ts']); 161 imagedestroy($crop); 162 163 imagejpeg($scale,$d['cache'],$conf['jpg_quality']); 164 imagedestroy($scale); 165 166 if($conf['fperm']) chmod($d['cache'], $conf['fperm']); 167 } 168 169 /** 170 * Create a tile using Image Magick 171 */ 172 function tile_im($d){ 173 global $conf; 174 175 $cmd = $this->getConf('nice'); 176 $cmd .= ' '.$conf['im_convert']; 177 $cmd .= ' '.escapeshellarg($d['file']); 178 $cmd .= ' -crop \''.abs($d['brx'] - $d['tlx']).'x'.abs($d['bry'] - $d['tly']).'!+'.$d['tlx'].'+'.$d['tly'].'\''; 179 $cmd .= ' -background black'; 180 $cmd .= ' -extent \''.abs($d['brx'] - $d['tlx']).'x'.abs($d['bry'] - $d['tly']).'!\''; 181 $cmd .= ' -resize \''.$d['ts'].'x'.$d['ts'].'!\''; 182 183 $cmd .= ' -quality '.$conf['jpg_quality']; 184 $cmd .= ' '.escapeshellarg($d['cache']); 185 186 # dbg($cmd); exit; 187 188 @exec($cmd,$out,$retval); 189 if ($retval == 0) return true; 190 $this->gfx_error('generic'); 191 } 192 193 /** 194 * Scale an image with libGD 195 */ 196 function image_scale($image,$x,$y,$w,$h){ 197 $scale=imagecreatetruecolor($w,$h); 198 imagecopyresampled($scale,$image,0,0,0,0,$w,$h,$x,$y); 199 return $scale; 200 } 201 202 /** 203 * Crop an image with libGD 204 */ 205 function image_crop($image,$x,$y,$left,$upper,$right,$lower) { 206 $w=abs($right-$left); 207 $h=abs($lower-$upper); 208 $crop = imagecreatetruecolor($w,$h); 209 imagecopy($crop,$image,0,0,$left,$upper,$w,$h); 210 return $crop; 211 } 212 213 /** 214 * Send a graphical error message and stop script 215 */ 216 function gfx_error($type){ 217 $file = dirname(__FILE__).'/gfx/'.$type.'.gif'; 218 $time = filemtime($file); 219 header('Content-type: image/gif'); 220 221 http_conditionalRequest($time); 222 http_sendfile($file); 223 readfile($file); 224 exit; 225 } 226 227 /** 228 * Acquire a lock for the tile generator 229 */ 230 function tile_lock($d){ 231 global $conf; 232 233 $lockDir = $conf['lockdir'].'/'.md5($d['id']).'.panoview'; 234 @ignore_user_abort(1); 235 236 $timeStart = time(); 237 do { 238 //waited longer than 25 seconds? -> stale lock? 239 if ((time() - $timeStart) > 25){ 240 if(time() - filemtime($lockDir) > 30) $this->tile_unlock($d); 241 send_redirect(DOKU_URL.'lib/plugins/panaoview/tiles.php?tile='.$d['zoom'].'-'.$d['col'].'-'.$d['row'].'&image='.rawurlencode($d['id'])); 242 exit; 243 } 244 $locked = @mkdir($lockDir, $conf['dmode']); 245 if($locked){ 246 if(!empty($conf['dperm'])) chmod($lockDir, $conf['dperm']); 247 break; 248 } 249 usleep(rand(500,3000)); 250 } while ($locked === false); 251 } 252 253 /** 254 * Unlock the tile generator 255 */ 256 function tile_unlock($d){ 257 global $conf; 258 259 $lockDir = $conf['lockdir'].'/'.md5($d['id']).'.panoview'; 260 @rmdir($lockDir); 261 @ignore_user_abort(0); 262 } 263 264 265} 266 267//Setup VIM: ex: et ts=4 enc=utf-8 : 268