Merge "mw.language.convertPlural: Check if matching form exists"
[lhc/web/wiklou.git] / includes / media / Bitmap.php
index ca9b636..4959687 100644 (file)
@@ -29,7 +29,7 @@
 class BitmapHandler extends ImageHandler {
        /**
         * @param $image File
-        * @param $params array Transform parameters. Entries with the keys 'width'
+        * @param array $params Transform parameters. Entries with the keys 'width'
         * and 'height' are the respective screen width and height, while the keys
         * 'physicalWidth' and 'physicalHeight' indicate the thumbnail dimensions.
         * @return bool
@@ -75,7 +75,6 @@ class BitmapHandler extends ImageHandler {
                return true;
        }
 
-
        /**
         * Extracts the width/height if the image will be scaled before rotating
         *
@@ -84,8 +83,8 @@ class BitmapHandler extends ImageHandler {
         * stored as raw landscape with 90-degress rotation, the resulting size
         * will be wider than it is tall.
         *
-        * @param $params array Parameters as returned by normaliseParams
-        * @param $rotation int The rotation angle that will be applied
+        * @param array $params Parameters as returned by normaliseParams
+        * @param int $rotation The rotation angle that will be applied
         * @return array ($width, $height) array
         */
        public function extractPreRotationDimensions( $params, $rotation ) {
@@ -100,18 +99,6 @@ class BitmapHandler extends ImageHandler {
                return array( $width, $height );
        }
 
-
-       /**
-        * Function that returns the number of pixels to be thumbnailed.
-        * Intended for animated GIFs to multiply by the number of frames.
-        *
-        * @param File $image
-        * @return int
-        */
-       function getImageArea( $image ) {
-               return $image->getWidth() * $image->getHeight();
-       }
-
        /**
         * @param $image File
         * @param  $dstPath
@@ -133,7 +120,7 @@ class BitmapHandler extends ImageHandler {
                        # The size of the image on the page
                        'clientWidth' => $params['width'],
                        'clientHeight' => $params['height'],
-                       # Comment as will be added to the EXIF of the thumbnail
+                       # Comment as will be added to the Exif of the thumbnail
                        'comment' => isset( $params['descriptionUrl'] ) ?
                                "File source: {$params['descriptionUrl']}" : '',
                        # Properties of the original image
@@ -158,7 +145,6 @@ class BitmapHandler extends ImageHandler {
                        return $this->getClientScalingThumbnailImage( $image, $scalerParams );
                }
 
-
                if ( $scaler == 'client' ) {
                        # Client-side image scaling, use the source URL
                        # Using the destination URL in a TRANSFORM_LATER request would be incorrect
@@ -182,6 +168,13 @@ class BitmapHandler extends ImageHandler {
 
                # Transform functions and binaries need a FS source file
                $scalerParams['srcPath'] = $image->getLocalRefPath();
+               if ( $scalerParams['srcPath'] === false ) { // Failed to get local copy
+                       wfDebugLog( 'thumbnail',
+                               sprintf( 'Thumbnail failed on %s: could not get local copy of "%s"',
+                                       wfHostname(), $image->getName() ) );
+                       return new MediaTransformError( 'thumbnail_error',
+                               $scalerParams['clientWidth'], $scalerParams['clientHeight'] );
+               }
 
                # Try a hook
                $mto = null;
@@ -264,7 +257,7 @@ class BitmapHandler extends ImageHandler {
         * client side
         *
         * @param $image File File associated with this thumbnail
-        * @param $scalerParams array Array with scaler params
+        * @param array $scalerParams Array with scaler params
         * @return ThumbnailImage
         *
         * @todo fixme: no rotation support
@@ -281,7 +274,7 @@ class BitmapHandler extends ImageHandler {
         * Transform an image using ImageMagick
         *
         * @param $image File File associated with this thumbnail
-        * @param $params array Array with scaler params
+        * @param array $params Array with scaler params
         *
         * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
         */
@@ -341,7 +334,7 @@ class BitmapHandler extends ImageHandler {
                $rotation = $this->getRotation( $image );
                list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
 
-               $cmd  =
+               $cmd =
                        wfEscapeShellArg( $wgImageMagickConvertCommand ) .
                        // Specify white background color, will be used for transparent images
                        // in Internet Explorer/Windows instead of default black.
@@ -360,12 +353,12 @@ class BitmapHandler extends ImageHandler {
                        " -depth 8 $sharpen " .
                        " -rotate -$rotation " .
                        " {$animation_post} " .
-                       wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) ) . " 2>&1";
+                       wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) );
 
                wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
                wfProfileIn( 'convert' );
                $retval = 0;
