Index: trunk/extensions/PagedTiffHandler/TiffReader.php |
— | — | @@ -0,0 +1,331 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Description of TiffReader |
| 5 | + * |
| 6 | + * @author Sebastian Ulbricht <sebastian.ulbricht@gmx.de> |
| 7 | + */ |
| 8 | +class TiffReader { |
| 9 | + protected $time = NULL; |
| 10 | + protected $file = NULL; |
| 11 | + protected $file_handle = NULL; |
| 12 | + protected $order = NULL; |
| 13 | + protected $the_answer = NULL; |
| 14 | + protected $embed_files = 0; |
| 15 | + protected $ifd_offsets = array(); |
| 16 | + protected $real_eof = 0; |
| 17 | + protected $highest_addr = 0; |
| 18 | + |
| 19 | + protected $unknown_fields = false; |
| 20 | + |
| 21 | + protected $hex = NULL; |
| 22 | + protected $short = NULL; |
| 23 | + protected $long = NULL; |
| 24 | + |
| 25 | + public function __construct($file) { |
| 26 | + $this->time = microtime(true); |
| 27 | + $this->file = $file; |
| 28 | + // set file-pointer |
| 29 | + $this->file_handle = fopen($this->file, 'rb'); |
| 30 | + // read the tiff-header |
| 31 | + $this->order = (fread($this->file_handle, 2) == 'II'); |
| 32 | + if($this->order) { |
| 33 | + $this->hex = 'h*'; |
| 34 | + $this->short = 'v*'; |
| 35 | + $this->long = 'V*'; |
| 36 | + } |
| 37 | + else { |
| 38 | + $this->hex = 'H*'; |
| 39 | + $this->short = 'n*'; |
| 40 | + $this->long = 'N*'; |
| 41 | + } |
| 42 | + $this->the_answer = unpack($this->short, fread($this->file_handle, 2)); |
| 43 | + // set the offset of the first ifd |
| 44 | + $offset = unpack($this->long, fread($this->file_handle, 4)); |
| 45 | + $this->ifd_offsets[]['offset'] = $offset[1]; |
| 46 | + fseek($this->file_handle, 0, SEEK_END); |
| 47 | + $this->real_eof = ftell($this->file_handle); |
| 48 | + } |
| 49 | + |
| 50 | + public function checkScriptAtEnd($size = 1) { |
| 51 | + $size = $size * 1024 * 1024; |
| 52 | + if($size > $this->real_eof) { |
| 53 | + fseek($this->file_handle, 0, SEEK_SET); |
| 54 | + $chunk = fread($this->file_handle, $this->real_eof); |
| 55 | + } |
| 56 | + else { |
| 57 | + fseek($this->file_handle, ($this->real_eof - $size), SEEK_SET); |
| 58 | + $chunk = fread($this->file_handle, $size); |
| 59 | + } |
| 60 | + #check for HTML doctype |
| 61 | + if (preg_match('/<!DOCTYPE *X?HTML/', $chunk)) return true; |
| 62 | + |
| 63 | + $tags = array('<a href', |
| 64 | + '<body', |
| 65 | + '<head', |
| 66 | + '<html', |
| 67 | + '<img', |
| 68 | + '<pre', |
| 69 | + '<script', |
| 70 | + '<table', |
| 71 | + '<title'); |
| 72 | + foreach( $tags as $tag ) { |
| 73 | + if( false !== strpos( $chunk, $tag ) ) { |
| 74 | + return true; |
| 75 | + } |
| 76 | + } |
| 77 | + #look for script-types |
| 78 | + if (preg_match('!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim',$chunk)) return true; |
| 79 | + |
| 80 | + #look for html-style script-urls |
| 81 | + if (preg_match('!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true; |
| 82 | + |
| 83 | + #look for css-style script-urls |
| 84 | + if (preg_match('!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim',$chunk)) return true; |
| 85 | + } |
| 86 | + |
| 87 | + public function checkSize() { |
| 88 | + $diff = $this->real_eof - $this->highest_addr; |
| 89 | + if($diff) { |
| 90 | + if($diff < 0) { |
| 91 | + return true; |
| 92 | + } |
| 93 | + fseek($this->file_handle, $this->highest_addr, SEEK_SET); |
| 94 | + $diffstr = fread($this->file_handle, $diff); |
| 95 | + if(preg_match('/^\0+$/', $diffstr)) { |
| 96 | + return true; |
| 97 | + } |
| 98 | + return false; |
| 99 | + } |
| 100 | + return true; |
| 101 | + } |
| 102 | + |
| 103 | + public function isValidTiff() { |
| 104 | + return $this->the_answer[1] == 42; |
| 105 | + } |
| 106 | + |
| 107 | + public function check($debug = false) { |
| 108 | + $offset = $this->ifd_offsets[$this->embed_files]['offset']; |
| 109 | + $rounds = 0; |
| 110 | + // loop over all ifd |
| 111 | + while($offset && $offset <= $this->real_eof) { |
| 112 | + // save the offset if it is the highest one |
| 113 | + if($offset > $this->highest_addr) { |
| 114 | + $this->highest_addr = $offset; |
| 115 | + } |
| 116 | + // set file-pointer to address with given offset |
| 117 | + fseek($this->file_handle, $offset, SEEK_SET); |
| 118 | + // read amount of ifd-entries |
| 119 | + $entries = unpack($this->short, fread($this->file_handle, 2)); |
| 120 | + $entries = $entries[1]; |
| 121 | + |
| 122 | + $address = $offset + 2 + ($entries * 12); |
| 123 | + if($address > $this->highest_addr) { |
| 124 | + $this->highest_addr = $address; |
| 125 | + } |
| 126 | + |
| 127 | + // run through all entries of this ifd an read them out |
| 128 | + for($i = 0; $i < $entries; $i++) { |
| 129 | + $tmp = $this->readIFDEntry(); |
| 130 | + $this->ifd_offsets[$this->embed_files]['data'][$tmp['tag']] = $tmp; |
| 131 | + } |
| 132 | + |
| 133 | + // set the offset of the next ifd or null if this is the last one |
| 134 | + $offset = unpack($this->long, fread($this->file_handle, 4)); |
| 135 | + $offset = $offset[1]; |
| 136 | + if($offset) { |
| 137 | + $this->ifd_offsets[]['offset'] = $offset; |
| 138 | + } |
| 139 | + $this->embed_files++; |
| 140 | + } |
| 141 | + $this->calculateDataRange(); |
| 142 | + |
| 143 | + if($debug) { |
| 144 | + echo "<h2>TiffReader-Debug:</h2>\n"; |
| 145 | + echo "<b>File: </b>".$this->file."<br />\n"; |
| 146 | + if($this->order) { |
| 147 | + echo "<b>Byte-Order: </b>little Endian<br />\n"; |
| 148 | + } |
| 149 | + else { |
| 150 | + echo "<b>Byte-Order: </b>big Endian<br />\n"; |
| 151 | + } |
| 152 | + echo "<b>Valid Tiff: </b>"; |
| 153 | + if($this->the_answer[1] == 42) { |
| 154 | + echo "yes<br />\n"; |
| 155 | + } |
| 156 | + else { |
| 157 | + echo "no<br />\n"; |
| 158 | + } |
| 159 | + echo "<b>Physicaly Size: </b>".$this->real_eof." bytes<br />\n"; |
| 160 | + echo "<b>Calculated Size: </b>".$this->highest_addr." bytes<br />\n"; |
| 161 | + echo "<b>Difference in Size: </b>".($this->real_eof - $this->highest_addr)." bytes<br />\n"; |
| 162 | + echo "<b>Unknown Fields: </b>"; |
| 163 | + if($this->unknown_fields) { |
| 164 | + echo "yes<br />\n"; |
| 165 | + } |
| 166 | + else { |
| 167 | + echo "no<br />\n"; |
| 168 | + } |
| 169 | + echo "<b>Embed Files: </b>".$this->embed_files."<br />\n"; |
| 170 | + echo "<b>Runtime: </b>".round((microtime(true) - $this->time), 6)." seconds<br />\n"; |
| 171 | + } |
| 172 | + } |
| 173 | + |
| 174 | + protected function readIFDEntry() { |
| 175 | + $tag = unpack($this->short, fread($this->file_handle, 2)); |
| 176 | + $type = unpack($this->short, fread($this->file_handle, 2)); |
| 177 | + $count = unpack($this->long, fread($this->file_handle, 4)); |
| 178 | + $value = unpack($this->long, fread($this->file_handle, 4)); |
| 179 | + return array('tag' => $tag[1], |
| 180 | + 'type' => $type[1], |
| 181 | + 'count' => $count[1], |
| 182 | + 'value' => $value[1]); |
| 183 | + } |
| 184 | + |
| 185 | + protected function calculateDataRange() { |
| 186 | + foreach($this->ifd_offsets as $number => $ifd) { |
| 187 | + foreach($ifd['data'] as $tag => $data) { |
| 188 | + // ignore all entries with local values |
| 189 | + if(($data['type'] == 1 && $data['count'] <= 4) || |
| 190 | + ($data['type'] == 2 && $data['count'] <= 4) || |
| 191 | + ($data['type'] == 3 && $data['count'] <= 2) || |
| 192 | + ($data['type'] == 4 && $data['count'] <= 1) || |
| 193 | + ($data['type'] == 6 && $data['count'] <= 4) || |
| 194 | + ($data['type'] == 7 && $data['count'] <= 4) || |
| 195 | + ($data['type'] == 8 && $data['count'] <= 2) || |
| 196 | + ($data['type'] == 9 && $data['count'] <= 1)) { |
| 197 | + continue; |
| 198 | + } |
| 199 | + // set value size |
| 200 | + switch($data['type']) { |
| 201 | + case 1: |
| 202 | + case 2: |
| 203 | + case 6: |
| 204 | + case 7: |
| 205 | + $size = 1; |
| 206 | + break; |
| 207 | + case 3: |
| 208 | + case 8: |
| 209 | + $size = 2; |
| 210 | + break; |
| 211 | + case 4: |
| 212 | + case 9: |
| 213 | + case 11: |
| 214 | + $size = 4; |
| 215 | + break; |
| 216 | + case 5: |
| 217 | + case 10; |
| 218 | + case 12: |
| 219 | + $size = 8; |
| 220 | + break; |
| 221 | + default: |
| 222 | + $size = 4; |
| 223 | + $this->unknown_fields = true; |
| 224 | + break; |
| 225 | + } |
| 226 | + // calculate the range of memory, the data need |
| 227 | + $size = $data['value'] + ($size * $data['count']); |
| 228 | + if($size > $this->highest_addr) { |
| 229 | + $this->highest_addr = $size; |
| 230 | + } |
| 231 | + } |
| 232 | + // check if more calculations needed |
| 233 | + if($this->highest_addr == $this->real_eof) { |
| 234 | + break; |
| 235 | + } |
| 236 | + // check if image data have to calculate |
| 237 | + if(isset($ifd['data'][273]) && isset($ifd['data'][279])) { |
| 238 | + // set file pointer to the offset for values from field 273 |
| 239 | + fseek($this->file_handle, $ifd['data'][273]['value'], SEEK_SET); |
| 240 | + // get all offsets of the ImageStripes |
| 241 | + $stripes = array(); |
| 242 | + if($ifd['data'][273]['type'] == 3) { |
| 243 | + for($i = 0; $i < $ifd['data'][273]['count']; $i++) { |
| 244 | + $stripes[] = unpack($this->short, fread($this->file_handle, 2)); |
| 245 | + } |
| 246 | + } |
| 247 | + else { |
| 248 | + for($i = 0; $i < $ifd['data'][273]['count']; $i++) { |
| 249 | + $stripes[] = unpack($this->long, fread($this->file_handle, 4)); |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + // set file pointer to the offset for values from field 279 |
| 254 | + fseek($this->file_handle, $ifd['data'][279]['value'], SEEK_SET); |
| 255 | + // get all offsets of the StripeByteCounts |
| 256 | + $stripebytes = array(); |
| 257 | + if($ifd['data'][279]['type'] == 3) { |
| 258 | + for($i = 0; $i < $ifd['data'][279]['count']; $i++) { |
| 259 | + $stripebytes[] = unpack($this->short, fread($this->file_handle, 2)); |
| 260 | + } |
| 261 | + } |
| 262 | + else { |
| 263 | + for($i = 0; $i < $ifd['data'][279]['count']; $i++) { |
| 264 | + $stripebytes[] = unpack($this->long, fread($this->file_handle, 4)); |
| 265 | + } |
| 266 | + } |
| 267 | + // calculate the memory range of the image stripes |
| 268 | + for($i = 0; $i < count($stripes); $i++) { |
| 269 | + $size = $stripes[$i][1] + $stripebytes[$i][1]; |
| 270 | + if($size > $this->highest_addr) { |
| 271 | + $this->highest_addr = $size; |
| 272 | + } |
| 273 | + } |
| 274 | + } |
| 275 | + if(isset($ifd['data'][324]) && isset($ifd['data'][325])) { |
| 276 | + // set file pointer to the offset for values from field 324 |
| 277 | + fseek($this->file_handle, $ifd['data'][324]['value'], SEEK_SET); |
| 278 | + // get all offsets of the ImageTiles |
| 279 | + $tiles = array(); |
| 280 | + for($i = 0; $i < $ifd['data'][324]['count']; $i++) { |
| 281 | + $tiles[] = unpack($this->long, fread($this->file_handle, 4)); |
| 282 | + } |
| 283 | + |
| 284 | + // set file pointer to the offset for values from field 325 |
| 285 | + fseek($this->file_handle, $ifd['data'][325]['value'], SEEK_SET); |
| 286 | + // get all offsets of the TileByteCounts |
| 287 | + $tilebytes = array(); |
| 288 | + if($ifd['data'][325]['type'] == 3) { |
| 289 | + for($i = 0; $i < $ifd['data'][325]['count']; $i++) { |
| 290 | + $tilebytes[] = unpack($this->short, fread($this->file_handle, 2)); |
| 291 | + } |
| 292 | + } |
| 293 | + else { |
| 294 | + for($i = 0; $i < $ifd['data'][325]['count']; $i++) { |
| 295 | + $tilebytes[] = unpack($this->long, fread($this->file_handle, 4)); |
| 296 | + } |
| 297 | + } |
| 298 | + // calculate the memory range of the image tiles |
| 299 | + for($i = 0; $i < count($tiles); $i++) { |
| 300 | + $size = $tiles[$i][1] + $tilebytes[$i][1]; |
| 301 | + if($size > $this->highest_addr) { |
| 302 | + $this->highest_addr = $size; |
| 303 | + } |
| 304 | + } |
| 305 | + } |
| 306 | + if(isset($ifd['data'][288]) && isset($ifd['data'][289])) { |
| 307 | + // set file pointer to the offset for values from field 288 |
| 308 | + fseek($this->file_handle, $ifd['data'][288]['value'], SEEK_SET); |
| 309 | + // get all offsets of the ImageTiles |
| 310 | + $free = array(); |
| 311 | + for($i = 0; $i < $ifd['data'][288]['count']; $i++) { |
| 312 | + $free[] = unpack($this->long, fread($this->file_handle, 4)); |
| 313 | + } |
| 314 | + |
| 315 | + // set file pointer to the offset for values from field 289 |
| 316 | + fseek($this->file_handle, $ifd['data'][289]['value'], SEEK_SET); |
| 317 | + // get all offsets of the TileByteCounts |
| 318 | + $freebytes = array(); |
| 319 | + for($i = 0; $i < $ifd['data'][289]['count']; $i++) { |
| 320 | + $freebytes[] = unpack($this->long, fread($this->file_handle, 4)); |
| 321 | + } |
| 322 | + // calculate the memory range of the image tiles |
| 323 | + for($i = 0; $i < count($tiles); $i++) { |
| 324 | + $size = $free[$i][1] + $freebytes[$i][1]; |
| 325 | + if($size > $this->highest_addr) { |
| 326 | + $this->highest_addr = $size; |
| 327 | + } |
| 328 | + } |
| 329 | + } |
| 330 | + } |
| 331 | + } |
| 332 | +} |
\ No newline at end of file |
Property changes on: trunk/extensions/PagedTiffHandler/TiffReader.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 333 | + native |
Index: trunk/extensions/PagedTiffHandler/PagedTiffHandler_body.php |
— | — | @@ -0,0 +1,560 @@ |
| 2 | +<?php |
| 3 | + /** |
| 4 | + * Copyright (C) Wikimedia Deuschland, 2009 |
| 5 | + * Authors Hallo Welt! Medienwerkstatt GmbH |
| 6 | + * Authors Sebastian Ulbricht, Daniel Lynge, Marc Reymann, Markus Glaser |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU General Public License along |
| 19 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 20 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 21 | + * http://www.gnu.org/copyleft/gpl.html |
| 22 | + * |
| 23 | + */ |
| 24 | + |
| 25 | +class PagedTiffHandler extends ImageHandler { |
| 26 | + /** |
| 27 | + * Sets $wgShowEXIF to true when file is a tiff file. |
| 28 | + * This does not influence other ImageHandlers, which are possibly dependent on read-exif-data. |
| 29 | + */ |
| 30 | + function __construct() { $this->PagedTiffHandler(); } |
| 31 | + function PagedTiffHandler() { |
| 32 | + global $wgShowEXIF; |
| 33 | + $wgShowEXIF = true; |
| 34 | + } |
| 35 | + |
| 36 | + /** |
| 37 | + * Add the "lossy"-parameter to image link. |
| 38 | + * Usage: |
| 39 | + * lossy=true|false |
| 40 | + * lossy=1|0 |
| 41 | + * lossy=lossy|lossless |
| 42 | + * E.g. [[Image:Test.tif|lossy=1]] |
| 43 | + */ |
| 44 | + static function addTiffLossyMagicWordLang( &$magicWords, $langCode ) { |
| 45 | + $magicWords['img_lossy'] = array( 0, "lossy=$1" ); |
| 46 | + return true; |
| 47 | + } |
| 48 | + |
| 49 | + /** |
| 50 | + * Customize the thumbnail shell command. |
| 51 | + */ |
| 52 | + static function renderCommand( &$cmd, $srcPath, $dstPath, $page, $width, $height ) { |
| 53 | + return true; |
| 54 | + } |
| 55 | + |
| 56 | + function isEnabled() { return true; } |
| 57 | + function mustRender() { return true; } |
| 58 | + function isMultiPage($img = false) { |
| 59 | + if (!$img) return true; |
| 60 | + $meta = unserialize($img->metadata); |
| 61 | + return ($meta['Pages'] > 1); |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * various checks against the uploaded file |
| 66 | + * - maximum upload-size |
| 67 | + * - maximum number of embedded files |
| 68 | + * - maximum size of metadata |
| 69 | + * - maximum size of metadata |
| 70 | + * - identify-errors |
| 71 | + * - identify-warnings |
| 72 | + * - check for running-identify-service |
| 73 | + */ |
| 74 | + function check( $saveName, $tempName, &$error ) { |
| 75 | + global $wgTiffMaxEmbedFiles, $wgTiffMaxMetaSize, $wgMaxUploadSize, $wgTiffRejectOnError, $wgTiffRejectOnWarning, |
| 76 | + $wgTiffUseTiffReader, $wgTiffReaderPath, $wgTiffReaderCheckEofForJS; |
| 77 | + if (!(substr($saveName, -5, 5)=='.tiff' || substr($saveName, -4, 4)=='.tif')) return true; |
| 78 | + wfLoadExtensionMessages( 'PagedTiffHandler' ); |
| 79 | + if($wgTiffUseTiffReader) { |
| 80 | + require_once($wgTiffReaderPath.'/TiffReader.php'); |
| 81 | + $tr = new TiffReader($tempName); |
| 82 | + $tr->check(); |
| 83 | + if(!$tr->isValidTiff()) { |
| 84 | + $error = 'tiff_bad_file'; |
| 85 | + wfDebug( __METHOD__.": tiff_bad_file ($saveName)\n" ); |
| 86 | + return false; |
| 87 | + } |
| 88 | + if($tr->checkScriptAtEnd($wgTiffReaderCheckEofForJS)) { |
| 89 | + $error = 'tiff_script_detected'; |
| 90 | + wfDebug( __METHOD__.": tiff_script_detected ($saveName)\n" ); |
| 91 | + return false; |
| 92 | + } |
| 93 | + if(!$tr->checkSize()) { |
| 94 | + $error = 'tiff_size_error'; |
| 95 | + wfDebug( __METHOD__.": tiff_size_error ($saveName)\n" ); |
| 96 | + return false; |
| 97 | + } |
| 98 | + } |
| 99 | + $meta = self::getTiffImage(false, $tempName)->retrieveMetaData(); |
| 100 | + if(!$meta && $meta != -1) { |
| 101 | + $error = 'tiff_out_of_service'; |
| 102 | + wfDebug( __METHOD__.": tiff_out_of_service ($saveName)\n" ); |
| 103 | + return false; |
| 104 | + } |
| 105 | + if($meta == -1) { |
| 106 | + $error = 'tiff_error_cached'; |
| 107 | + wfDebug( __METHOD__.": tiff_error_cached ($saveName)\n" ); |
| 108 | + } |
| 109 | + return self::extCheck($meta, $error, $saveName); |
| 110 | + } |
| 111 | + |
| 112 | + function extCheck($meta, &$error, $saveName = '') { |
| 113 | + global $wgTiffMaxEmbedFiles, $wgTiffMaxMetaSize; |
| 114 | + if(isset($meta['errors'])) { |
| 115 | + $error = 'tiff_bad_file'; |
| 116 | + |
| 117 | + //NOTE: in future, it will become possible to pass parameters |
| 118 | + //$error = array( 'tiff_bad_file' , join('<br />', $meta['errors']) ); |
| 119 | + |
| 120 | + wfDebug( __METHOD__.": tiff_bad_file ($saveName) " . join('; ', $meta['errors']) . "\n" ); |
| 121 | + return false; |
| 122 | + } |
| 123 | + if((strlen(serialize($meta))+1) > $wgTiffMaxMetaSize) { |
| 124 | + $error = 'tiff_too_much_meta'; |
| 125 | + wfDebug( __METHOD__.": tiff_too_much_meta ($saveName)\n" ); |
| 126 | + return false; |
| 127 | + } |
| 128 | + if($wgTiffMaxEmbedFiles && $meta['Pages'] > $wgTiffMaxEmbedFiles) { |
| 129 | + $error = 'tiff_too_much_embed_files'; |
| 130 | + wfDebug( __METHOD__.": tiff_too_much_embed_files ($saveName)\n" ); |
| 131 | + return false; |
| 132 | + } |
| 133 | + return true; |
| 134 | + } |
| 135 | + |
| 136 | + /** |
| 137 | + * maps MagicWord-IDs to parameters. |
| 138 | + * parameter 'lossy' was added. |
| 139 | + */ |
| 140 | + function getParamMap() { |
| 141 | + return array( |
| 142 | + 'img_width' => 'width', |
| 143 | + // @todo check height |
| 144 | + 'img_page' => 'page', |
| 145 | + 'img_lossy' => 'lossy', |
| 146 | + ); |
| 147 | + } |
| 148 | + |
| 149 | + |
| 150 | + /* |
| 151 | + * checks whether parameters are valid and have valid values. |
| 152 | + * check for lossy was added. |
| 153 | + */ |
| 154 | + function validateParam( $name, $value ) { |
| 155 | + if ( in_array( $name, array( 'width', 'height', 'page', 'lossy' ) ) ) { |
| 156 | + if($name == 'lossy') { |
| 157 | + if(in_array($value, array(1, 0, '1', '0', 'true', 'false', 'lossy', 'lossless'))) { |
| 158 | + return true; |
| 159 | + } |
| 160 | + return false; |
| 161 | + } |
| 162 | + if ( $value <= 0 ) { |
| 163 | + return false; |
| 164 | + } else { |
| 165 | + return true; |
| 166 | + } |
| 167 | + } else { |
| 168 | + return false; |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + /** |
| 173 | + * creates parameter string for file name. |
| 174 | + * page number was added. |
| 175 | + */ |
| 176 | + function makeParamString( $params ) { |
| 177 | + $page = isset( $params['page'] ) ? $params['page'] : 1; |
| 178 | + if ( !isset( $params['width'] ) ) { |
| 179 | + return false; |
| 180 | + } |
| 181 | + return "page{$page}-{$params['width']}px"; |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * parses parameter string into an array. |
| 186 | + */ |
| 187 | + function parseParamString( $str ) { |
| 188 | + $m = false; |
| 189 | + if ( preg_match( '/^page(\d+)-(\d+)px$/', $str, $m ) ) { |
| 190 | + return array( 'width' => $m[2], 'page' => $m[1] ); |
| 191 | + } else { |
| 192 | + return false; |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + /** |
| 197 | + * lossy-paramter added |
| 198 | + * TODO: The purpose of this function is not yet fully clear. |
| 199 | + */ |
| 200 | + function getScriptParams( $params ) { |
| 201 | + return array( |
| 202 | + 'width' => $params['width'], |
| 203 | + 'page' => $params['page'], |
| 204 | + 'lossy' => $params['lossy'], |
| 205 | + ); |
| 206 | + } |
| 207 | + |
| 208 | + /** |
| 209 | + * prepares param array and sets standard values |
| 210 | + * standard values for page and lossy are added |
| 211 | + */ |
| 212 | + function normaliseParams( $image, &$params ) { |
| 213 | + global $wgImageMagickIdentifyCommand; |
| 214 | + |
| 215 | + $mimeType = $image->getMimeType(); |
| 216 | + |
| 217 | + if ( !isset( $params['width'] ) ) { |
| 218 | + return false; |
| 219 | + } |
| 220 | + if ( !isset( $params['page'] ) || $params['page'] < 1 ) { |
| 221 | + $params['page'] = 1; |
| 222 | + } |
| 223 | + if ( $params['page'] > $this->pageCount( $image ) ) { |
| 224 | + $params['page'] = $this->pageCount( $image ); |
| 225 | + } |
| 226 | + if ( !isset( $params['lossy'] ) ) { |
| 227 | + $params['lossy'] = NULL; |
| 228 | + } |
| 229 | + $size = PagedTiffImage::getPageSize($this->getMetaArray($image), $params['page']); |
| 230 | + $srcWidth = $size['width']; |
| 231 | + $srcHeight = $size['height']; |
| 232 | + |
| 233 | + if ( isset( $params['height'] ) && $params['height'] != -1 ) { |
| 234 | + if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) { |
| 235 | + $params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] ); |
| 236 | + } |
| 237 | + } |
| 238 | + $params['height'] = File::scaleHeight( $srcWidth, $srcHeight, $params['width'] ); |
| 239 | + if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) { |
| 240 | + return false; |
| 241 | + } |
| 242 | + return true; |
| 243 | + } |
| 244 | + |
| 245 | + /** |
| 246 | + * doTransform was changed for multipage and lossy support. |
| 247 | + * self::TRANSFORM_LATER is ignored. Instead, the function checks whether a |
| 248 | + * thumbnail with the requested file type and resolution exists. It will be |
| 249 | + * created if necessary. |
| 250 | + */ |
| 251 | + function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) { |
| 252 | + global $wgImageMagickConvertCommand, $wgTiffMaxEmbedFileResolution, $wgTiffUseVips, $wgTiffVipsCommand; |
| 253 | + |
| 254 | + $metadata = $image->getMetadata(); |
| 255 | + |
| 256 | + if ( !$metadata ) { |
| 257 | + if($metadata == -1) { |
| 258 | + return $this->doThumbError( @$params['width'], @$params['height'], 'tiff_error_cached' ); |
| 259 | + } |
| 260 | + return $this->doThumbError( @$params['width'], @$params['height'], 'tiff_no_metadata' ); |
| 261 | + } |
| 262 | + if ( !$this->normaliseParams( $image, $params ) ) |
| 263 | + return new TransformParameterError( $params ); |
| 264 | + |
| 265 | + $width = $params['width']; |
| 266 | + $height = $params['height']; |
| 267 | + $srcPath = $image->getPath(); |
| 268 | + $page = $params['page']; |
| 269 | + |
| 270 | + $extension = $this->getThumbExtension($image, $page, $params['lossy']); |
| 271 | + $dstPath .= $extension; |
| 272 | + $dstUrl .= $extension; |
| 273 | + |
| 274 | + $meta = unserialize($metadata); |
| 275 | + |
| 276 | + if(!$this->extCheck($meta, $error, $dstPath)) { |
| 277 | + return $this->doThumbError( @$params['width'], @$params['height'], $error ); |
| 278 | + } |
| 279 | + |
| 280 | + if ( is_file($dstPath) ) |
| 281 | + return new ThumbnailImage( $image, $dstUrl, $width, |
| 282 | + $height, $dstPath, $page ); |
| 283 | + |
| 284 | + if(isset($meta['pages'][$page]['pixels']) && $meta['pages'][$page]['pixels'] > $wgTiffMaxEmbedFileResolution) |
| 285 | + return $this->doThumbError( $width, $height, 'tiff_sourcefile_too_large' ); |
| 286 | + |
| 287 | + if ( !wfMkdirParents( dirname( $dstPath ) ) ) |
| 288 | + return $this->doThumbError( $width, $height, 'thumbnail_dest_directory' ); |
| 289 | + |
| 290 | + if($wgTiffUseVips) { |
| 291 | + // tested in Linux |
| 292 | + $cmd = wfEscapeShellArg( $wgTiffVipsCommand ); |
| 293 | + $cmd .= ' im_resize_linear "'.$srcPath.':'.($page-1).'" '; |
| 294 | + $cmd .= wfEscapeShellArg( $dstPath ); |
| 295 | + $cmd .= " {$width} {$height} 2>&1"; |
| 296 | + } |
| 297 | + else { |
| 298 | + $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ); |
| 299 | + $cmd .= " ". wfEscapeShellArg( $srcPath )."[".($page-1)."]"; |
| 300 | + $cmd .= " -depth 8 -resize {$width} "; |
| 301 | + $cmd .= wfEscapeShellArg( $dstPath ); |
| 302 | + } |
| 303 | + |
| 304 | + wfRunHooks( "PagedTiffHandlerRenderCommand", array( &$cmd, $srcPath, $dstPath, $page, $width, $height ) ); |
| 305 | + |
| 306 | + wfProfileIn( 'PagedTiffHandler' ); |
| 307 | + wfDebug( __METHOD__.": $cmd\n" ); |
| 308 | + $err = wfShellExec( $cmd, $retval ); |
| 309 | + wfProfileOut( 'PagedTiffHandler' ); |
| 310 | + |
| 311 | + $removed = $this->removeBadFile( $dstPath, $retval ); |
| 312 | + |
| 313 | + if ( $retval != 0 || $removed ) { |
| 314 | + wfDebugLog( 'thumbnail', |
| 315 | + sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', |
| 316 | + wfHostname(), $retval, trim($err), $cmd ) ); |
| 317 | + return new MediaTransformError( 'thumbnail_error', $width, $height, $err ); |
| 318 | + } else { |
| 319 | + return new ThumbnailImage( $image, $dstUrl, $width, $height, $dstPath, $page ); |
| 320 | + } |
| 321 | + } |
| 322 | + |
| 323 | + /** |
| 324 | + * Decides (taking lossy parameter into account) the filetype of the thumbnail. |
| 325 | + * If there is no lossy-Parameter (NULL = not set), the decision is being made |
| 326 | + * according to the presence of an alpha value. |
| 327 | + * (alpha == true = png, alpha == false = jpg) |
| 328 | + */ |
| 329 | + function getThumbExtension( $image, $page, $lossy ) { |
| 330 | + if($lossy === NULL) { |
| 331 | + $data = $this->getMetaArray($image); |
| 332 | + if((strtolower($data['pages'][$page]['alpha']) == 'true')) { |
| 333 | + return '.png'; |
| 334 | + } |
| 335 | + else { |
| 336 | + return '.jpg'; |
| 337 | + } |
| 338 | + } |
| 339 | + else { |
| 340 | + if(in_array($lossy, array(1, '1', 'true', 'lossy'))) { |
| 341 | + return '.jpg'; |
| 342 | + } |
| 343 | + else { |
| 344 | + return '.png'; |
| 345 | + } |
| 346 | + } |
| 347 | + } |
| 348 | + |
| 349 | + /** |
| 350 | + * Returns the number of available pages/embedded files |
| 351 | + */ |
| 352 | + function pageCount( $image ) { |
| 353 | + $data = $this->getMetaArray( $image ); |
| 354 | + if ( !$data ) return false; |
| 355 | + return intval( $data['Pages'] ); |
| 356 | + } |
| 357 | + |
| 358 | + /** |
| 359 | + * Returns a new Error-Message. |
| 360 | + */ |
| 361 | + protected function doThumbError( $width, $height, $msg ) { |
| 362 | + wfLoadExtensionMessages( 'PagedTiffHandler' ); |
| 363 | + return new MediaTransformError( 'thumbnail_error', |
| 364 | + $width, $height, wfMsg($msg) ); |
| 365 | + } |
| 366 | + |
| 367 | + /** |
| 368 | + * Get handler-specific metadata which will be saved in the img_metadata field. |
| 369 | + * |
| 370 | + * @param Image $image The image object, or false if there isn't one |
| 371 | + * @param string $fileName The filename |
| 372 | + * @return string |
| 373 | + */ |
| 374 | + function getMetadata( $image, $path ) { |
| 375 | + return serialize( $this->getTiffImage( $image, $path )->retrieveMetaData() ); |
| 376 | + } |
| 377 | + |
| 378 | + /** |
| 379 | + * Creates detail information that is being displayed on image page. |
| 380 | + */ |
| 381 | + function getLongDesc( $image ) { |
| 382 | + global $wgLang; |
| 383 | + $page = isset($_GET['page']) ? $_GET['page'] : 1; |
| 384 | + if ( !isset( $page ) || $page < 1 ) { |
| 385 | + $page = 1; |
| 386 | + } |
| 387 | + if ( $page > $this->pageCount( $image ) ) { |
| 388 | + $page = $this->pageCount( $image ); |
| 389 | + } |
| 390 | + $metadata = $this->getMetaArray($image); |
| 391 | + if( $metadata ) { |
| 392 | + wfLoadExtensionMessages( 'PagedTiffHandler' ); |
| 393 | + return wfMsgExt('tiff-file-info-size', 'parseinline', |
| 394 | + $wgLang->formatNum( $metadata['pages'][$page]['width'] ), |
| 395 | + $wgLang->formatNum( $metadata['pages'][$page]['height'] ), |
| 396 | + $wgLang->formatSize( $image->getSize() ), |
| 397 | + 'image/tiff', |
| 398 | + $page ); |
| 399 | + } |
| 400 | + return true; |
| 401 | + } |
| 402 | + |
| 403 | + /** |
| 404 | + * Check if the metadata string is valid for this handler. |
| 405 | + * If it returns false, Image will reload the metadata from the file and update the database |
| 406 | + */ |
| 407 | + function isMetadataValid( $image, $metadata ) { |
| 408 | + if(!empty( $metadata ) && $metadata != serialize(array())) { |
| 409 | + $meta = unserialize($metadata); |
| 410 | + if(isset($meta['Pages']) && isset($meta['pages'])) { |
| 411 | + return true; |
| 412 | + } |
| 413 | + } |
| 414 | + return false; |
| 415 | + } |
| 416 | + |
| 417 | + /** |
| 418 | + * Get a list of EXIF metadata items which should be displayed when |
| 419 | + * the metadata table is collapsed. |
| 420 | + * |
| 421 | + * @return array of strings |
| 422 | + * @access private |
| 423 | + */ |
| 424 | + function visibleMetadataFields() { |
| 425 | + $fields = array(); |
| 426 | + $lines = explode( "\n", wfMsg( 'metadata-fields' ) ); |
| 427 | + foreach( $lines as $line ) { |
| 428 | + $matches = array(); |
| 429 | + if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) { |
| 430 | + $fields[] = $matches[1]; |
| 431 | + } |
| 432 | + } |
| 433 | + $fields = array_map( 'strtolower', $fields ); |
| 434 | + return $fields; |
| 435 | + } |
| 436 | + |
| 437 | + /** |
| 438 | + * Get an array structure that looks like this: |
| 439 | + * |
| 440 | + * array( |
| 441 | + * 'visible' => array( |
| 442 | + * 'Human-readable name' => 'Human readable value', |
| 443 | + * ... |
| 444 | + * ), |
| 445 | + * 'collapsed' => array( |
| 446 | + * 'Human-readable name' => 'Human readable value', |
| 447 | + * ... |
| 448 | + * ) |
| 449 | + * ) |
| 450 | + * The UI will format this into a table where the visible fields are always |
| 451 | + * visible, and the collapsed fields are optionally visible. |
| 452 | + * |
| 453 | + * The function should return false if there is no metadata to display. |
| 454 | + */ |
| 455 | + |
| 456 | + function formatMetadata( $image ) { |
| 457 | + $result = array( |
| 458 | + 'visible' => array(), |
| 459 | + 'collapsed' => array() |
| 460 | + ); |
| 461 | + $metadata = $image->getMetadata(); |
| 462 | + if ( !$metadata ) { |
| 463 | + return false; |
| 464 | + } |
| 465 | + $exif = unserialize( $metadata ); |
| 466 | + $exif = $exif['exif']; |
| 467 | + if ( !$exif ) { |
| 468 | + return false; |
| 469 | + } |
| 470 | + unset( $exif['MEDIAWIKI_EXIF_VERSION'] ); |
| 471 | + $format = new FormatExif( $exif ); |
| 472 | + |
| 473 | + $formatted = $format->getFormattedData(); |
| 474 | + // Sort fields into visible and collapsed |
| 475 | + $visibleFields = $this->visibleMetadataFields(); |
| 476 | + foreach ( $formatted as $name => $value ) { |
| 477 | + $tag = strtolower( $name ); |
| 478 | + self::addMeta( $result, |
| 479 | + in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed', |
| 480 | + 'exif', |
| 481 | + $tag, |
| 482 | + htmlspecialchars($value) |
| 483 | + ); |
| 484 | + } |
| 485 | + $meta = unserialize($metadata); |
| 486 | + if(isset($meta['errors'])) { |
| 487 | + $errors = array(); |
| 488 | + foreach($meta['errors'] as $error) { |
| 489 | + $errors[] = htmlspecialchars($error); |
| 490 | + } |
| 491 | + self::addMeta( $result, |
| 492 | + 'collapsed', |
| 493 | + 'identify', |
| 494 | + 'error', |
| 495 | + join('<br />', $errors) |
| 496 | + ); |
| 497 | + } |
| 498 | + if(isset($meta['warnings'])) { |
| 499 | + $warnings = array(); |
| 500 | + foreach($meta['warnings'] as $warning) { |
| 501 | + $warnings[] = htmlspecialchars($warning); |
| 502 | + } |
| 503 | + self::addMeta( $result, |
| 504 | + 'collapsed', |
| 505 | + 'identify', |
| 506 | + 'warning', |
| 507 | + join('<br />', $warnings) |
| 508 | + ); |
| 509 | + } |
| 510 | + return $result; |
| 511 | + } |
| 512 | + |
| 513 | + /** |
| 514 | + * Returns a PagedTiffImage or create a new one if don´t exist. |
| 515 | + */ |
| 516 | + static function getTiffImage( $image, $path ) { |
| 517 | + if ( !$image ) |
| 518 | + $tiffimg = new PagedTiffImage( $path ); |
| 519 | + elseif ( !isset( $image->tiffImage ) ) |
| 520 | + $tiffimg = $image->tiffImage = new PagedTiffImage( $path ); |
| 521 | + else |
| 522 | + $tiffimg = $image->tiffImage; |
| 523 | + |
| 524 | + return $tiffimg; |
| 525 | + } |
| 526 | + |
| 527 | + /** |
| 528 | + * Returns an Array with the Image-Metadata. |
| 529 | + */ |
| 530 | + function getMetaArray( $image ) { |
| 531 | + if ( isset( $image->tiffMetaArray ) ) |
| 532 | + return $image->tiffMetaArray; |
| 533 | + |
| 534 | + $metadata = $image->getMetadata(); |
| 535 | + |
| 536 | + if ( !$this->isMetadataValid( $image, $metadata ) ) { |
| 537 | + wfDebug( "Tiff metadata is invalid or missing, should have been fixed in upgradeRow\n" ); |
| 538 | + return false; |
| 539 | + } |
| 540 | + |
| 541 | + wfProfileIn( __METHOD__ ); |
| 542 | + wfSuppressWarnings(); |
| 543 | + $image->tiffMetaArray = unserialize( $metadata ); |
| 544 | + wfRestoreWarnings(); |
| 545 | + wfProfileOut( __METHOD__ ); |
| 546 | + |
| 547 | + return $image->tiffMetaArray; |
| 548 | + } |
| 549 | + |
| 550 | + /** |
| 551 | + * Get an associative array of page dimensions |
| 552 | + * Currently "width" and "height" are understood, but this might be |
| 553 | + * expanded in the future. |
| 554 | + * Returns false if unknown or if the document is not multi-page. |
| 555 | + */ |
| 556 | + function getPageDimensions( $image, $page ) { |
| 557 | + if(!$page) { $page=1; } // makeImageLink2 (Linker.php) sets $page to false if no page parameter in wiki code is set |
| 558 | + $data = $this->getMetaArray( $image ); |
| 559 | + return PagedTiffImage::getPageSize( $data, $page ); |
| 560 | + } |
| 561 | +} |
Property changes on: trunk/extensions/PagedTiffHandler/PagedTiffHandler_body.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 562 | + native |
Index: trunk/extensions/PagedTiffHandler/PagedTiffHandler.i18n.php |
— | — | @@ -0,0 +1,65 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation file for extension PagedTiffHandler. |
| 5 | + * |
| 6 | + * @addtogroup Extensions |
| 7 | + */ |
| 8 | + |
| 9 | +$messages = array(); |
| 10 | + |
| 11 | +/** English (English) |
| 12 | + * @author Hallo Welt! - Medienwerkstatt GmbH |
| 13 | + */ |
| 14 | +$messages['en'] = array( |
| 15 | + 'tiff-desc' => 'Handler for viewing TIFF files in image mode', |
| 16 | + 'tiff_no_metadata' => 'Cannot get metadata from TIFF', |
| 17 | + 'tiff_page_error' => 'Page number not in range', |
| 18 | + 'tiff_too_many_embed_files' => 'The image contains too many embedded files.', |
| 19 | + 'tiff_sourcefile_too_large' => 'The resolution of the source file is too large. No thumbnail will be generated.', |
| 20 | + 'tiff_file_too_large' => 'The uploaded file is too large and was rejected.', |
| 21 | + 'tiff_out_of_service' => 'The uploaded file could not be processed. ImageMagick is not available.', |
| 22 | + 'tiff_too_much_meta' => 'Metadata uses too much space.', |
| 23 | + 'tiff_error_cached' => 'This file can only be rerendered after the the caching interval.', |
| 24 | + 'tiff_size_error' => 'The reported file size does not match the actual file size.', |
| 25 | + 'tiff_script_detected' => 'The uploaded file contains scripts.', |
| 26 | + 'tiff_bad_file' => 'The uploaded file contains errors.', |
| 27 | + 'tiff-file-info-size' => '(page $5, $1 × $2 pixel, file size: $3, MIME type: $4)', |
| 28 | + ); |
| 29 | + |
| 30 | +/** Message documentation (Message documentation) |
| 31 | + * @author Hallo Welt! - Medienwerkstatt GmbH |
| 32 | + */ |
| 33 | +$messages['qqq'] = array( |
| 34 | + 'tiff-desc' => 'Short description of the extension, shown in [[Special:Version]]. Do not translate or change links.', |
| 35 | + 'tiff_no_metadata' => 'Error message shown when no metadata extraction is not possible', |
| 36 | + 'tiff_page_error' => 'Error message shown when page number is out of range', |
| 37 | + 'tiff_too_many_embed_files' => 'Error message shown when the uploaded image contains too many embedded files.', |
| 38 | + 'tiff_sourcefile_too_large' => 'Error message shown when the resolution of the source file is too large.', |
| 39 | + 'tiff_file_too_large' => 'Error message shown when the uploaded file is too large.', |
| 40 | + 'tiff_out_of_service' => 'Error message shown when the uploaded file could not be processed by external renderer (ImageMagick).', |
| 41 | + 'tiff_too_much_meta' => 'Error message shown when the metadata uses too much space.', |
| 42 | + 'tiff_error_cached' => 'Error message shown when a error occurres and it is cached.', |
| 43 | + 'tiff_size_error' => 'Error message shown when the reported file size does not match the actual file size.', |
| 44 | + 'tiff_script_detected' => 'Error message shown when the uploaded file contains scripts.', |
| 45 | + 'tiff_bad_file' => 'Error message shown when the uploaded file contains errors.', |
| 46 | + 'tiff-file-info-size' => 'Information about the image dimensions etc. on image page. Extended by page information', |
| 47 | +); |
| 48 | + |
| 49 | +/** German (Deutsch) |
| 50 | + * @author Hallo Welt! - Medienwerkstatt GmbH |
| 51 | + */ |
| 52 | +$messages['de'] = array( |
| 53 | + 'tiff-desc' => 'Schnittstelle für die Ansicht von TIFF-Dateien im Bilder-Modus', |
| 54 | + 'tiff_no_metadata' => 'Keine Metadaten im TIFF vorhanden.', |
| 55 | + 'tiff_page_error' => 'Seitenzahl außerhalb des Dokumentes.', |
| 56 | + 'tiff_too_much_embed_files' => 'Die Datei enthält zu viele eingebettete Dateien.', |
| 57 | + 'tiff_sourcefile_too_large' => 'Die Quelldatei hat eine zu hohe Auflösung. Es wird kein Thumbnail generiert.', |
| 58 | + 'tiff_file_too_large' => 'Die hochgeladene Datei ist zu groß und wurde abgewiesen.', |
| 59 | + 'tiff_out_of_service' => 'Die hochgeladene Datei konnte nicht verarbeitet werden. ImageMagick ist nicht verfügbar.', |
| 60 | + 'tiff_too_much_meta' => 'Die Metadaten benötigen zu viel Speicherplatz.', |
| 61 | + 'tiff_error_cached' => 'Dies Datei kann erst nach Ablauf der Caching-Periode neu gerendert werden.', |
| 62 | + 'tiff_size_error' => 'Die errechnete Größe der Datei stimmt nicht mit der tatsächlichen überein.', |
| 63 | + 'tiff_script_detected' => 'Die hochgeladene Datei enthält Skripte.', |
| 64 | + 'tiff_bad_file' => 'Die hochgeladene Datei ist fehlerhaft.', |
| 65 | + 'tiff-file-info-size' => '(Seite $5, $1 × $2 Pixel, Dateigröße: $3, MIME-Typ: $4)', |
| 66 | +); |
\ No newline at end of file |
Property changes on: trunk/extensions/PagedTiffHandler/PagedTiffHandler.i18n.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 67 | + native |
Index: trunk/extensions/PagedTiffHandler/PagedTiffHandler.php |
— | — | @@ -0,0 +1,106 @@ |
| 2 | +<?php |
| 3 | + /** |
| 4 | + * Copyright (C) Wikimedia Deuschland, 2009 |
| 5 | + * Authors Hallo Welt! Medienwerkstatt GmbH |
| 6 | + * Authors Sebastian Ulbricht, Daniel Lynge, Marc Reymann, Markus Glaser |
| 7 | + * |
| 8 | + * This program is free software; you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation; either version 2 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU General Public License along |
| 19 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 20 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 21 | + * http://www.gnu.org/copyleft/gpl.html |
| 22 | + * |
| 23 | + */ |
| 24 | + |
| 25 | +//error_reporting(E_ALL); |
| 26 | +# Not a valid entry point, skip unless MEDIAWIKI is defined |
| 27 | +if (!defined('MEDIAWIKI')) { |
| 28 | + echo "PagedTiffHandler extension"; |
| 29 | + exit(1); |
| 30 | +} |
| 31 | + |
| 32 | +/** Add to LocalSettings.php |
| 33 | +require_once("$IP/extensions/PagedTiffHandler/PagedTiffHandler.php"); |
| 34 | + |
| 35 | +$wgUseImageMagick = true; |
| 36 | +$wgImageMagickConvertCommand = "C:\Program Files\ImageMagick-6.5.6-Q8\convert"; |
| 37 | +$wgImageMagickIdentifyCommand = "C:\Program Files\ImageMagick-6.5.6-Q8\identify"; |
| 38 | +$wgTiffExivCommand = "C:\Program Files\Exiv2\exiv2"; |
| 39 | +$wgMaxUploadSize = 1073741824; |
| 40 | + |
| 41 | + |
| 42 | + */ |
| 43 | + |
| 44 | +$wgExtensionCredits['other'][] = array( |
| 45 | + 'name' => 'Paged Tiff Handler', |
| 46 | + 'svn-date' => '$LastChangedDate: 2008-12-25 00:29:44 +0000 (Thu, 25 Dec 2008) $', |
| 47 | + 'svn-revision' => '$LastChangedRevision: 45011 $', |
| 48 | + 'author' => '[http://www.hallowelt.biz HalloWelt! Medienwerkstatt GmbH], Sebastian Ulbricht, Daniel Lynge, Marc Reymann, Markus Glaser for Wikimedia Deutschland', |
| 49 | + 'description' => 'Handler for viewing paged TIFF files in image mode', |
| 50 | + 'descriptionmsg' => 'tiff-desc', |
| 51 | + 'url' => 'http://www.mediawiki.org/wiki/Extension:PagedTiffHandler', |
| 52 | +); |
| 53 | + |
| 54 | +$wgTiffIdentifyRejectMessages = array( |
| 55 | + '/TIFFErrors?/', |
| 56 | + '/^identify: Compression algorithm does not support random access/', |
| 57 | + '/^identify: Old-style LZW codes, convert file/', |
| 58 | + '/^identify: Sorry, requested compression method is not configured/', |
| 59 | + '/^identify: ThunderDecode: Not enough data at scanline/', |
| 60 | + '/^identify: .+?: Read error on strip/', |
| 61 | + '/^identify: .+?: Can not read TIFF directory/', |
| 62 | + '/^identify: Not a TIFF/', |
| 63 | +); |
| 64 | + |
| 65 | +$wgTiffIdentifyBypassMessages = array( |
| 66 | + //'/TIFFWarnings/', |
| 67 | + //'/TIFFWarning/', |
| 68 | + '/^identify: .*TIFFReadDirectory/', |
| 69 | + '/^identify: .+?: unknown field with tag .+? encountered/' |
| 70 | +); |
| 71 | + |
| 72 | +// Use PHP-TiffReader |
| 73 | +$wgTiffUseTiffReader = true; |
| 74 | +$wgTiffReaderPath = dirname(__FILE__); |
| 75 | +$wgTiffReaderCheckEofForJS = 4; // check the last 4MB for JS |
| 76 | + |
| 77 | +// Path to identify |
| 78 | +$wgImageMagickIdentifyCommand = '/usr/bin/identify'; |
| 79 | +// Path to exiv2 |
| 80 | +$wgTiffExivCommand = '/usr/bin/exiv2'; |
| 81 | +// Use exiv2? |
| 82 | +$wgTiffUseExiv = false; |
| 83 | +// Path to vips |
| 84 | +$wgTiffVipsCommand = '/usr/bin/vips'; |
| 85 | +// Use vips |
| 86 | +$wgTiffUseVips = false; |
| 87 | +// Maximum number of embedded files in tiff image |
| 88 | +$wgTiffMaxEmbedFiles = 10000; |
| 89 | +// Maximum resolution of embedded images (product of width x height pixels) |
| 90 | +$wgTiffMaxEmbedFileResolution = 25600000; // max. Resolution 1600 x 1600 pixels |
| 91 | +// Maximum size of meta data |
| 92 | +$wgTiffMaxMetaSize = 67108864; // 64kB |
| 93 | +// TTL of Cacheentries for Errors |
| 94 | +$wgTiffErrorCacheTTL = 84600; |
| 95 | + |
| 96 | +$wgFileExtensions[] = 'tiff'; |
| 97 | +$wgFileExtensions[] = 'tif'; |
| 98 | + |
| 99 | +$dir = dirname(__FILE__) . '/'; |
| 100 | +$wgExtensionMessagesFiles['PagedTiffHandler'] = $dir . 'PagedTiffHandler.i18n.php'; |
| 101 | +$wgAutoloadClasses['PagedTiffImage'] = $dir . 'PagedTiffHandler.image.php'; |
| 102 | +$wgAutoloadClasses['PagedTiffHandler'] = $dir . 'PagedTiffHandler_body.php'; |
| 103 | +$wgMediaHandlers['image/tiff'] = 'PagedTiffHandler'; |
| 104 | +$wgHooks['UploadVerification'][] = 'PagedTiffHandler::check'; |
| 105 | +$wgHooks['LanguageGetMagic'][] = 'PagedTiffHandler::addTiffLossyMagicWordLang'; |
| 106 | +$wgHooks['PagedTiffHandlerRenderCommand'][] = 'PagedTiffHandler::renderCommand'; |
| 107 | +$wgHooks['PagedTiffHandlerExivCommand'][] = 'PagedTiffImage::exivCommand'; |
\ No newline at end of file |
Property changes on: trunk/extensions/PagedTiffHandler/PagedTiffHandler.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 108 | + native |
Index: trunk/extensions/PagedTiffHandler/PagedTiffHandler.image.php |
— | — | @@ -0,0 +1,243 @@ |
| 2 | +<?php |
| 3 | + /** |
| 4 | + * |
| 5 | + * Copyright (C) Wikimedia Deuschland, 2009 |
| 6 | + * Authors Hallo Welt! Medienwerkstatt GmbH |
| 7 | + * Authors Sebastian Ulbricht, Daniel Lynge, Marc Reymann, Markus Glaser |
| 8 | + * |
| 9 | + * This program is free software; you can redistribute it and/or modify |
| 10 | + * it under the terms of the GNU General Public License as published by |
| 11 | + * the Free Software Foundation; either version 2 of the License, or |
| 12 | + * (at your option) any later version. |
| 13 | + * |
| 14 | + * This program is distributed in the hope that it will be useful, |
| 15 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 17 | + * GNU General Public License for more details. |
| 18 | + * |
| 19 | + * You should have received a copy of the GNU General Public License along |
| 20 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 22 | + * http://www.gnu.org/copyleft/gpl.html |
| 23 | + * |
| 24 | + */ |
| 25 | + |
| 26 | +/** |
| 27 | + * inspired by djvuimage from brion vibber |
| 28 | + * modified and written by xarax |
| 29 | + * adapted to tiff by Hallo Welt! - Medienwerkstatt GmbH |
| 30 | + */ |
| 31 | + |
| 32 | +class PagedTiffImage { |
| 33 | + protected $_meta = NULL; |
| 34 | + protected $mFilename; |
| 35 | + |
| 36 | + function __construct( $filename ) { |
| 37 | + $this->mFilename = $filename; |
| 38 | + } |
| 39 | + |
| 40 | + /** |
| 41 | + * Customize the exiv shell command. |
| 42 | + */ |
| 43 | + static function exivCommand( &$cmd, $filename ) { |
| 44 | + return true; |
| 45 | + } |
| 46 | + |
| 47 | + /** |
| 48 | + * Called by MimeMagick functions. |
| 49 | + */ |
| 50 | + public function isValid() { |
| 51 | + return count($this->retrieveMetaData()); |
| 52 | + } |
| 53 | + |
| 54 | + /** |
| 55 | + * Returns an array that corresponds to the native PHP function getimagesize(). |
| 56 | + */ |
| 57 | + public function getImageSize() { |
| 58 | + $data = $this->retrieveMetaData(); |
| 59 | + $size = $this->getPageSize( $data, 1 ); |
| 60 | + |
| 61 | + if( $size ) { |
| 62 | + $width = $size['width']; |
| 63 | + $height = $size['height']; |
| 64 | + return array( $width, $height, 'Tiff', |
| 65 | + "width=\"$width\" height=\"$height\"" ); |
| 66 | + } |
| 67 | + return false; |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Returns an array with width and height of the tiff page. |
| 72 | + */ |
| 73 | + public static function getPageSize( $data, $page ) { |
| 74 | + if( isset( $data['pages'][$page] ) ) { |
| 75 | + return array('width' => $data['pages'][$page]['width'], |
| 76 | + 'height' => $data['pages'][$page]['height']); |
| 77 | + } |
| 78 | + return false; |
| 79 | + } |
| 80 | + |
| 81 | + /** |
| 82 | + * Reads metadata of the tiff file via shell command and returns an associative array. |
| 83 | + * layout: |
| 84 | + * meta['Pages'] = amount of pages |
| 85 | + * meta['pages'] = metadata per page |
| 86 | + * meta['exif'] = Exif, XMP and IPTC |
| 87 | + * meta['errors'] = identify-errors |
| 88 | + * meta['warnings'] = identify-warnings |
| 89 | + */ |
| 90 | + public function retrieveMetaData() { |
| 91 | + global $wgImageMagickIdentifyCommand, $wgTiffExivCommand, $wgTiffUseExiv, $wgMemc, $wgTiffErrorCacheTTL; |
| 92 | + |
| 93 | + $imgKey = wfMemcKey('PagedTiffHandler-ThumbnailGeneration', $this->mFilename); |
| 94 | + $isCached = $wgMemc->get($imgKey); |
| 95 | + if($isCached) { |
| 96 | + return -1; |
| 97 | + } |
| 98 | + $wgMemc->add($imgKey, 1, $wgTiffErrorCacheTTL); |
| 99 | + |
| 100 | + if($this->_meta === NULL) { |
| 101 | + if ( $wgImageMagickIdentifyCommand ) { |
| 102 | + |
| 103 | + wfProfileIn( 'PagedTiffImage' ); |
| 104 | + /** |
| 105 | + * ImageMagick is used in order to get the basic metadata of embedded files. |
| 106 | + * This is not reliable in exiv2m since it is not possible to name a set of required fields. |
| 107 | + */ |
| 108 | + $cmd = wfEscapeShellArg( $wgImageMagickIdentifyCommand ) . |
| 109 | + ' -format "[BEGIN]page=%p\nalpha=%A\nheight=%h\nwidth=%w\ndepth=%z[END]" ' . |
| 110 | + wfEscapeShellArg( $this->mFilename ) . ' 2>&1'; |
| 111 | + |
| 112 | + wfProfileIn( 'identify' ); |
| 113 | + wfDebug( __METHOD__.": $cmd\n" ); |
| 114 | + $dump = wfShellExec( $cmd, $retval ); |
| 115 | + wfProfileOut( 'identify' ); |
| 116 | + if($retval) { |
| 117 | + return false; |
| 118 | + } |
| 119 | + $this->_meta = $this->convertDumpToArray( $dump ); |
| 120 | + $this->_meta['exif'] = array(); |
| 121 | + |
| 122 | + if($wgTiffUseExiv) { |
| 123 | + $cmd = wfEscapeShellArg( $wgTiffExivCommand ) . |
| 124 | + ' -u -psix -Pnt ' . // read EXIF, XMP, IPTC as name-tag => interpreted data -ignore unknown fields |
| 125 | + // exiv2-doc @link http://www.exiv2.org/sample.html |
| 126 | + wfEscapeShellArg( $this->mFilename ); |
| 127 | + |
| 128 | + wfRunHooks( "PagedTiffHandlerExivCommand", array( &$cmd, $this->mFilename ) ); |
| 129 | + |
| 130 | + wfProfileIn( 'exiv2' ); |
| 131 | + wfDebug( __METHOD__.": $cmd\n" ); |
| 132 | + $dump = wfShellExec( $cmd, $retval ); |
| 133 | + wfProfileOut( 'exiv2' ); |
| 134 | + $result = array(); |
| 135 | + preg_match_all('/(\w+)\s+(.+)/', $dump, $result, PREG_SET_ORDER); |
| 136 | + |
| 137 | + foreach($result as $data) { |
| 138 | + $this->_meta['exif'][$data[1]] = $data[2]; |
| 139 | + } |
| 140 | + } |
| 141 | + else { |
| 142 | + $cmd = wfEscapeShellArg( $wgImageMagickIdentifyCommand ) . |
| 143 | + ' -verbose ' . |
| 144 | + wfEscapeShellArg( $this->mFilename )."[0]"; |
| 145 | + |
| 146 | + wfProfileIn( 'identify -verbose' ); |
| 147 | + wfDebug( __METHOD__.": $cmd\n" ); |
| 148 | + $dump = wfShellExec( $cmd, $retval ); |
| 149 | + wfProfileOut( 'identify -verbose' ); |
| 150 | + $this->_meta['exif'] = $this->parseVerbose($dump); |
| 151 | + } |
| 152 | + wfProfileOut( 'PagedTiffImage' ); |
| 153 | + } |
| 154 | + } |
| 155 | + unset($this->_meta['exif']['Image']); |
| 156 | + unset($this->_meta['exif']['filename']); |
| 157 | + unset($this->_meta['exif']['Base filename']); |
| 158 | + return $this->_meta; |
| 159 | + } |
| 160 | + |
| 161 | + /** |
| 162 | + * helper function of retrieveMetaData(). |
| 163 | + * parses shell return from identify-command into an array. |
| 164 | + */ |
| 165 | + protected function convertDumpToArray( $dump ) { |
| 166 | + global $wgTiffIdentifyRejectMessages, $wgTiffIdentifyBypassMessages; |
| 167 | + if ( strval( $dump ) == '' ) return false; |
| 168 | + $infos = NULL; |
| 169 | + preg_match_all('/\[BEGIN\](.+?)\[END\]/si', $dump, $infos, PREG_SET_ORDER); |
| 170 | + $data = array(); |
| 171 | + $data['Pages'] = count($infos); |
| 172 | + $data['pages'] = array(); |
| 173 | + foreach($infos as $info) { |
| 174 | + $entry = array(); |
| 175 | + $lines = explode("\n", $info[1]); |
| 176 | + foreach($lines as $line) { |
| 177 | + if(trim($line) == '') { |
| 178 | + continue; |
| 179 | + } |
| 180 | + $parts = explode('=', $line); |
| 181 | + $entry[trim($parts[0])] = trim($parts[1]); |
| 182 | + } |
| 183 | + $entry['pixels'] = $entry['height'] * $entry['width']; |
| 184 | + $data['pages'][$entry['page']] = $entry; |
| 185 | + } |
| 186 | + |
| 187 | + $dump = preg_replace('/\[BEGIN\](.+?)\[END\]/si', '', $dump); |
| 188 | + if(strlen($dump)) { |
| 189 | + $errors = explode("\n", $dump); |
| 190 | + foreach($errors as $error) { |
| 191 | + $knownError = false; |
| 192 | + foreach($wgTiffIdentifyRejectMessages as $msg) { |
| 193 | + if (preg_match($msg, trim($error))) { |
| 194 | + $data['errors'][] = $error; |
| 195 | + $knownError = true; |
| 196 | + break; |
| 197 | + } |
| 198 | + } |
| 199 | + if(!$knownError) { |
| 200 | + foreach($wgTiffIdentifyBypassMessages as $msg) { |
| 201 | + if (preg_match($msg, trim($error))) { |
| 202 | + $data['warnings'][] = $error; |
| 203 | + $knownError = true; |
| 204 | + break; |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + if(!$knownError) { |
| 209 | + $data['unknownErrors'][] = $error; |
| 210 | + } |
| 211 | + } |
| 212 | + } |
| 213 | + return $data; |
| 214 | + } |
| 215 | + |
| 216 | + /** |
| 217 | + * helper function of retrieveMetaData(). |
| 218 | + * parses shell return from identify-verbose-command into an array. |
| 219 | + */ |
| 220 | + protected function parseVerbose($dump) { |
| 221 | + $data = array(); |
| 222 | + $dump = explode("\n", $dump); |
| 223 | + $lastwhite = 0; |
| 224 | + $lastkey = false; |
| 225 | + foreach($dump as $line) { |
| 226 | + if(preg_match('/^(\s*?)(\w([\w\s]+?)?):(.*?)$/sim', $line, $res)) { |
| 227 | + if($lastwhite == 0 || strlen($res[1]) == $lastwhite) { |
| 228 | + if(strlen(trim($res[4]))) { |
| 229 | + $data[trim($res[2])] = trim($res[4]); |
| 230 | + } |
| 231 | + else { |
| 232 | + $data[trim($res[2])] = " Data:\n"; |
| 233 | + } |
| 234 | + $lastkey = trim($res[2]); |
| 235 | + $lastwhite = strlen($res[1]); |
| 236 | + } |
| 237 | + else { |
| 238 | + $data[$lastkey] .= $line."\n"; |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + return $data; |
| 243 | + } |
| 244 | +} |
Property changes on: trunk/extensions/PagedTiffHandler/PagedTiffHandler.image.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 245 | + native |