MediaWiki  1.23.2
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  array( '-depth', 8 ),
377  $sharpen,
378  array( '-rotate', "-$rotation" ),
379  $animation_post,
380  array( $this->escapeMagickOutput( $params['dstPath'] ) ) ) );
381 
382  wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
383  wfProfileIn( 'convert' );
384  $retval = 0;
385  $err = wfShellExecWithStderr( $cmd, $retval, $env );
386  wfProfileOut( 'convert' );
387 
388  if ( $retval !== 0 ) {
389  $this->logErrorForExternalProcess( $retval, $err, $cmd );
390 
391  return $this->getMediaTransformError( $params, "$err\nError code: $retval" );
392  }
393 
394  return false; # No error
395  }
396 
405  protected function transformImageMagickExt( $image, $params ) {
406  global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea;
407 
408  try {
409  $im = new Imagick();
410  $im->readImage( $params['srcPath'] );
411 
412  if ( $params['mimeType'] == 'image/jpeg' ) {
413  // Sharpening, see bug 6193
414  if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
415  / ( $params['srcWidth'] + $params['srcHeight'] )
416  < $wgSharpenReductionThreshold
417  ) {
418  // Hack, since $wgSharpenParamater is written specifically for the command line convert
419  list( $radius, $sigma ) = explode( 'x', $wgSharpenParameter );
420  $im->sharpenImage( $radius, $sigma );
421  }
422  $im->setCompressionQuality( 80 );
423  } elseif ( $params['mimeType'] == 'image/png' ) {
424  $im->setCompressionQuality( 95 );
425  } elseif ( $params['mimeType'] == 'image/gif' ) {
426  if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
427  // Extract initial frame only; we're so big it'll
428  // be a total drag. :P
429  $im->setImageScene( 0 );
430  } elseif ( $this->isAnimatedImage( $image ) ) {
431  // Coalesce is needed to scale animated GIFs properly (bug 1017).
432  $im = $im->coalesceImages();
433  }
434  }
435 
436  $rotation = $this->getRotation( $image );
437  list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
438 
439  $im->setImageBackgroundColor( new ImagickPixel( 'white' ) );
440 
441  // Call Imagick::thumbnailImage on each frame
442  foreach ( $im as $i => $frame ) {
443  if ( !$frame->thumbnailImage( $width, $height, /* fit */ false ) ) {
444  return $this->getMediaTransformError( $params, "Error scaling frame $i" );
445  }
446  }
447  $im->setImageDepth( 8 );
448 
449  if ( $rotation ) {
450  if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
451  return $this->getMediaTransformError( $params, "Error rotating $rotation degrees" );
452  }
453  }
454 
455  if ( $this->isAnimatedImage( $image ) ) {
456  wfDebug( __METHOD__ . ": Writing animated thumbnail\n" );
457  // This is broken somehow... can't find out how to fix it
458  $result = $im->writeImages( $params['dstPath'], true );
459  } else {
460  $result = $im->writeImage( $params['dstPath'] );
461  }
462  if ( !$result ) {
463  return $this->getMediaTransformError( $params,
464  "Unable to write thumbnail to {$params['dstPath']}" );
465  }
466  } catch ( ImagickException $e ) {
467  return $this->getMediaTransformError( $params, $e->getMessage() );
468  }
469 
470  return false;
471  }
472 
481  protected function transformCustom( $image, $params ) {
482  # Use a custom convert command
483  global $wgCustomConvertCommand;
484 
485  # Variables: %s %d %w %h
486  $src = wfEscapeShellArg( $params['srcPath'] );
487  $dst = wfEscapeShellArg( $params['dstPath'] );
488  $cmd = $wgCustomConvertCommand;
489  $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
490  $cmd = str_replace( '%h', wfEscapeShellArg( $params['physicalHeight'] ),
491  str_replace( '%w', wfEscapeShellArg( $params['physicalWidth'] ), $cmd ) ); # Size
492  wfDebug( __METHOD__ . ": Running custom convert command $cmd\n" );
493  wfProfileIn( 'convert' );
494  $retval = 0;
495  $err = wfShellExecWithStderr( $cmd, $retval );
496  wfProfileOut( 'convert' );
497 
498  if ( $retval !== 0 ) {
499  $this->logErrorForExternalProcess( $retval, $err, $cmd );
500 
501  return $this->getMediaTransformError( $params, $err );
502  }
503 
504  return false; # No error
505  }
506 
514  public function getMediaTransformError( $params, $errMsg ) {
515  return new MediaTransformError( 'thumbnail_error', $params['clientWidth'],
516  $params['clientHeight'], $errMsg );
517  }
518 
527  protected function transformGd( $image, $params ) {
528  # Use PHP's builtin GD library functions.
529  #
530  # First find out what kind of file this is, and select the correct
531  # input routine for this.
532 
533  $typemap = array(
534  'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
535  'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor',
536  array( __CLASS__, 'imageJpegWrapper' ) ),
537  'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
538  'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
539  'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
540  );
541  if ( !isset( $typemap[$params['mimeType']] ) ) {
542  $err = 'Image type not supported';
543  wfDebug( "$err\n" );
544  $errMsg = wfMessage( 'thumbnail_image-type' )->text();
545 
546  return $this->getMediaTransformError( $params, $errMsg );
547  }
548  list( $loader, $colorStyle, $saveType ) = $typemap[$params['mimeType']];
549 
550  if ( !function_exists( $loader ) ) {
551  $err = "Incomplete GD library configuration: missing function $loader";
552  wfDebug( "$err\n" );
553  $errMsg = wfMessage( 'thumbnail_gd-library', $loader )->text();
554 
555  return $this->getMediaTransformError( $params, $errMsg );
556  }
557 
558  if ( !file_exists( $params['srcPath'] ) ) {
559  $err = "File seems to be missing: {$params['srcPath']}";
560  wfDebug( "$err\n" );
561  $errMsg = wfMessage( 'thumbnail_image-missing', $params['srcPath'] )->text();
562 
563  return $this->getMediaTransformError( $params, $errMsg );
564  }
565 
566  $src_image = call_user_func( $loader, $params['srcPath'] );
567 
568  $rotation = function_exists( 'imagerotate' ) ? $this->getRotation( $image ) : 0;
569  list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
570  $dst_image = imagecreatetruecolor( $width, $height );
571 
572  // Initialise the destination image to transparent instead of
573  // the default solid black, to support PNG and GIF transparency nicely
574  $background = imagecolorallocate( $dst_image, 0, 0, 0 );
575  imagecolortransparent( $dst_image, $background );
576  imagealphablending( $dst_image, false );
577 
578  if ( $colorStyle == 'palette' ) {
579  // Don't resample for paletted GIF images.
580  // It may just uglify them, and completely breaks transparency.
581  imagecopyresized( $dst_image, $src_image,
582  0, 0, 0, 0,
583  $width, $height,
584  imagesx( $src_image ), imagesy( $src_image ) );
585  } else {
586  imagecopyresampled( $dst_image, $src_image,
587  0, 0, 0, 0,
588  $width, $height,
589  imagesx( $src_image ), imagesy( $src_image ) );
590  }
591 
592  if ( $rotation % 360 != 0 && $rotation % 90 == 0 ) {
593  $rot_image = imagerotate( $dst_image, $rotation, 0 );
594  imagedestroy( $dst_image );
595  $dst_image = $rot_image;
596  }
597 
598  imagesavealpha( $dst_image, true );
599 
600  call_user_func( $saveType, $dst_image, $params['dstPath'] );
601  imagedestroy( $dst_image );
602  imagedestroy( $src_image );
603 
604  return false; # No error
605  }
606 
613  function escapeMagickProperty( $s ) {
614  // Double the backslashes
615  $s = str_replace( '\\', '\\\\', $s );
616  // Double the percents
617  $s = str_replace( '%', '%%', $s );
618  // Escape initial - or @
619  if ( strlen( $s ) > 0 && ( $s[0] === '-' || $s[0] === '@' ) ) {
620  $s = '\\' . $s;
621  }
622 
623  return $s;
624  }
625 
643  function escapeMagickInput( $path, $scene = false ) {
644  # Die on initial metacharacters (caller should prepend path)
645  $firstChar = substr( $path, 0, 1 );
646  if ( $firstChar === '~' || $firstChar === '@' ) {
647  throw new MWException( __METHOD__ . ': cannot escape this path name' );
648  }
649 
650  # Escape glob chars
651  $path = preg_replace( '/[*?\[\]{}]/', '\\\\\0', $path );
652 
653  return $this->escapeMagickPath( $path, $scene );
654  }
655 
663  function escapeMagickOutput( $path, $scene = false ) {
664  $path = str_replace( '%', '%%', $path );
665 
666  return $this->escapeMagickPath( $path, $scene );
667  }
668 
678  protected function escapeMagickPath( $path, $scene = false ) {
679  # Die on format specifiers (other than drive letters). The regex is
680  # meant to match all the formats you get from "convert -list format"
681  if ( preg_match( '/^([a-zA-Z0-9-]+):/', $path, $m ) ) {
682  if ( wfIsWindows() && is_dir( $m[0] ) ) {
683  // OK, it's a drive letter
684  // ImageMagick has a similar exception, see IsMagickConflict()
685  } else {
686  throw new MWException( __METHOD__ . ': unexpected colon character in path name' );
687  }
688  }
689 
690  # If there are square brackets, add a do-nothing scene specification
691  # to force a literal interpretation
692  if ( $scene === false ) {
693  if ( strpos( $path, '[' ) !== false ) {
694  $path .= '[0--1]';
695  }
696  } else {
697  $path .= "[$scene]";
698  }
699 
700  return $path;
701  }
702 
709  protected function getMagickVersion() {
710  global $wgMemc;
711 
712  $cache = $wgMemc->get( "imagemagick-version" );
713  if ( !$cache ) {
714  global $wgImageMagickConvertCommand;
715  $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . ' -version';
716  wfDebug( __METHOD__ . ": Running convert -version\n" );
717  $retval = '';
718  $return = wfShellExec( $cmd, $retval );
719  $x = preg_match( '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', $return, $matches );
720  if ( $x != 1 ) {
721  wfDebug( __METHOD__ . ": ImageMagick version check failed\n" );
722 
723  return null;
724  }
725  $wgMemc->set( "imagemagick-version", $matches[1], 3600 );
726 
727  return $matches[1];
728  }
729 
730  return $cache;
731  }
732 
733  static function imageJpegWrapper( $dst_image, $thumbPath ) {
734  imageinterlace( $dst_image );
735  imagejpeg( $dst_image, $thumbPath, 95 );
736  }
737 
743  public static function canRotate() {
744  $scaler = self::getScalerType( null, false );
745  switch ( $scaler ) {
746  case 'im':
747  # ImageMagick supports autorotation
748  return true;
749  case 'imext':
750  # Imagick::rotateImage
751  return true;
752  case 'gd':
753  # GD's imagerotate function is used to rotate images, but not
754  # all precompiled PHP versions have that function
755  return function_exists( 'imagerotate' );
756  default:
757  # Other scalers don't support rotation
758  return false;
759  }
760  }
761 
766  public static function autoRotateEnabled() {
767  global $wgEnableAutoRotation;
768 
769  if ( $wgEnableAutoRotation === null ) {
770  // Only enable auto-rotation when the bitmap handler can rotate
771  $wgEnableAutoRotation = BitmapHandler::canRotate();
772  }
773 
774  return $wgEnableAutoRotation;
775  }
776 
784  public function rotate( $file, $params ) {
785  global $wgImageMagickConvertCommand;
786 
787  $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;
788  $scene = false;
789 
790  $scaler = self::getScalerType( null, false );
791  switch ( $scaler ) {
792  case 'im':
793  $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . " " .
794  wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
795  " -rotate " . wfEscapeShellArg( "-$rotation" ) . " " .
796  wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) );
797  wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
798  wfProfileIn( 'convert' );
799  $retval = 0;
800  $err = wfShellExecWithStderr( $cmd, $retval );
801  wfProfileOut( 'convert' );
802  if ( $retval !== 0 ) {
803  $this->logErrorForExternalProcess( $retval, $err, $cmd );
804 
805  return new MediaTransformError( 'thumbnail_error', 0, 0, $err );
806  }
807 
808  return false;
809  case 'imext':
810  $im = new Imagick();
811  $im->readImage( $params['srcPath'] );
812  if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
813  return new MediaTransformError( 'thumbnail_error', 0, 0,
814  "Error rotating $rotation degrees" );
815  }
816  $result = $im->writeImage( $params['dstPath'] );
817  if ( !$result ) {
818  return new MediaTransformError( 'thumbnail_error', 0, 0,
819  "Unable to write image to {$params['dstPath']}" );
820  }
821 
822  return false;
823  default:
824  return new MediaTransformError( 'thumbnail_error', 0, 0,
825  "$scaler rotation not implemented" );
826  }
827  }
828 
836  public function mustRender( $file ) {
837  return self::canRotate() && $this->getRotation( $file ) != 0;
838  }
839 }
MediaHandler\removeBadFile
removeBadFile( $dstPath, $retval=0)
Check for zero-sized thumbnails.
Definition: MediaHandler.php:688
BitmapHandler\autoRotateEnabled
static autoRotateEnabled()
Definition: Bitmap.php:766
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:2804
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:405
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:678
wfMkdirParents
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
Definition: GlobalFunctions.php:2590
BitmapHandler\imageJpegWrapper
static imageJpegWrapper( $dst_image, $thumbPath)
Definition: Bitmap.php:733
$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:613
BitmapHandler\canRotate
static canRotate()
Returns whether the current scaler supports rotation (im and gd do)
Definition: Bitmap.php:743
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:1040
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
BitmapHandler\rotate
rotate( $file, $params)
Definition: Bitmap.php:784
$params
$params
Definition: styleTest.css.php:40
wfHostname
wfHostname()
Fetch server name for use in error reporting etc.
Definition: GlobalFunctions.php:1786
wfShellExecWithStderr
wfShellExecWithStderr( $cmd, &$retval=null, $environ=array(), $limits=array())
Execute a shell command, returning both stdout and stderr.
Definition: GlobalFunctions.php:3020
$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:2113
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:481
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:663
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:4001
BitmapHandler\mustRender
mustRender( $file)
Rerurns whether the file needs to be rendered.
Definition: Bitmap.php:836
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:514
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
$matches
if(!defined( 'MEDIAWIKI')) if(!isset( $wgVersion)) $matches
Definition: NoLocalSettings.php:33
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:2524
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:2705
BitmapHandler\transformGd
transformGd( $image, $params)
Transform an image using the built in GD library.
Definition: Bitmap.php:527
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:709
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
$e
if( $useReadline) $e
Definition: eval.php:66
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:643
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
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:1956