r61478 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r61477‎ | r61478 | r61479 >
Date:23:54, 24 January 2010
Author:mglaser
Status:deferred (Comments)
Tags:
Comment:
Initial upload of PagedTiffHandler. This is still in testing phase.
Modified paths:
  • /trunk/extensions/PagedTiffHandler (added) (history)
  • /trunk/extensions/PagedTiffHandler/PagedTiffHandler.i18n.php (added) (history)
  • /trunk/extensions/PagedTiffHandler/PagedTiffHandler.image.php (added) (history)
  • /trunk/extensions/PagedTiffHandler/PagedTiffHandler.php (added) (history)
  • /trunk/extensions/PagedTiffHandler/PagedTiffHandler_body.php (added) (history)
  • /trunk/extensions/PagedTiffHandler/TiffReader.php (added) (history)

Diff [purge]

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
1333 + 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
1562 + 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
167 + 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
1108 + 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
1245 + native

Follow-up revisions

RevisionCommit summaryAuthorDate
r105275Fix stray quotes from r61478, they cause a file not found error from VIPS. I ...tstarling01:56, 6 December 2011

Comments

#Comment by Raymond (talk | contribs)   17:54, 25 January 2010

Some styling tweaks done in r61494.

Please read our Manual:Coding conventions and run stylize.php over the other files not touched by me (to avoid conflicts).

#Comment by Raymond (talk | contribs)   17:54, 25 January 2010

Some styling tweaks done in r61494.

Please read our Manual:Coding conventions and run stylize.php over the other files not touched by me (to avoid conflicts).

Status & tagging log