MediaWiki  1.23.12
Bitmap.php
Go to the documentation of this file.
1 <?php
29 class BitmapHandler extends ImageHandler {
37  function normaliseParams( $image, &$params ) {
38  if ( !parent::normaliseParams( $image, $params ) ) {
39  return false;
40  }
41 
42  # Obtain the source, pre-rotation dimensions
43  $srcWidth = $image->getWidth( $params['page'] );
44  $srcHeight = $image->getHeight( $params['page'] );
45 
46  # Don't make an image bigger than the source
47  if ( $params['physicalWidth'] >= $srcWidth ) {
48  $params['physicalWidth'] = $srcWidth;
49  $params['physicalHeight'] = $srcHeight;
50 
51  # Skip scaling limit checks if no scaling is required
52  # due to requested size being bigger than source.
53  if ( !$image->mustRender() ) {
54  return true;
55  }
56  }
57 
58  # Check if the file is smaller than the maximum image area for thumbnailing
59  $checkImageAreaHookResult = null;
60  wfRunHooks(
61  'BitmapHandlerCheckImageArea',
62  array( $image, &$params, &$checkImageAreaHookResult )
63  );
64 
65  if ( is_null( $checkImageAreaHookResult ) ) {
66  global $wgMaxImageArea;
67 
68  if ( $srcWidth * $srcHeight > $wgMaxImageArea
69  && !( $image->getMimeType() == 'image/jpeg'
70  && self::getScalerType( false, false ) == 'im' )
71  ) {
72  # Only ImageMagick can efficiently downsize jpg images without loading
73  # the entire file in memory
74  return false;
75  }
76  } else {
77  return $checkImageAreaHookResult;
78  }
79 
80  return true;
81  }
82 
95  public function extractPreRotationDimensions( $params, $rotation ) {
96  if ( $rotation == 90 || $rotation == 270 ) {
97  # We'll resize before rotation, so swap the dimensions again
98  $width = $params['physicalHeight'];
99  $height = $params['physicalWidth'];
100  } else {
101  $width = $params['physicalWidth'];
102  $height = $params['physicalHeight'];
103  }
104 
105  return array( $width, $height );
106  }
107 
116  function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
117  if ( !$this->normaliseParams( $image, $params ) ) {
118  return new TransformParameterError( $params );
119  }
120  # Create a parameter array to pass to the scaler
121  $scalerParams = array(
122  # The size to which the image will be resized
123  'physicalWidth' => $params['physicalWidth'],
124  'physicalHeight' => $params['physicalHeight'],
125  'physicalDimensions' => "{$params['physicalWidth']}x{$params['physicalHeight']}",
126  # The size of the image on the page
127  'clientWidth' => $params['width'],
128  'clientHeight' => $params['height'],
129  # Comment as will be added to the Exif of the thumbnail
130  'comment' => isset( $params['descriptionUrl'] )
131  ? "File source: {$params['descriptionUrl']}"
132  : '',
133  # Properties of the original image
134  'srcWidth' => $image->getWidth(),
135  'srcHeight' => $image->getHeight(),
136  'mimeType' => $image->getMimeType(),
137  'dstPath' => $dstPath,
138  'dstUrl' => $dstUrl,
139  );
140 
141  # Determine scaler type
142  $scaler = self::getScalerType( $dstPath );
143 
144  wfDebug( __METHOD__ . ": creating {$scalerParams['physicalDimensions']} " .
145  "thumbnail at $dstPath using scaler $scaler\n" );
146 
147  if ( !$image->mustRender() &&
148  $scalerParams['physicalWidth'] == $scalerParams['srcWidth']
149  && $scalerParams['physicalHeight'] == $scalerParams['srcHeight']
150  ) {
151 
152  # normaliseParams (or the user) wants us to return the unscaled image
153  wfDebug( __METHOD__ . ": returning unscaled image\n" );
154 
155  return $this->getClientScalingThumbnailImage( $image, $scalerParams );
156  }
157 
158  if ( $scaler == 'client' ) {
159  # Client-side image scaling, use the source URL
160  # Using the destination URL in a TRANSFORM_LATER request would be incorrect
161  return $this->getClientScalingThumbnailImage( $image, $scalerParams );
162  }
163 
164  if ( $flags & self::TRANSFORM_LATER ) {
165  wfDebug( __METHOD__ . ": Transforming later per flags.\n" );
166  $params = array(
167  'width' => $scalerParams['clientWidth'],
168  'height' => $scalerParams['clientHeight']
169  );
170 
171  return new ThumbnailImage( $image, $dstUrl, false, $params );
172  }
173 
174  # Try to make a target path for the thumbnail
175  if ( !wfMkdirParents( dirname( $dstPath ), null, __METHOD__ ) ) {
176  wfDebug( __METHOD__ . ": Unable to create thumbnail destination " .
177  "directory, falling back to client scaling\n" );
178 
179  return $this->getClientScalingThumbnailImage( $image, $scalerParams );
180  }
181 
182  # Transform functions and binaries need a FS source file
183  $scalerParams['srcPath'] = $image->getLocalRefPath();
184  if ( $scalerParams['srcPath'] === false ) { // Failed to get local copy
185  wfDebugLog( 'thumbnail',
186  sprintf( 'Thumbnail failed on %s: could not get local copy of "%s"',
187  wfHostname(), $image->getName() ) );
188 
189  return new MediaTransformError( 'thumbnail_error',
190  $scalerParams['clientWidth'], $scalerParams['clientHeight'],
191  wfMessage( 'filemissing' )->text()
192  );
193  }
194 
195  # Try a hook
196  $mto = null;
197  wfRunHooks( 'BitmapHandlerTransform', array( $this, $image, &$scalerParams, &$mto ) );
198  if ( !is_null( $mto ) ) {
199  wfDebug( __METHOD__ . ": Hook to BitmapHandlerTransform created an mto\n" );
200  $scaler = 'hookaborted';
201  }
202 
203  switch ( $scaler ) {
204  case 'hookaborted':
205  # Handled by the hook above
206 
207  $err = $mto->isError() ? $mto : false;
208  break;
209  case 'im':
210  $err = $this->transformImageMagick( $image, $scalerParams );
211  break;
212  case 'custom':
213  $err = $this->transformCustom( $image, $scalerParams );
214  break;
215  case 'imext':
216  $err = $this->transformImageMagickExt( $image, $scalerParams );
217  break;
218  case 'gd':
219  default:
220  $err = $this->transformGd( $image, $scalerParams );
221  break;
222  }
223 
224  # Remove the file if a zero-byte thumbnail was created, or if there was an error
225  $removed = $this->removeBadFile( $dstPath, (bool)$err );
226  if ( $err ) {
227  # transform returned MediaTransforError
228  return $err;
229  } elseif ( $removed ) {
230  # Thumbnail was zero-byte and had to be removed
231  return new MediaTransformError( 'thumbnail_error',
232  $scalerParams['clientWidth'], $scalerParams['clientHeight'],
233  wfMessage( 'unknown-error' )->text()
234  );
235  } elseif ( $mto ) {
236  return $mto;
237  } else {
238  $params = array(
239  'width' => $scalerParams['clientWidth'],
240  'height' => $scalerParams['clientHeight']
241  );
242 
243  return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
244  }
245  }
246 
255  protected static function getScalerType( $dstPath, $checkDstPath = true ) {
256  global $wgUseImageResize, $wgUseImageMagick, $wgCustomConvertCommand;
257 
258  if ( !$dstPath && $checkDstPath ) {
259  # No output path available, client side scaling only
260  $scaler = 'client';
261  } elseif ( !$wgUseImageResize ) {
262  $scaler = 'client';
263  } elseif ( $wgUseImageMagick ) {
264  $scaler = 'im';
265  } elseif ( $wgCustomConvertCommand ) {
266  $scaler = 'custom';
267  } elseif ( function_exists( 'imagecreatetruecolor' ) ) {
268  $scaler = 'gd';
269  } elseif ( class_exists( 'Imagick' ) ) {
270  $scaler = 'imext';
271  } else {
272  $scaler = 'client';
273  }
274 
275  return $scaler;
276  }
277 
288  protected function getClientScalingThumbnailImage( $image, $scalerParams ) {
289  $params = array(
290  'width' => $scalerParams['clientWidth'],
291  'height' => $scalerParams['clientHeight']
292  );
293 
294  return new ThumbnailImage( $image, $image->getURL(), null, $params );
295  }
296 
305  protected function transformImageMagick( $image, $params ) {
306  # use ImageMagick
307  global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea,
308  $wgImageMagickTempDir, $wgImageMagickConvertCommand;
309 
310  $quality = array();
311  $sharpen = array();
312  $scene = false;
313  $animation_pre = array();
314  $animation_post = array();
315  $decoderHint = array();
316  if ( $params['mimeType'] == 'image/jpeg' ) {
317  $quality = array( '-quality', '80' ); // 80%
318  # Sharpening, see bug 6193
319  if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
320  / ( $params['srcWidth'] + $params['srcHeight'] )
321  < $wgSharpenReductionThreshold
322  ) {
323  $sharpen = array( '-sharpen', $wgSharpenParameter );
324  }
325  if ( version_compare( $this->getMagickVersion(), "6.5.6" ) >= 0 ) {
326  // JPEG decoder hint to reduce memory, available since IM 6.5.6-2
327  $decoderHint = array( '-define', "jpeg:size={$params['physicalDimensions']}" );
328  }
329  } elseif ( $params['mimeType'] == 'image/png' ) {
330  $quality = array( '-quality', '95' ); // zlib 9, adaptive filtering
331 
332  } elseif ( $params['mimeType'] == 'image/gif' ) {
333  if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
334  // Extract initial frame only; we're so big it'll
335  // be a total drag. :P
336  $scene = 0;
337  } elseif ( $this->isAnimatedImage( $image ) ) {
338  // Coalesce is needed to scale animated GIFs properly (bug 1017).
339  $animation_pre = array( '-coalesce' );
340  // We optimize the output, but -optimize is broken,
341  // use optimizeTransparency instead (bug 11822)
342  if ( version_compare( $this->getMagickVersion(), "6.3.5" ) >= 0 ) {
343  $animation_post = array( '-fuzz', '5%', '-layers', 'optimizeTransparency' );
344  }
345  }
346  } elseif ( $params['mimeType'] == 'image/x-xcf' ) {
347  $animation_post = array( '-layers', 'merge' );
348  }
349 
350  // Use one thread only, to avoid deadlock bugs on OOM
351  $env = array( 'OMP_NUM_THREADS' => 1 );
352  if ( strval( $wgImageMagickTempDir ) !== '' ) {
353  $env['MAGICK_TMPDIR'] = $wgImageMagickTempDir;
354  }
355 
356  $rotation = $this->getRotation( $image );
357  list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
358 
359  $cmd = call_user_func_array( 'wfEscapeShellArg', array_merge(
360  array( $wgImageMagickConvertCommand ),
361  $quality,
362  // Specify white background color, will be used for transparent images
363  // in Internet Explorer/Windows instead of default black.
364  array( '-background', 'white' ),
365  $decoderHint,
366  array( $this->escapeMagickInput( $params['srcPath'], $scene ) ),
367  $animation_pre,
368  // For the -thumbnail option a "!" is needed to force exact size,
369  // or ImageMagick may decide your ratio is wrong and slice off
370  // a pixel.
371  array( '-thumbnail', "{$width}x{$height}!" ),
372  // Add the source url as a comment to the thumb, but don't add the flag if there's no comment
373  ( $params['comment'] !== ''
374  ? array( '-set', 'comment', $this->escapeMagickProperty( $params['comment'] ) )
375  : array() ),
376  // T108616: Avoid exposure of local file path
377  array( '+set', 'Thumb::URI'),
378  array( '-depth', 8 ),
379  $sharpen,
380  array( '-rotate', "-$rotation" ),
381  $animation_post,
382  array( $this->escapeMagickOutput( $params['dstPath'] ) ) ) );
383 
384  wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
385  wfProfileIn( 'convert' );
386  $retval = 0;
387  $err = wfShellExecWithStderr( $cmd, $retval, $env );
388  wfProfileOut( 'convert' );
389 
390  if ( $retval !== 0 ) {
391  $this->logErrorForExternalProcess( $retval, $err, $cmd );
392 
393  return $this->getMediaTransformError( $params, "$err\nError code: $retval" );
394  }
395 
396  return false; # No error
397  }
398 
407  protected function transformImageMagickExt( $image, $params ) {
408  global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea;
409 
410  try {
411  $im = new Imagick();
412  $im->readImage( $params['srcPath'] );
413 
414  if ( $params['mimeType'] == 'image/jpeg' ) {
415  // Sharpening, see bug 6193
416  if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
417  / ( $params['srcWidth'] + $params['srcHeight'] )
418  < $wgSharpenReductionThreshold
419  ) {
420  // Hack, since $wgSharpenParamater is written specifically for the command line convert
421  list( $radius, $sigma ) = explode( 'x', $wgSharpenParameter );
422  $im->sharpenImage( $radius, $sigma );
423  }
424  $im->setCompressionQuality( 80 );
425  } elseif ( $params['mimeType'] == 'image/png' ) {
426  $im->setCompressionQuality( 95 );
427  } elseif ( $params['mimeType'] == 'image/gif' ) {
428  if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
429  // Extract initial frame only; we're so big it'll
430  // be a total drag. :P
431  $im->setImageScene( 0 );
432  } elseif ( $this->isAnimatedImage( $image ) ) {
433  // Coalesce is needed to scale animated GIFs properly (bug 1017).
434  $im = $im->coalesceImages();
435  }
436  }
437 
438  $rotation = $this->getRotation( $image );
439  list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
440 
441  $im->setImageBackgroundColor( new ImagickPixel( 'white' ) );
442 
443  // Call Imagick::thumbnailImage on each frame
444  foreach ( $im as $i => $frame ) {
445  if ( !$frame->thumbnailImage( $width, $height, /* fit */ false ) ) {
446  return $this->getMediaTransformError( $params, "Error scaling frame $i" );
447  }
448  }
449  $im->setImageDepth( 8 );
450 
451  if ( $rotation ) {
452  if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
453  return $this->getMediaTransformError( $params, "Error rotating $rotation degrees" );
454  }
455  }
456 
457  if ( $this->isAnimatedImage( $image ) ) {
458  wfDebug( __METHOD__ . ": Writing animated thumbnail\n" );
459  // This is broken somehow... can't find out how to fix it
460  $result = $im->writeImages( $params['dstPath'], true );
461  } else {
462  $result = $im->writeImage( $params['dstPath'] );
463  }
464  if ( !$result ) {
465  return $this->getMediaTransformError( $params,
466  "Unable to write thumbnail to {$params['dstPath']}" );
467  }
468  } catch ( ImagickException $e ) {
469  return $this->getMediaTransformError( $params, $e->getMessage() );
470  }
471 
472  return false;
473  }
474 
483  protected function transformCustom( $image, $params ) {
484  # Use a custom convert command
485  global $wgCustomConvertCommand;
486 
487  # Variables: %s %d %w %h
488  $src = wfEscapeShellArg( $params['srcPath'] );
489  $dst = wfEscapeShellArg( $params['dstPath'] );
490  $cmd = $wgCustomConvertCommand;
491  $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
492  $cmd = str_replace( '%h', wfEscapeShellArg( $params['physicalHeight'] ),
493  str_replace( '%w', wfEscapeShellArg( $params['physicalWidth'] ), $cmd ) ); # Size
494  wfDebug( __METHOD__ . ": Running custom convert command $cmd\n" );
495  wfProfileIn( 'convert' );
496  $retval = 0;
497  $err = wfShellExecWithStderr( $cmd, $retval );
498  wfProfileOut( 'convert' );
499 
500  if ( $retval !== 0 ) {
501  $this->logErrorForExternalProcess( $retval, $err, $cmd );
502 
503  return $this->getMediaTransformError( $params, $err );
504  }
505 
506  return false; # No error
507  }
508 
516  public function getMediaTransformError( $params, $errMsg ) {
517  return new MediaTransformError( 'thumbnail_error', $params['clientWidth'],
518  $params['clientHeight'], $errMsg );
519  }
520 
529  protected function transformGd( $image, $params ) {
530  # Use PHP's builtin GD library functions.
531  #
532  # First find out what kind of file this is, and select the correct
533  # input routine for this.
534 
535  $typemap = array(
536  'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
537  'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor',
538  array( __CLASS__, 'imageJpegWrapper' ) ),
539  'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
540  'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
541  'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
542  );
543  if ( !isset( $typemap[$params['mimeType']] ) ) {
544  $err = 'Image type not supported';
545  wfDebug( "$err\n" );
546  $errMsg = wfMessage( 'thumbnail_image-type' )->text();
547 
548  return $this->getMediaTransformError( $params, $errMsg );
549  }
550  list( $loader, $colorStyle, $saveType ) = $typemap[$params['mimeType']];
551 
552  if ( !function_exists( $loader ) ) {
553  $err = "Incomplete GD library configuration: missing function $loader";
554  wfDebug( "$err\n" );
555  $errMsg = wfMessage( 'thumbnail_gd-library', $loader )->text();
556 
557  return $this->getMediaTransformError( $params, $errMsg );
558  }
559 
560  if ( !file_exists( $params['srcPath'] ) ) {
561  $err = "File seems to be missing: {$params['srcPath']}";
562  wfDebug( "$err\n" );
563  $errMsg = wfMessage( 'thumbnail_image-missing', $params['srcPath'] )->text();
564 
565  return $this->getMediaTransformError( $params, $errMsg );
566  }
567 
568  $src_image = call_user_func( $loader, $params['srcPath'] );
569 
570  $rotation = function_exists( 'imagerotate' ) ? $this->getRotation( $image ) : 0;
571  list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
572  $dst_image = imagecreatetruecolor( $width, $height );
573 
574  // Initialise the destination image to transparent instead of
575  // the default solid black, to support PNG and GIF transparency nicely
576  $background = imagecolorallocate( $dst_image, 0, 0, 0 );
577  imagecolortransparent( $dst_image, $background );
578  imagealphablending( $dst_image, false );
579 
580  if ( $colorStyle == 'palette' ) {
581  // Don't resample for paletted GIF images.
582  // It may just uglify them, and completely breaks transparency.
583  imagecopyresized( $dst_image, $src_image,
584  0, 0, 0, 0,
585  $width, $height,
586  imagesx( $src_image ), imagesy( $src_image ) );
587  } else {
588  imagecopyresampled( $dst_image, $src_image,
589  0, 0, 0, 0,
590  $width, $height,
591  imagesx( $src_image ), imagesy( $src_image ) );
592  }
593 
594  if ( $rotation % 360 != 0 && $rotation % 90 == 0 ) {
595  $rot_image = imagerotate( $dst_image, $rotation, 0 );
596  imagedestroy( $dst_image );
597  $dst_image = $rot_image;
598  }
599 
600  imagesavealpha( $dst_image, true );
601 
602  call_user_func( $saveType, $dst_image, $params['dstPath'] );
603  imagedestroy( $dst_image );
604  imagedestroy( $src_image );
605 
606  return false; # No error
607  }
608 
615  function escapeMagickProperty( $s ) {
616  // Double the backslashes
617  $s = str_replace( '\\', '\\\\', $s );
618  // Double the percents
619  $s = str_replace( '%', '%%', $s );
620  // Escape initial - or @
621  if ( strlen( $s ) > 0 && ( $s[0] === '-' || $s[0] === '@' ) ) {
622  $s = '\\' . $s;
623  }
624 
625  return $s;
626  }
627 
645  function escapeMagickInput( $path, $scene = false ) {
646  # Die on initial metacharacters (caller should prepend path)
647  $firstChar = substr( $path, 0, 1 );
648  if ( $firstChar === '~' || $firstChar === '@' ) {
649  throw new MWException( __METHOD__ . ': cannot escape this path name' );
650  }
651 
652  # Escape glob chars
653  $path = preg_replace( '/[*?\[\]{}]/', '\\\\\0', $path );
654 
655  return $this->escapeMagickPath( $path, $scene );
656  }
657 
665  function escapeMagickOutput( $path, $scene = false ) {
666  $path = str_replace( '%', '%%', $path );
667 
668  return $this->escapeMagickPath( $path, $scene );
669  }
670 
680  protected function escapeMagickPath( $path, $scene = false ) {
681  # Die on format specifiers (other than drive letters). The regex is
682  # meant to match all the formats you get from "convert -list format"
683  if ( preg_match( '/^([a-zA-Z0-9-]+):/', $path, $m ) ) {
684  if ( wfIsWindows() && is_dir( $m[0] ) ) {
685  // OK, it's a drive letter
686  // ImageMagick has a similar exception, see IsMagickConflict()
687  } else {
688  throw new MWException( __METHOD__ . ': unexpected colon character in path name' );
689  }
690  }
691 
692  # If there are square brackets, add a do-nothing scene specification
693  # to force a literal interpretation
694  if ( $scene === false ) {
695  if ( strpos( $path, '[' ) !== false ) {
696  $path .= '[0--1]';
697  }
698  } else {
699  $path .= "[$scene]";
700  }
701 
702  return $path;
703  }
704 
711  protected function getMagickVersion() {
712  global $wgMemc;
713 
714  $cache = $wgMemc->get( "imagemagick-version" );
715  if ( !$cache ) {
716  global $wgImageMagickConvertCommand;
717  $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . ' -version';
718  wfDebug( __METHOD__ . ": Running convert -version\n" );
719  $retval = '';
720  $return = wfShellExec( $cmd, $retval );
721  $x = preg_match( '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', $return, $matches );
722  if ( $x != 1 ) {
723  wfDebug( __METHOD__ . ": ImageMagick version check failed\n" );
724 
725  return null;
726  }
727  $wgMemc->set( "imagemagick-version", $matches[1], 3600 );
728 
729  return $matches[1];
730  }
731 
732  return $cache;
733  }
734 
735  static function imageJpegWrapper( $dst_image, $thumbPath ) {
736  imageinterlace( $dst_image );
737  imagejpeg( $dst_image, $thumbPath, 95 );
738  }
739 
745  public static function canRotate() {
746  $scaler = self::getScalerType( null, false );
747  switch ( $scaler ) {
748  case 'im':
749  # ImageMagick supports autorotation
750  return true;
751  case 'imext':
752  # Imagick::rotateImage
753  return true;
754  case 'gd':
755  # GD's imagerotate function is used to rotate images, but not
756  # all precompiled PHP versions have that function
757  return function_exists( 'imagerotate' );
758  default:
759  # Other scalers don't support rotation
760  return false;
761  }
762  }
763 
768  public static function autoRotateEnabled() {
769  global $wgEnableAutoRotation;
770 
771  if ( $wgEnableAutoRotation === null ) {
772  // Only enable auto-rotation when the bitmap handler can rotate
773  $wgEnableAutoRotation = BitmapHandler::canRotate();
774  }
775 
776  return $wgEnableAutoRotation;
777  }
778 
786  public function rotate( $file, $params ) {
787  global $wgImageMagickConvertCommand;
788 
789  $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;
790  $scene = false;
791 
792  $scaler = self::getScalerType( null, false );
793  switch ( $scaler ) {
794  case 'im':
795  $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . " " .
796  wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
797  " -rotate " . wfEscapeShellArg( "-$rotation" ) . " " .
798  wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) );
799  wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
800  wfProfileIn( 'convert' );
801  $retval = 0;
802  $err = wfShellExecWithStderr( $cmd, $retval );
803  wfProfileOut( 'convert' );
804  if ( $retval !== 0 ) {
805  $this->logErrorForExternalProcess( $retval, $err, $cmd );
806 
807  return new MediaTransformError( 'thumbnail_error', 0, 0, $err );
808  }
809 
810  return false;
811  case 'imext':
812  $im = new Imagick();
813  $im->readImage( $params['srcPath'] );
814  if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
815  return new MediaTransformError( 'thumbnail_error', 0, 0,
816  "Error rotating $rotation degrees" );
817  }
818  $result = $im->writeImage( $params['dstPath'] );
819  if ( !$result ) {
820  return new MediaTransformError( 'thumbnail_error', 0, 0,
821  "Unable to write image to {$params['dstPath']}" );
822  }
823 
824  return false;
825  default:
826  return new MediaTransformError( 'thumbnail_error', 0, 0,
827  "$scaler rotation not implemented" );
828  }
829  }
830 
838  public function mustRender( $file ) {
839  return self::canRotate() && $this->getRotation( $file ) != 0;
840  }
841 }
MediaHandler\removeBadFile
removeBadFile( $dstPath, $retval=0)
Check for zero-sized thumbnails.
Definition: MediaHandler.php:688
BitmapHandler\autoRotateEnabled
static autoRotateEnabled()
Definition: Bitmap.php:768
BitmapHandler\doTransform
doTransform( $image, $dstPath, $dstUrl, $params, $flags=0)
Definition: Bitmap.php:116
MediaTransformError
Basic media transform error class.
Definition: MediaTransformOutput.php:409
ThumbnailImage
Media transform output for images.
Definition: MediaTransformOutput.php:250
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
wfShellExec
wfShellExec( $cmd, &$retval=null, $environ=array(), $limits=array(), $options=array())
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
Definition: GlobalFunctions.php:2851
of
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
Definition: globals.txt:10
BitmapHandler\transformImageMagickExt
transformImageMagickExt( $image, $params)
Transform an image using the Imagick PHP extension.
Definition: Bitmap.php:407
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
BitmapHandler\escapeMagickPath
escapeMagickPath( $path, $scene=false)
Armour a string against ImageMagick's GetPathComponent().
Definition: Bitmap.php:680
wfMkdirParents
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
Definition: GlobalFunctions.php:2637
BitmapHandler\imageJpegWrapper
static imageJpegWrapper( $dst_image, $thumbPath)
Definition: Bitmap.php:735
$wgMemc
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest to get request data $wgMemc
Definition: globals.txt:25
BitmapHandler\escapeMagickProperty
escapeMagickProperty( $s)
Escape a string for ImageMagick's property input (e.g.
Definition: Bitmap.php:615
BitmapHandler\canRotate
static canRotate()
Returns whether the current scaler supports rotation (im and gd do)
Definition: Bitmap.php:745
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1087
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
BitmapHandler\rotate
rotate( $file, $params)
Definition: Bitmap.php:786
$params
$params
Definition: styleTest.css.php:40
wfHostname
wfHostname()
Fetch server name for use in error reporting etc.
Definition: GlobalFunctions.php:1833
wfShellExecWithStderr
wfShellExecWithStderr( $cmd, &$retval=null, $environ=array(), $limits=array())
Execute a shell command, returning both stdout and stderr.
Definition: GlobalFunctions.php:3076
$s
$s
Definition: mergeMessageFileList.php:156
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2118
Exif
Class to extract and validate Exif data from jpeg (and possibly tiff) files.
Definition: Exif.php:32
BitmapHandler\getClientScalingThumbnailImage
getClientScalingThumbnailImage( $image, $scalerParams)
Get a ThumbnailImage that respresents an image that will be scaled client side.
Definition: Bitmap.php:288
BitmapHandler\transformCustom
transformCustom( $image, $params)
Transform an image using a custom command.
Definition: Bitmap.php:483
BitmapHandler\extractPreRotationDimensions
extractPreRotationDimensions( $params, $rotation)
Extracts the width/height if the image will be scaled before rotating.
Definition: Bitmap.php:95
MWException
MediaWiki exception.
Definition: MWException.php:26
BitmapHandler\escapeMagickOutput
escapeMagickOutput( $path, $scene=false)
Escape a string for ImageMagick's output filename.
Definition: Bitmap.php:665
BitmapHandler
Generic handler for bitmap images.
Definition: Bitmap.php:29
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
ImageHandler
Media handler abstract base class for images.
Definition: ImageHandler.php:29
wfMessage
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4058
BitmapHandler\mustRender
mustRender( $file)
Rerurns whether the file needs to be rendered.
Definition: Bitmap.php:838
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
will
</td >< td > &</td >< td > t want your writing to be edited mercilessly and redistributed at will
Definition: All_system_messages.txt:914
BitmapHandler\getMediaTransformError
getMediaTransformError( $params, $errMsg)
Get a MediaTransformError with error 'thumbnail_error'.
Definition: Bitmap.php:516
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:2571
TransformParameterError
Shortcut class for parameter validation errors.
Definition: MediaTransformOutput.php:452
wfEscapeShellArg
wfEscapeShellArg()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell,...
Definition: GlobalFunctions.php:2752
BitmapHandler\transformGd
transformGd( $image, $params)
Transform an image using the built in GD library.
Definition: Bitmap.php:529
BitmapHandler\getScalerType
static getScalerType( $dstPath, $checkDstPath=true)
Returns which scaler type should be used.
Definition: Bitmap.php:255
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
BitmapHandler\getMagickVersion
getMagickVersion()
Retrieve the version of the installed ImageMagick You can use PHPs version_compare() to use this valu...
Definition: Bitmap.php:711
BitmapHandler\normaliseParams
normaliseParams( $image, &$params)
Definition: Bitmap.php:37
$cache
$cache
Definition: mcc.php:32
BitmapHandler\transformImageMagick
transformImageMagick( $image, $params)
Transform an image using ImageMagick.
Definition: Bitmap.php:305
MediaHandler\logErrorForExternalProcess
logErrorForExternalProcess( $retval, $err, $cmd)
Log an error that occurred in an external process.
Definition: MediaHandler.php:766
on
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going on
Definition: hooks.txt:86
$path
$path
Definition: NoLocalSettings.php:35
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
MediaHandler\isAnimatedImage
isAnimatedImage( $file)
The material is an image, and is animated.
Definition: MediaHandler.php:373
ImageHandler\getImageArea
getImageArea( $image)
Function that returns the number of pixels to be thumbnailed.
Definition: ImageHandler.php:220
$retval
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account incomplete not yet checked for validity & $retval
Definition: hooks.txt:237
BitmapHandler\escapeMagickInput
escapeMagickInput( $path, $scene=false)
Escape a string for ImageMagick's input filenames.
Definition: Bitmap.php:645
MediaHandler\getRotation
getRotation( $file)
On supporting image formats, try to read out the low-level orientation of the file and return the ang...
Definition: MediaHandler.php:751
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk page
Definition: hooks.txt:1961