-               $err = wfShellExec( $cmd, $retval, $env );
+               $err = wfShellExecWithStderr( $cmd, $retval, $env );
                wfProfileOut( 'convert' );
 
                if ( $retval !== 0 ) {
@@ -380,7 +373,7 @@ class BitmapHandler extends ImageHandler {
         * Transform an image using the Imagick PHP extension
         *
         * @param $image File File associated with this thumbnail
-        * @param $params array Array with scaler params
+        * @param array $params Array with scaler params
         *
         * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
         */
@@ -401,7 +394,7 @@ class BitmapHandler extends ImageHandler {
                                        $im->sharpenImage( $radius, $sigma );
                                }
                                $im->setCompressionQuality( 80 );
-                       } elseif( $params['mimeType'] == 'image/png' ) {
+                       } elseif ( $params['mimeType'] == 'image/png' ) {
                                $im->setCompressionQuality( 95 );
                        } elseif ( $params['mimeType'] == 'image/gif' ) {
                                if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
@@ -457,7 +450,7 @@ class BitmapHandler extends ImageHandler {
         * Transform an image using a custom command
         *
         * @param $image File File associated with this thumbnail
-        * @param $params array Array with scaler params
+        * @param array $params Array with scaler params
         *
         * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
         */
@@ -475,7 +468,7 @@ class BitmapHandler extends ImageHandler {
                wfDebug( __METHOD__ . ": Running custom convert command $cmd\n" );
                wfProfileIn( 'convert' );
                $retval = 0;
-               $err = wfShellExec( $cmd, $retval );
+               $err = wfShellExecWithStderr( $cmd, $retval );
                wfProfileOut( 'convert' );
 
                if ( $retval !== 0 ) {
@@ -500,8 +493,8 @@ class BitmapHandler extends ImageHandler {
        /**
         * Get a MediaTransformError with error 'thumbnail_error'
         *
-        * @param $params array Parameter array as passed to the transform* functions
-        * @param $errMsg string Error message
+        * @param array $params Parameter array as passed to the transform* functions
+        * @param string $errMsg Error message
         * @return MediaTransformError
         */
        public function getMediaTransformError( $params, $errMsg ) {
@@ -513,7 +506,7 @@ class BitmapHandler extends ImageHandler {
         * Transform an image using the built in GD library
         *
         * @param $image File File associated with this thumbnail
-        * @param $params array Array with scaler params
+        * @param array $params Array with scaler params
         *
         * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
         */
@@ -622,7 +615,7 @@ class BitmapHandler extends ImageHandler {
         * in a directory, so we're better off escaping and waiting for the bugfix
         * to filter down to users.
         *
-        * @param $path string The file path
+        * @param string $path The file path
         * @param bool|string $scene The scene specification, or false if there is none
         * @throws MWException
         * @return string
@@ -654,7 +647,7 @@ class BitmapHandler extends ImageHandler {
         * Armour a string against ImageMagick's GetPathComponent(). This is a
         * helper function for escapeMagickInput() and escapeMagickOutput().
         *
-        * @param $path string The file path
+        * @param string $path The file path
         * @param bool|string $scene The scene specification, or false if there is none
         * @throws MWException
         * @return string
@@ -715,25 +708,6 @@ class BitmapHandler extends ImageHandler {
                imagejpeg( $dst_image, $thumbPath, 95 );
        }
 
-       /**
-        * On supporting image formats, try to read out the low-level orientation
-        * of the file and return the angle that the file needs to be rotated to
-        * be viewed.
-        *
-        * This information is only useful when manipulating the original file;
-        * the width and height we normally work with is logical, and will match
-        * any produced output views.
-        *
-        * The base BitmapHandler doesn't understand any metadata formats, so this
-        * is left up to child classes to implement.
-        *
-        * @param $file File
-        * @return int 0, 90, 180 or 270
-        */
-       public function getRotation( $file ) {
-               return 0;
-       }
-
        /**
         * Returns whether the current scaler supports rotation (im and gd do)
         *
@@ -758,6 +732,55 @@ class BitmapHandler extends ImageHandler {
                }
        }
 
+       /**
+        * @param $file File
+        * @param array $params Rotate parameters.
+        *      'rotation' clockwise rotation in degrees, allowed are multiples of 90
+        * @since 1.21
+        * @return bool
+        */
+       public function rotate( $file, $params ) {
+               global $wgImageMagickConvertCommand;
+
+               $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;
+               $scene = false;
+
+               $scaler = self::getScalerType( null, false );
+               switch ( $scaler ) {
+                       case 'im':
+                               $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . " " .
+                                       wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
+                                       " -rotate -$rotation " .
+                                       wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) );
+                               wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
+                               wfProfileIn( 'convert' );
+                               $retval = 0;
+                               $err = wfShellExecWithStderr( $cmd, $retval, $env );
+                               wfProfileOut( 'convert' );
+                               if ( $retval !== 0 ) {
+                                       $this->logErrorForExternalProcess( $retval, $err, $cmd );
+                                       return new MediaTransformError( 'thumbnail_error', 0, 0, $err );
+                               }
+                               return false;
+                       case 'imext':
+                               $im = new Imagick();
+                               $im->readImage( $params['srcPath'] );
+                               if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
+                                       return new MediaTransformError( 'thumbnail_error', 0, 0,
+                                               "Error rotating $rotation degrees" );
+                               }
+                               $result = $im->writeImage( $params['dstPath'] );
+                               if ( !$result ) {
+                                       return new MediaTransformError( 'thumbnail_error', 0, 0,
+                                               "Unable to write image to {$params['dstPath']}" );
+                               }
+                               return false;
+                       default:
+                               return new MediaTransformError( 'thumbnail_error', 0, 0,
+                                       "$scaler rotation not implemented" );
+               }
+       }
+
        /**
         * Rerurns whether the file needs to be rendered. Returns true if the
         * file requires rotation and we are able to rotate it.