From 4ac9fa081a7c045f6a9f1cfc529d82423f485b2e Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sun, 8 Dec 2013 09:55:49 +0100 Subject: Update to MediaWiki 1.22.0 --- includes/media/BMP.php | 6 +- includes/media/Bitmap.php | 47 ++-------- includes/media/BitmapMetadataHandler.php | 24 ++--- includes/media/DjVu.php | 21 +++-- includes/media/DjVuImage.php | 44 ++++----- includes/media/Exif.php | 76 ++++++++-------- includes/media/ExifBitmap.php | 8 +- includes/media/FormatMetadata.php | 101 ++++++++++----------- includes/media/GIF.php | 8 +- includes/media/GIFMetadataExtractor.php | 46 ++++++---- includes/media/IPTC.php | 8 +- includes/media/ImageHandler.php | 23 +++-- includes/media/Jpeg.php | 20 ++--- includes/media/JpegMetadataExtractor.php | 22 +++-- includes/media/MediaHandler.php | 149 +++++++++++++++++++++++++------ includes/media/MediaTransformOutput.php | 53 ++++++++--- includes/media/PNG.php | 13 +-- includes/media/PNGMetadataExtractor.php | 30 +++---- includes/media/SVG.php | 92 +++++++++++++++++-- includes/media/SVGMetadataExtractor.php | 64 ++++++------- includes/media/Tiff.php | 2 +- includes/media/XCF.php | 6 +- includes/media/XMP.php | 26 +++--- includes/media/XMPInfo.php | 6 +- includes/media/XMPValidate.php | 12 +-- 25 files changed, 556 insertions(+), 351 deletions(-) (limited to 'includes/media') diff --git a/includes/media/BMP.php b/includes/media/BMP.php index 46d1b95b..99b7741a 100644 --- a/includes/media/BMP.php +++ b/includes/media/BMP.php @@ -58,15 +58,15 @@ class BmpHandler extends BitmapHandler { */ function getImageSize( $image, $filename ) { $f = fopen( $filename, 'rb' ); - if( !$f ) { + if ( !$f ) { return false; } $header = fread( $f, 54 ); fclose( $f ); // Extract binary form of width and height from the header - $w = substr( $header, 18, 4); - $h = substr( $header, 22, 4); + $w = substr( $header, 18, 4 ); + $h = substr( $header, 22, 4 ); // Convert the unsigned long 32 bits (little endian): try { diff --git a/includes/media/Bitmap.php b/includes/media/Bitmap.php index e2dc68b2..e2444a11 100644 --- a/includes/media/Bitmap.php +++ b/includes/media/Bitmap.php @@ -99,17 +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 @@ -131,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 @@ -357,12 +346,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 ) { @@ -398,7 +387,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 ) { @@ -472,7 +461,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 ) { @@ -712,24 +701,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) @@ -765,20 +736,20 @@ class BitmapHandler extends ImageHandler { public function rotate( $file, $params ) { global $wgImageMagickConvertCommand; - $rotation = ( $params[ 'rotation' ] + $this->getRotation( $file ) ) % 360; + $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 ) ) . + wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) . " -rotate -$rotation " . - 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 ) { $this->logErrorForExternalProcess( $retval, $err, $cmd ); diff --git a/includes/media/BitmapMetadataHandler.php b/includes/media/BitmapMetadataHandler.php index 345e7869..7c39c814 100644 --- a/includes/media/BitmapMetadataHandler.php +++ b/includes/media/BitmapMetadataHandler.php @@ -54,7 +54,7 @@ class BitmapMetadataHandler { * * @param string $app13 String containing app13 block from jpeg file */ - private function doApp13 ( $app13 ) { + private function doApp13( $app13 ) { try { $this->iptcType = JpegMetadataExtractor::doPSIR( $app13 ); } catch ( MWException $e ) { @@ -79,7 +79,7 @@ class BitmapMetadataHandler { * @param $filename string * @param $byteOrder string */ - function getExif ( $filename, $byteOrder ) { + function getExif( $filename, $byteOrder ) { global $wgShowEXIF; if ( file_exists( $filename ) && $wgShowEXIF ) { $exif = new Exif( $filename, $byteOrder ); @@ -95,7 +95,7 @@ class BitmapMetadataHandler { * @param array $metaArray array of metadata values * @param string $type type. defaults to other. if two things have the same type they're merged */ - function addMetadata ( $metaArray, $type = 'other' ) { + function addMetadata( $metaArray, $type = 'other' ) { if ( isset( $this->metadata[$type] ) ) { /* merge with old data */ $metaArray = $metaArray + $this->metadata[$type]; @@ -113,7 +113,7 @@ class BitmapMetadataHandler { * * @return Array metadata array */ - function getMetadataArray () { + function getMetadataArray() { // this seems a bit ugly... This is all so its merged in right order // based on the MWG recomendation. $temp = Array(); @@ -147,7 +147,7 @@ class BitmapMetadataHandler { * @return array metadata result array. * @throws MWException on invalid file. */ - static function Jpeg ( $filename ) { + static function Jpeg( $filename ) { $showXMP = function_exists( 'xml_parser_create_ns' ); $meta = new self(); @@ -156,7 +156,7 @@ class BitmapMetadataHandler { $meta->addMetadata( Array( 'JPEGFileComment' => $seg['COM'] ), 'native' ); } if ( isset( $seg['PSIR'] ) && count( $seg['PSIR'] ) > 0 ) { - foreach( $seg['PSIR'] as $curPSIRValue ) { + foreach ( $seg['PSIR'] as $curPSIRValue ) { $meta->doApp13( $curPSIRValue ); } } @@ -189,7 +189,7 @@ class BitmapMetadataHandler { * @param string $filename full path to file * @return Array Array for storage in img_metadata. */ - public static function PNG ( $filename ) { + public static function PNG( $filename ) { $showXMP = function_exists( 'xml_parser_create_ns' ); $meta = new self(); @@ -218,7 +218,7 @@ class BitmapMetadataHandler { * @param string $filename full path to file * @return Array metadata array */ - public static function GIF ( $filename ) { + public static function GIF( $filename ) { $meta = new self(); $baseArray = GIFMetadataExtractor::getMetadata( $filename ); @@ -259,7 +259,7 @@ class BitmapMetadataHandler { * @throws MWException * @return Array The metadata. */ - public static function Tiff ( $filename ) { + public static function Tiff( $filename ) { if ( file_exists( $filename ) ) { $byteOrder = self::getTiffByteOrder( $filename ); if ( !$byteOrder ) { @@ -286,11 +286,13 @@ class BitmapMetadataHandler { */ static function getTiffByteOrder( $filename ) { $fh = fopen( $filename, 'rb' ); - if ( !$fh ) return false; + if ( !$fh ) { + return false; + } $head = fread( $fh, 2 ); fclose( $fh ); - switch( $head ) { + switch ( $head ) { case 'II': return 'LE'; // II for intel. case 'MM': diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php index 0a39a2cf..b9e89d9d 100644 --- a/includes/media/DjVu.php +++ b/includes/media/DjVu.php @@ -185,7 +185,7 @@ class DjVuHandler extends ImageHandler { } $cmd .= ' > ' . wfEscapeShellArg( $dstPath ) . ') 2>&1'; wfProfileIn( 'ddjvu' ); - wfDebug( __METHOD__.": $cmd\n" ); + wfDebug( __METHOD__ . ": $cmd\n" ); $retval = ''; $err = wfShellExec( $cmd, $retval ); wfProfileOut( 'ddjvu' ); @@ -246,24 +246,23 @@ class DjVuHandler extends ImageHandler { $image->dejaMetaTree = false; $image->djvuTextTree = false; $tree = new SimpleXMLElement( $metadata ); - if( $tree->getName() == 'mw-djvu' ) { - foreach( $tree->children() as $b ) { - if( $b->getName() == 'DjVuTxt' ) { + if ( $tree->getName() == 'mw-djvu' ) { + foreach ( $tree->children() as $b ) { + if ( $b->getName() == 'DjVuTxt' ) { $image->djvuTextTree = $b; - } - elseif ( $b->getName() == 'DjVuXML' ) { + } elseif ( $b->getName() == 'DjVuXML' ) { $image->dejaMetaTree = $b; } } } else { $image->dejaMetaTree = $tree; } - } catch( Exception $e ) { + } catch ( Exception $e ) { wfDebug( "Bogus multipage XML metadata on '{$image->getName()}'\n" ); } wfRestoreWarnings(); wfProfileOut( __METHOD__ ); - if( $gettext ) { + if ( $gettext ) { return $image->djvuTextTree; } else { return $image->dejaMetaTree; @@ -294,7 +293,7 @@ class DjVuHandler extends ImageHandler { } function isMetadataValid( $image, $metadata ) { - return !empty( $metadata ) && $metadata != serialize(array()); + return !empty( $metadata ) && $metadata != serialize( array() ); } function pageCount( $image ) { @@ -311,7 +310,7 @@ class DjVuHandler extends ImageHandler { return false; } - $o = $tree->BODY[0]->OBJECT[$page-1]; + $o = $tree->BODY[0]->OBJECT[$page - 1]; if ( $o ) { return array( 'width' => intval( $o['width'] ), @@ -328,7 +327,7 @@ class DjVuHandler extends ImageHandler { return false; } - $o = $tree->BODY[0]->PAGE[$page-1]; + $o = $tree->BODY[0]->PAGE[$page - 1]; if ( $o ) { $txt = $o['value']; return $txt; diff --git a/includes/media/DjVuImage.php b/includes/media/DjVuImage.php index 46989668..54efe7a8 100644 --- a/includes/media/DjVuImage.php +++ b/includes/media/DjVuImage.php @@ -64,7 +64,7 @@ class DjVuImage { public function getImageSize() { $data = $this->getInfo(); - if( $data !== false ) { + if ( $data !== false ) { $width = $data['width']; $height = $data['height']; @@ -93,20 +93,20 @@ class DjVuImage { $start = ftell( $file ); $secondary = fread( $file, 4 ); echo str_repeat( ' ', $indent * 4 ) . "($secondary)\n"; - while( ftell( $file ) - $start < $length ) { + while ( ftell( $file ) - $start < $length ) { $chunkHeader = fread( $file, 8 ); - if( $chunkHeader == '' ) { + if ( $chunkHeader == '' ) { break; } // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables. extract( unpack( 'a4chunk/NchunkLength', $chunkHeader ) ); echo str_repeat( ' ', $indent * 4 ) . "$chunk $chunkLength\n"; - if( $chunk == 'FORM' ) { + if ( $chunk == 'FORM' ) { $this->dumpForm( $file, $chunkLength, $indent + 1 ); } else { fseek( $file, $chunkLength, SEEK_CUR ); - if( $chunkLength & 1 == 1 ) { + if ( $chunkLength & 1 == 1 ) { // Padding byte between chunks fseek( $file, 1, SEEK_CUR ); } @@ -118,7 +118,7 @@ class DjVuImage { wfSuppressWarnings(); $file = fopen( $this->mFilename, 'rb' ); wfRestoreWarnings(); - if( $file === false ) { + if ( $file === false ) { wfDebug( __METHOD__ . ": missing or failed file read\n" ); return false; } @@ -126,21 +126,21 @@ class DjVuImage { $header = fread( $file, 16 ); $info = false; - if( strlen( $header ) < 16 ) { + if ( strlen( $header ) < 16 ) { wfDebug( __METHOD__ . ": too short file header\n" ); } else { // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables. extract( unpack( 'a4magic/a4form/NformLength/a4subtype', $header ) ); - if( $magic != 'AT&T' ) { + if ( $magic != 'AT&T' ) { wfDebug( __METHOD__ . ": not a DjVu file\n" ); - } elseif( $subtype == 'DJVU' ) { + } elseif ( $subtype == 'DJVU' ) { // Single-page document $info = $this->getPageInfo( $file, $formLength ); - } elseif( $subtype == 'DJVM' ) { + } elseif ( $subtype == 'DJVM' ) { // Multi-page document $info = $this->getMultiPageInfo( $file, $formLength ); - } else { + } else { wfDebug( __METHOD__ . ": unrecognized DJVU file type '$formType'\n" ); } } @@ -150,7 +150,7 @@ class DjVuImage { private function readChunk( $file ) { $header = fread( $file, 8 ); - if( strlen( $header ) < 8 ) { + if ( strlen( $header ) < 8 ) { return array( false, 0 ); } else { // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables. @@ -162,7 +162,7 @@ class DjVuImage { private function skipChunk( $file, $chunkLength ) { fseek( $file, $chunkLength, SEEK_CUR ); - if( $chunkLength & 0x01 == 1 && !feof( $file ) ) { + if ( $chunkLength & 0x01 == 1 && !feof( $file ) ) { // padding byte fseek( $file, 1, SEEK_CUR ); } @@ -174,13 +174,13 @@ class DjVuImage { $start = ftell( $file ); do { list( $chunk, $length ) = $this->readChunk( $file ); - if( !$chunk ) { + if ( !$chunk ) { break; } - if( $chunk == 'FORM' ) { + if ( $chunk == 'FORM' ) { $subtype = fread( $file, 4 ); - if( $subtype == 'DJVU' ) { + if ( $subtype == 'DJVU' ) { wfDebug( __METHOD__ . ": found first subpage\n" ); return $this->getPageInfo( $file, $length ); } @@ -189,7 +189,7 @@ class DjVuImage { wfDebug( __METHOD__ . ": skipping '$chunk' chunk\n" ); $this->skipChunk( $file, $length ); } - } while( $length != 0 && !feof( $file ) && ftell( $file ) - $start < $formLength ); + } while ( $length != 0 && !feof( $file ) && ftell( $file ) - $start < $formLength ); wfDebug( __METHOD__ . ": multi-page DJVU file contained no pages\n" ); return false; @@ -197,17 +197,17 @@ class DjVuImage { private function getPageInfo( $file, $formLength ) { list( $chunk, $length ) = $this->readChunk( $file ); - if( $chunk != 'INFO' ) { + if ( $chunk != 'INFO' ) { wfDebug( __METHOD__ . ": expected INFO chunk, got '$chunk'\n" ); return false; } - if( $length < 9 ) { + if ( $length < 9 ) { wfDebug( __METHOD__ . ": INFO should be 9 or 10 bytes, found $length\n" ); return false; } $data = fread( $file, $length ); - if( strlen( $data ) < $length ) { + if ( strlen( $data ) < $length ) { wfDebug( __METHOD__ . ": INFO chunk cut off\n" ); return false; } @@ -263,7 +263,7 @@ class DjVuImage { $retval = ''; $txt = wfShellExec( $cmd, $retval, array(), array( 'memory' => self::DJVUTXT_MEMORY_LIMIT ) ); wfProfileOut( 'djvutxt' ); - if( $retval == 0) { + if ( $retval == 0 ) { # Strip some control characters $txt = preg_replace( "/[\013\035\037]/", "", $txt ); $reg = <<\n\n"; $xml = preg_replace( "//", "", $xml, 1 ); - $xml = $xml . $txt. ''; + $xml = $xml . $txt . ''; } } wfProfileOut( __METHOD__ ); diff --git a/includes/media/Exif.php b/includes/media/Exif.php index 17671808..9a2794a5 100644 --- a/includes/media/Exif.php +++ b/includes/media/Exif.php @@ -36,6 +36,7 @@ class Exif { const SHORT = 3; //!< A 16-bit (2-byte) unsigned integer. const LONG = 4; //!< A 32-bit (4-byte) unsigned integer. const RATIONAL = 5; //!< Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator + const SHORT_OR_LONG = 6; //!< A 16-bit (2-byte) or 32-bit (4-byte) unsigned integer. const UNDEFINED = 7; //!< An 8-bit byte that can take any value depending on the field definition const SLONG = 9; //!< A 32-bit (4-byte) signed integer (2's complement notation), const SRATIONAL = 10; //!< Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator. @@ -113,7 +114,7 @@ class Exif { */ function __construct( $file, $byteOrder = '' ) { /** - * Page numbers here refer to pages in the EXIF 2.2 standard + * Page numbers here refer to pages in the Exif 2.2 standard * * Note, Exif::UNDEFINED is treated as a string, not as an array of bytes * so don't put a count parameter for any UNDEFINED values. @@ -124,8 +125,8 @@ class Exif { # TIFF Rev. 6.0 Attribute Information (p22) 'IFD0' => array( # Tags relating to image structure - 'ImageWidth' => Exif::SHORT.','.Exif::LONG, # Image width - 'ImageLength' => Exif::SHORT.','.Exif::LONG, # Image height + 'ImageWidth' => Exif::SHORT_OR_LONG, # Image width + 'ImageLength' => Exif::SHORT_OR_LONG, # Image height 'BitsPerSample' => array( Exif::SHORT, 3 ), # Number of bits per component # "When a primary image is JPEG compressed, this designation is not" # "necessary and is omitted." (p23) @@ -134,25 +135,25 @@ class Exif { 'Orientation' => Exif::SHORT, # Orientation of image #p24 'SamplesPerPixel' => Exif::SHORT, # Number of components 'PlanarConfiguration' => Exif::SHORT, # Image data arrangement #p24 - 'YCbCrSubSampling' => array( Exif::SHORT, 2), # Subsampling ratio of Y to C #p24 + 'YCbCrSubSampling' => array( Exif::SHORT, 2 ), # Subsampling ratio of Y to C #p24 'YCbCrPositioning' => Exif::SHORT, # Y and C positioning #p24-25 'XResolution' => Exif::RATIONAL, # Image resolution in width direction 'YResolution' => Exif::RATIONAL, # Image resolution in height direction 'ResolutionUnit' => Exif::SHORT, # Unit of X and Y resolution #(p26) # Tags relating to recording offset - 'StripOffsets' => Exif::SHORT.','.Exif::LONG, # Image data location - 'RowsPerStrip' => Exif::SHORT.','.Exif::LONG, # Number of rows per strip - 'StripByteCounts' => Exif::SHORT.','.Exif::LONG, # Bytes per compressed strip - 'JPEGInterchangeFormat' => Exif::SHORT.','.Exif::LONG, # Offset to JPEG SOI - 'JPEGInterchangeFormatLength' => Exif::SHORT.','.Exif::LONG, # Bytes of JPEG data + 'StripOffsets' => Exif::SHORT_OR_LONG, # Image data location + 'RowsPerStrip' => Exif::SHORT_OR_LONG, # Number of rows per strip + 'StripByteCounts' => Exif::SHORT_OR_LONG, # Bytes per compressed strip + 'JPEGInterchangeFormat' => Exif::SHORT_OR_LONG, # Offset to JPEG SOI + 'JPEGInterchangeFormatLength' => Exif::SHORT_OR_LONG, # Bytes of JPEG data # Tags relating to image data characteristics 'TransferFunction' => Exif::IGNORE, # Transfer function - 'WhitePoint' => array( Exif::RATIONAL, 2), # White point chromaticity - 'PrimaryChromaticities' => array( Exif::RATIONAL, 6), # Chromaticities of primarities - 'YCbCrCoefficients' => array( Exif::RATIONAL, 3), # Color space transformation matrix coefficients #p27 - 'ReferenceBlackWhite' => array( Exif::RATIONAL, 6), # Pair of black and white reference values + 'WhitePoint' => array( Exif::RATIONAL, 2 ), # White point chromaticity + 'PrimaryChromaticities' => array( Exif::RATIONAL, 6 ), # Chromaticities of primarities + 'YCbCrCoefficients' => array( Exif::RATIONAL, 3 ), # Color space transformation matrix coefficients #p27 + 'ReferenceBlackWhite' => array( Exif::RATIONAL, 6 ), # Pair of black and white reference values # Other tags 'DateTime' => Exif::ASCII, # File change date and time @@ -167,8 +168,8 @@ class Exif { # Exif IFD Attribute Information (p30-31) 'EXIF' => array( # TODO: NOTE: Nonexistence of this field is taken to mean nonconformance - # to the EXIF 2.1 AND 2.2 standards - 'ExifVersion' => Exif::UNDEFINED, # Exif version + # to the Exif 2.1 AND 2.2 standards + 'ExifVersion' => Exif::UNDEFINED, # Exif version 'FlashPixVersion' => Exif::UNDEFINED, # Supported Flashpix version #p32 # Tags relating to Image Data Characteristics @@ -177,8 +178,8 @@ class Exif { # Tags relating to image configuration 'ComponentsConfiguration' => Exif::UNDEFINED, # Meaning of each component #p33 'CompressedBitsPerPixel' => Exif::RATIONAL, # Image compression mode - 'PixelYDimension' => Exif::SHORT.','.Exif::LONG, # Valid image width - 'PixelXDimension' => Exif::SHORT.','.Exif::LONG, # Valid image height + 'PixelYDimension' => Exif::SHORT_OR_LONG, # Valid image width + 'PixelXDimension' => Exif::SHORT_OR_LONG, # Valid image height # Tags relating to related user information 'MakerNote' => Exif::IGNORE, # Manufacturer notes @@ -218,7 +219,7 @@ class Exif { 'FocalPlaneXResolution' => Exif::RATIONAL, # Focal plane X resolution 'FocalPlaneYResolution' => Exif::RATIONAL, # Focal plane Y resolution 'FocalPlaneResolutionUnit' => Exif::SHORT, # Focal plane resolution unit #p46 - 'SubjectLocation' => array( Exif::SHORT, 2), # Subject location + 'SubjectLocation' => array( Exif::SHORT, 2 ), # Subject location 'ExposureIndex' => Exif::RATIONAL, # Exposure index 'SensingMethod' => Exif::SHORT, # Sensing method #p46 'FileSource' => Exif::UNDEFINED, # File source #p47 @@ -250,12 +251,12 @@ class Exif { 'GPSLatitudeRef' => Exif::ASCII, # North or South Latitude #p52-53 'GPSLatitude' => array( Exif::RATIONAL, 3 ), # Latitude 'GPSLongitudeRef' => Exif::ASCII, # East or West Longitude #p53 - 'GPSLongitude' => array( Exif::RATIONAL, 3), # Longitude + 'GPSLongitude' => array( Exif::RATIONAL, 3 ), # Longitude 'GPSAltitudeRef' => Exif::UNDEFINED, # Altitude reference. Note, the exif standard says this should be an EXIF::Byte, # but php seems to disagree. 'GPSAltitude' => Exif::RATIONAL, # Altitude - 'GPSTimeStamp' => array( Exif::RATIONAL, 3), # GPS time (atomic clock) + 'GPSTimeStamp' => array( Exif::RATIONAL, 3 ), # GPS time (atomic clock) 'GPSSatellites' => Exif::ASCII, # Satellites used for measurement 'GPSStatus' => Exif::ASCII, # Receiver status #p54 'GPSMeasureMode' => Exif::ASCII, # Measurement mode #p54-55 @@ -296,7 +297,7 @@ class Exif { } $this->debugFile( $this->basename, __FUNCTION__, true ); - if( function_exists( 'exif_read_data' ) ) { + if ( function_exists( 'exif_read_data' ) ) { wfSuppressWarnings(); $data = exif_read_data( $this->file, 0, true ); wfRestoreWarnings(); @@ -393,7 +394,7 @@ class Exif { //ComponentsConfiguration should really be an array instead of a string... //This turns a string of binary numbers into an array of numbers. - if ( isset ( $this->mFilteredExifData['ComponentsConfiguration'] ) ) { + if ( isset( $this->mFilteredExifData['ComponentsConfiguration'] ) ) { $val = $this->mFilteredExifData['ComponentsConfiguration']; $ccVals = array(); for ( $i = 0; $i < strlen( $val ); $i++ ) { @@ -410,7 +411,7 @@ class Exif { //Also change exif tag name from GPSVersion (what php exif thinks it is) //to GPSVersionID (what the exif standard thinks it is). - if ( isset ( $this->mFilteredExifData['GPSVersion'] ) ) { + if ( isset( $this->mFilteredExifData['GPSVersion'] ) ) { $val = $this->mFilteredExifData['GPSVersion']; $newVal = ''; for ( $i = 0; $i < strlen( $val ); $i++ ) { @@ -439,7 +440,7 @@ class Exif { * This has not been tested on any shift-JIS strings. * @param string $prop prop name. */ - private function charCodeString ( $prop ) { + private function charCodeString( $prop ) { if ( isset( $this->mFilteredExifData[$prop] ) ) { if ( strlen( $this->mFilteredExifData[$prop] ) <= 8 ) { @@ -500,7 +501,7 @@ class Exif { * the type of UNDEFINED field * @param string $prop name of property */ - private function exifPropToOrd ( $prop ) { + private function exifPropToOrd( $prop ) { if ( isset( $this->mFilteredExifData[$prop] ) ) { $this->mFilteredExifData[$prop] = ord( $this->mFilteredExifData[$prop] ); } @@ -510,7 +511,7 @@ class Exif { * for example 10 degress 20`40`` S -> -10.34444 * @param string $prop a gps coordinate exif tag name (like GPSLongitude) */ - private function exifGPStoNumber ( $prop ) { + private function exifGPStoNumber( $prop ) { $loc =& $this->mFilteredExifData[$prop]; $dir =& $this->mFilteredExifData[$prop . 'Ref']; $res = false; @@ -737,26 +738,27 @@ class Exif { $debug = "tag is '$tag'"; $etype = $this->mExifTags[$section][$tag]; $ecount = 1; - if( is_array( $etype ) ) { + if ( is_array( $etype ) ) { list( $etype, $ecount ) = $etype; - if ( $recursive ) + if ( $recursive ) { $ecount = 1; // checking individual elements + } } $count = count( $val ); - if( $ecount != $count ) { + if ( $ecount != $count ) { $this->debug( $val, __FUNCTION__, "Expected $ecount elements for $tag but got $count" ); return false; } - if( $count > 1 ) { - foreach( $val as $v ) { - if( !$this->validate( $section, $tag, $v, true ) ) { + if ( $count > 1 ) { + foreach ( $val as $v ) { + if ( !$this->validate( $section, $tag, $v, true ) ) { return false; } } return true; } // Does not work if not typecast - switch( (string)$etype ) { + switch ( (string)$etype ) { case (string)Exif::BYTE: $this->debug( $val, __FUNCTION__, $debug ); return $this->isByte( $val ); @@ -772,6 +774,9 @@ class Exif { case (string)Exif::RATIONAL: $this->debug( $val, __FUNCTION__, $debug ); return $this->isRational( $val ); + case (string)Exif::SHORT_OR_LONG: + $this->debug( $val, __FUNCTION__, $debug ); + return $this->isShort( $val ) || $this->isLong( $val ); case (string)Exif::UNDEFINED: $this->debug( $val, __FUNCTION__, $debug ); return $this->isUndefined( $val ); @@ -781,9 +786,6 @@ class Exif { case (string)Exif::SRATIONAL: $this->debug( $val, __FUNCTION__, $debug ); return $this->isSrational( $val ); - case (string)Exif::SHORT.','.Exif::LONG: - $this->debug( $val, __FUNCTION__, $debug ); - return $this->isShort( $val ) || $this->isLong( $val ); case (string)Exif::IGNORE: $this->debug( $val, __FUNCTION__, $debug ); return false; @@ -808,7 +810,7 @@ class Exif { } $type = gettype( $in ); $class = ucfirst( __CLASS__ ); - if ( $type === 'array' ) { + if ( is_array( $in ) ) { $in = print_r( $in, true ); } diff --git a/includes/media/ExifBitmap.php b/includes/media/ExifBitmap.php index 1671ab25..d8d0bede 100644 --- a/includes/media/ExifBitmap.php +++ b/includes/media/ExifBitmap.php @@ -51,11 +51,11 @@ class ExifBitmapHandler extends BitmapHandler { // Treat Software as a special case because in can contain // an array of (SoftwareName, Version). - if (isset( $metadata['Software'] ) + if ( isset( $metadata['Software'] ) && is_array( $metadata['Software'] ) - && is_array( $metadata['Software'][0]) + && is_array( $metadata['Software'][0] ) && isset( $metadata['Software'][0][0] ) - && isset( $metadata['Software'][0][1]) + && isset( $metadata['Software'][0][1] ) ) { $metadata['Software'] = $metadata['Software'][0][0] . ' (Version ' . $metadata['Software'][0][1] . ')'; @@ -84,7 +84,7 @@ class ExifBitmapHandler extends BitmapHandler { return self::METADATA_GOOD; } if ( $metadata === self::OLD_BROKEN_FILE ) { - # Old special value indicating that there is no EXIF data in the file. + # Old special value indicating that there is no Exif data in the file. # or that there was an error well extracting the metadata. wfDebug( __METHOD__ . ": back-compat version\n" ); return self::METADATA_COMPATIBLE; diff --git a/includes/media/FormatMetadata.php b/includes/media/FormatMetadata.php index 1a7d7723..1c5136f5 100644 --- a/includes/media/FormatMetadata.php +++ b/includes/media/FormatMetadata.php @@ -79,7 +79,7 @@ class FormatMetadata { } //This is done differently as the tag is an array. - if ( $tag == 'GPSTimeStamp' && count( $vals ) === 3) { + if ( $tag == 'GPSTimeStamp' && count( $vals ) === 3 ) { //hour min sec array $h = explode( '/', $vals[0] ); @@ -127,9 +127,9 @@ class FormatMetadata { foreach ( $vals as &$val ) { - switch( $tag ) { + switch ( $tag ) { case 'Compression': - switch( $val ) { + switch ( $val ) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 32773: case 32946: case 34712: @@ -142,7 +142,7 @@ class FormatMetadata { break; case 'PhotometricInterpretation': - switch( $val ) { + switch ( $val ) { case 2: case 6: $val = self::msg( $tag, $val ); break; @@ -153,7 +153,7 @@ class FormatMetadata { break; case 'Orientation': - switch( $val ) { + switch ( $val ) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: $val = self::msg( $tag, $val ); break; @@ -164,7 +164,7 @@ class FormatMetadata { break; case 'PlanarConfiguration': - switch( $val ) { + switch ( $val ) { case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -189,7 +189,7 @@ class FormatMetadata { case 'XResolution': case 'YResolution': - switch( $resolutionunit ) { + switch ( $resolutionunit ) { case 2: $val = self::msg( 'XYResolution', 'i', self::formatNum( $val ) ); break; @@ -208,7 +208,7 @@ class FormatMetadata { break; case 'ColorSpace': - switch( $val ) { + switch ( $val ) { case 1: case 65535: $val = self::msg( $tag, $val ); break; @@ -219,7 +219,7 @@ class FormatMetadata { break; case 'ComponentsConfiguration': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: $val = self::msg( $tag, $val ); break; @@ -267,7 +267,7 @@ class FormatMetadata { break; case 'ExposureProgram': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: $val = self::msg( $tag, $val ); break; @@ -282,7 +282,7 @@ class FormatMetadata { break; case 'MeteringMode': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255: $val = self::msg( $tag, $val ); break; @@ -293,7 +293,7 @@ class FormatMetadata { break; case 'LightSource': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 255: @@ -307,11 +307,11 @@ class FormatMetadata { case 'Flash': $flashDecode = array( - 'fired' => $val & bindec( '00000001' ), - 'return' => ( $val & bindec( '00000110' ) ) >> 1, - 'mode' => ( $val & bindec( '00011000' ) ) >> 3, + 'fired' => $val & bindec( '00000001' ), + 'return' => ( $val & bindec( '00000110' ) ) >> 1, + 'mode' => ( $val & bindec( '00011000' ) ) >> 3, 'function' => ( $val & bindec( '00100000' ) ) >> 5, - 'redeye' => ( $val & bindec( '01000000' ) ) >> 6, + 'redeye' => ( $val & bindec( '01000000' ) ) >> 6, // 'reserved' => ($val & bindec( '10000000' )) >> 7, ); $flashMsgs = array(); @@ -328,7 +328,7 @@ class FormatMetadata { break; case 'FocalPlaneResolutionUnit': - switch( $val ) { + switch ( $val ) { case 2: $val = self::msg( $tag, $val ); break; @@ -339,7 +339,7 @@ class FormatMetadata { break; case 'SensingMethod': - switch( $val ) { + switch ( $val ) { case 1: case 2: case 3: case 4: case 5: case 7: case 8: $val = self::msg( $tag, $val ); break; @@ -350,7 +350,7 @@ class FormatMetadata { break; case 'FileSource': - switch( $val ) { + switch ( $val ) { case 3: $val = self::msg( $tag, $val ); break; @@ -361,7 +361,7 @@ class FormatMetadata { break; case 'SceneType': - switch( $val ) { + switch ( $val ) { case 1: $val = self::msg( $tag, $val ); break; @@ -372,7 +372,7 @@ class FormatMetadata { break; case 'CustomRendered': - switch( $val ) { + switch ( $val ) { case 0: case 1: $val = self::msg( $tag, $val ); break; @@ -383,7 +383,7 @@ class FormatMetadata { break; case 'ExposureMode': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -394,7 +394,7 @@ class FormatMetadata { break; case 'WhiteBalance': - switch( $val ) { + switch ( $val ) { case 0: case 1: $val = self::msg( $tag, $val ); break; @@ -405,7 +405,7 @@ class FormatMetadata { break; case 'SceneCaptureType': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: $val = self::msg( $tag, $val ); break; @@ -416,7 +416,7 @@ class FormatMetadata { break; case 'GainControl': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: case 4: $val = self::msg( $tag, $val ); break; @@ -427,7 +427,7 @@ class FormatMetadata { break; case 'Contrast': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -438,7 +438,7 @@ class FormatMetadata { break; case 'Saturation': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -449,7 +449,7 @@ class FormatMetadata { break; case 'Sharpness': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -460,7 +460,7 @@ class FormatMetadata { break; case 'SubjectDistanceRange': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: case 3: $val = self::msg( $tag, $val ); break; @@ -473,7 +473,7 @@ class FormatMetadata { //The GPS...Ref values are kept for compatibility, probably won't be reached. case 'GPSLatitudeRef': case 'GPSDestLatitudeRef': - switch( $val ) { + switch ( $val ) { case 'N': case 'S': $val = self::msg( 'GPSLatitude', $val ); break; @@ -485,7 +485,7 @@ class FormatMetadata { case 'GPSLongitudeRef': case 'GPSDestLongitudeRef': - switch( $val ) { + switch ( $val ) { case 'E': case 'W': $val = self::msg( 'GPSLongitude', $val ); break; @@ -504,7 +504,7 @@ class FormatMetadata { break; case 'GPSStatus': - switch( $val ) { + switch ( $val ) { case 'A': case 'V': $val = self::msg( $tag, $val ); break; @@ -515,7 +515,7 @@ class FormatMetadata { break; case 'GPSMeasureMode': - switch( $val ) { + switch ( $val ) { case 2: case 3: $val = self::msg( $tag, $val ); break; @@ -528,7 +528,7 @@ class FormatMetadata { case 'GPSTrackRef': case 'GPSImgDirectionRef': case 'GPSDestBearingRef': - switch( $val ) { + switch ( $val ) { case 'T': case 'M': $val = self::msg( 'GPSDirection', $val ); break; @@ -548,7 +548,7 @@ class FormatMetadata { break; case 'GPSSpeedRef': - switch( $val ) { + switch ( $val ) { case 'K': case 'M': case 'N': $val = self::msg( 'GPSSpeed', $val ); break; @@ -559,7 +559,7 @@ class FormatMetadata { break; case 'GPSDestDistanceRef': - switch( $val ) { + switch ( $val ) { case 'K': case 'M': case 'N': $val = self::msg( 'GPSDestDistance', $val ); break; @@ -646,7 +646,7 @@ class FormatMetadata { break; case 'iimCategory': - switch( strtolower( $val ) ) { + switch ( strtolower( $val ) ) { // See pg 29 of IPTC photo // metadata standard. case 'ace': case 'clj': @@ -682,7 +682,7 @@ class FormatMetadata { $urgency = 'high'; } elseif ( $val == 5 ) { $urgency = 'normal'; - } elseif ( $val <= 8 && $val > 5) { + } elseif ( $val <= 8 && $val > 5 ) { $urgency = 'low'; } @@ -791,7 +791,7 @@ class FormatMetadata { } break; case 'Copyrighted': - switch( $val ) { + switch ( $val ) { case 'True': case 'False': $val = self::msg( $tag, $val ); break; @@ -854,7 +854,7 @@ class FormatMetadata { return $vals[0]; } elseif ( count( $vals ) === 0 ) { - wfDebug( __METHOD__ . ' metadata array with 0 elements!' ); + wfDebug( __METHOD__ . " metadata array with 0 elements!\n" ); return ""; // paranoia. This should never happen } /* @todo FIXME: This should hide some of the list entries if there are @@ -863,7 +863,7 @@ class FormatMetadata { */ else { global $wgContLang; - switch( $type ) { + switch ( $type ) { case 'lang': // Display default, followed by ContLang, // followed by the rest in no particular @@ -948,7 +948,7 @@ class FormatMetadata { * this is treated as wikitext not html). */ private static function langItem( $value, $lang, $default = false, $noHtml = false ) { - if ( $lang === false && $default === false) { + if ( $lang === false && $default === false ) { throw new MWException( '$lang and $default cannot both ' . 'be false.' ); } @@ -1015,8 +1015,9 @@ class FormatMetadata { static function msg( $tag, $val, $arg = null, $arg2 = null ) { global $wgContLang; - if ( $val === '' ) + if ( $val === '' ) { $val = 'value'; + } return wfMessage( $wgContLang->lc( "exif-$tag-$val" ), $arg, $arg2 )->text(); } @@ -1031,9 +1032,9 @@ class FormatMetadata { static function formatNum( $num, $round = false ) { global $wgLang; $m = array(); - if( is_array( $num ) ) { + if ( is_array( $num ) ) { $out = array(); - foreach( $num as $number ) { + foreach ( $num as $number ) { $out[] = self::formatNum( $number ); } return $wgLang->commaList( $out ); @@ -1071,7 +1072,7 @@ class FormatMetadata { $numerator = intval( $m[1] ); $denominator = intval( $m[2] ); $gcd = self::gcd( abs( $numerator ), $denominator ); - if( $gcd != 0 ) { + if ( $gcd != 0 ) { // 0 shouldn't happen! ;) return self::formatNum( $numerator / $gcd ) . '/' . self::formatNum( $denominator / $gcd ); } @@ -1096,7 +1097,7 @@ class FormatMetadata { else return gcd( $b, $a % $b ); */ - while( $b != 0 ) { + while ( $b != 0 ) { $remainder = $a % $b; // tail recursion... @@ -1124,7 +1125,7 @@ class FormatMetadata { return $val; } $cat = ''; - switch( substr( $val, 0, 2 ) ) { + switch ( substr( $val, 0, 2 ) ) { case '01': $cat = 'ace'; break; @@ -1236,7 +1237,7 @@ class FormatMetadata { * @return String of html-ish looking wikitext */ public static function collapseContactInfo( $vals ) { - if( !( isset( $vals['CiAdrExtadr'] ) + if ( !( isset( $vals['CiAdrExtadr'] ) || isset( $vals['CiAdrCity'] ) || isset( $vals['CiAdrCtry'] ) || isset( $vals['CiEmailWork'] ) @@ -1254,7 +1255,7 @@ class FormatMetadata { // because people often insert >, etc into // the metadata which should not be interpreted // but we still want to auto-link urls. - foreach( $vals as &$val ) { + foreach ( $vals as &$val ) { $val = htmlspecialchars( $val ); } return self::flattenArray( $vals ); diff --git a/includes/media/GIF.php b/includes/media/GIF.php index 2e532feb..608fb257 100644 --- a/includes/media/GIF.php +++ b/includes/media/GIF.php @@ -33,7 +33,7 @@ class GIFHandler extends BitmapHandler { function getMetadata( $image, $filename ) { try { $parsedGIFMetadata = BitmapMetadataHandler::GIF( $filename ); - } catch( Exception $e ) { + } catch ( Exception $e ) { // Broken file? wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); return self::BROKEN_FILE; @@ -86,7 +86,7 @@ class GIFHandler extends BitmapHandler { $ser = $image->getMetadata(); if ( $ser ) { $metadata = unserialize( $ser ); - if( $metadata['frameCount'] > 1 ) { + if ( $metadata['frameCount'] > 1 ) { return true; } } @@ -119,13 +119,13 @@ class GIFHandler extends BitmapHandler { wfRestoreWarnings(); if ( !$data || !is_array( $data ) ) { - wfDebug( __METHOD__ . ' invalid GIF metadata' ); + wfDebug( __METHOD__ . " invalid GIF metadata\n" ); return self::METADATA_BAD; } if ( !isset( $data['metadata']['_MW_GIF_VERSION'] ) || $data['metadata']['_MW_GIF_VERSION'] != GIFMetadataExtractor::VERSION ) { - wfDebug( __METHOD__ . ' old but compatible GIF metadata' ); + wfDebug( __METHOD__ . " old but compatible GIF metadata\n" ); return self::METADATA_COMPATIBLE; } return self::METADATA_GOOD; diff --git a/includes/media/GIFMetadataExtractor.php b/includes/media/GIFMetadataExtractor.php index 6a4e753d..887afa3f 100644 --- a/includes/media/GIFMetadataExtractor.php +++ b/includes/media/GIFMetadataExtractor.php @@ -90,7 +90,7 @@ class GIFMetadataExtractor { // Skip over the GCT self::readGCT( $fh, $bpp ); - while( !feof( $fh ) ) { + while ( !feof( $fh ) ) { $buf = fread( $fh, 1 ); if ( $buf == self::$gif_frame_sep ) { @@ -110,7 +110,9 @@ class GIFMetadataExtractor { self::skipBlock( $fh ); } elseif ( $buf == self::$gif_extension_sep ) { $buf = fread( $fh, 1 ); - if ( strlen( $buf ) < 1 ) throw new Exception( "Ran out of input" ); + if ( strlen( $buf ) < 1 ) { + throw new Exception( "Ran out of input" ); + } $extension_code = unpack( 'C', $buf ); $extension_code = $extension_code[1]; @@ -121,7 +123,9 @@ class GIFMetadataExtractor { fread( $fh, 1 ); // Transparency, disposal method, user input $buf = fread( $fh, 2 ); // Delay, in hundredths of seconds. - if ( strlen( $buf ) < 2 ) throw new Exception( "Ran out of input" ); + if ( strlen( $buf ) < 2 ) { + throw new Exception( "Ran out of input" ); + } $delay = unpack( 'v', $buf ); $delay = $delay[1]; $duration += $delay * 0.01; @@ -129,7 +133,9 @@ class GIFMetadataExtractor { fread( $fh, 1 ); // Transparent colour index $term = fread( $fh, 1 ); // Should be a terminator - if ( strlen( $term ) < 1 ) throw new Exception( "Ran out of input" ); + if ( strlen( $term ) < 1 ) { + throw new Exception( "Ran out of input" ); + } $term = unpack( 'C', $term ); $term = $term[1]; if ( $term != 0 ) { @@ -157,7 +163,7 @@ class GIFMetadataExtractor { $commentCount = count( $comment ); if ( $commentCount === 0 - || $comment[$commentCount-1] !== $data ) + || $comment[$commentCount - 1] !== $data ) { // Some applications repeat the same comment on each // frame of an animated GIF image, so if this comment @@ -168,14 +174,16 @@ class GIFMetadataExtractor { // Application extension (Netscape info about the animated gif) // or XMP (or theoretically any other type of extension block) $blockLength = fread( $fh, 1 ); - if ( strlen( $blockLength ) < 1 ) throw new Exception( "Ran out of input" ); + if ( strlen( $blockLength ) < 1 ) { + throw new Exception( "Ran out of input" ); + } $blockLength = unpack( 'C', $blockLength ); $blockLength = $blockLength[1]; $data = fread( $fh, $blockLength ); if ( $blockLength != 11 ) { - wfDebug( __METHOD__ . ' GIF application block with wrong length' ); - fseek( $fh, -($blockLength + 1), SEEK_CUR ); + wfDebug( __METHOD__ . " GIF application block with wrong length\n" ); + fseek( $fh, -( $blockLength + 1 ), SEEK_CUR ); self::skipBlock( $fh ); continue; } @@ -190,7 +198,9 @@ class GIFMetadataExtractor { // Unsigned little-endian integer, loop count or zero for "forever" $loopData = fread( $fh, 2 ); - if ( strlen( $loopData ) < 2 ) throw new Exception( "Ran out of input" ); + if ( strlen( $loopData ) < 2 ) { + throw new Exception( "Ran out of input" ); + } $loopData = unpack( 'v', $loopData ); $loopCount = $loopData[1]; @@ -218,7 +228,7 @@ class GIFMetadataExtractor { } else { // unrecognized extension block - fseek( $fh, -($blockLength + 1), SEEK_CUR ); + fseek( $fh, -( $blockLength + 1 ), SEEK_CUR ); self::skipBlock( $fh ); continue; } @@ -228,7 +238,9 @@ class GIFMetadataExtractor { } elseif ( $buf == self::$gif_term ) { break; } else { - if ( strlen( $buf ) < 1 ) throw new Exception( "Ran out of input" ); + if ( strlen( $buf ) < 1 ) { + throw new Exception( "Ran out of input" ); + } $byte = unpack( 'C', $buf ); $byte = $byte[1]; throw new Exception( "At position: " . ftell( $fh ) . ", Unknown byte " . $byte ); @@ -251,7 +263,7 @@ class GIFMetadataExtractor { */ static function readGCT( $fh, $bpp ) { if ( $bpp > 0 ) { - for( $i = 1; $i <= pow( 2, $bpp ); ++$i ) { + for ( $i = 1; $i <= pow( 2, $bpp ); ++$i ) { fread( $fh, 3 ); } } @@ -263,7 +275,9 @@ class GIFMetadataExtractor { * @return int */ static function decodeBPP( $data ) { - if ( strlen( $data ) < 1 ) throw new Exception( "Ran out of input" ); + if ( strlen( $data ) < 1 ) { + throw new Exception( "Ran out of input" ); + } $buf = unpack( 'C', $data ); $buf = $buf[1]; $bpp = ( $buf & 7 ) + 1; @@ -281,7 +295,9 @@ class GIFMetadataExtractor { static function skipBlock( $fh ) { while ( !feof( $fh ) ) { $buf = fread( $fh, 1 ); - if ( strlen( $buf ) < 1 ) throw new Exception( "Ran out of input" ); + if ( strlen( $buf ) < 1 ) { + throw new Exception( "Ran out of input" ); + } $block_len = unpack( 'C', $buf ); $block_len = $block_len[1]; if ( $block_len == 0 ) { @@ -310,7 +326,7 @@ class GIFMetadataExtractor { $subLength = fread( $fh, 1 ); $blocks = 0; - while( $subLength !== "\0" ) { + while ( $subLength !== "\0" ) { $blocks++; if ( $blocks > self::MAX_SUBBLOCKS ) { throw new Exception( "MAX_SUBBLOCKS exceeded (over $blocks sub-blocks)" ); diff --git a/includes/media/IPTC.php b/includes/media/IPTC.php index 4191cde0..544dd211 100644 --- a/includes/media/IPTC.php +++ b/includes/media/IPTC.php @@ -63,7 +63,7 @@ class IPTC { wfDebugLog( 'iptc', "IPTC tag $tag had only whitespace as its value." ); continue; } - switch( $tag ) { + switch ( $tag ) { case '2#120': /*IPTC caption. mapped with exif ImageDescription*/ $data['ImageDescription'] = self::convIPTC( $val, $c ); break; @@ -396,7 +396,7 @@ class IPTC { return null; } - $tz = ( intval( substr( $time, 7, 2 ) ) *60*60 ) + $tz = ( intval( substr( $time, 7, 2 ) ) * 60 * 60 ) + ( intval( substr( $time, 9, 2 ) ) * 60 ); if ( substr( $time, 6, 1 ) === '-' ) { @@ -423,7 +423,7 @@ class IPTC { * * @return string|array */ - private static function convIPTC ( $data, $charset ) { + private static function convIPTC( $data, $charset ) { if ( is_array( $data ) ) { foreach ( $data as &$val ) { $val = self::convIPTCHelper( $val, $charset ); @@ -441,7 +441,7 @@ class IPTC { * * @return string */ - private static function convIPTCHelper ( $data, $charset ) { + private static function convIPTCHelper( $data, $charset ) { if ( $charset ) { wfSuppressWarnings(); $data = iconv( $charset, "UTF-8//IGNORE", $data ); diff --git a/includes/media/ImageHandler.php b/includes/media/ImageHandler.php index 419afeef..e079003b 100644 --- a/includes/media/ImageHandler.php +++ b/includes/media/ImageHandler.php @@ -58,7 +58,7 @@ abstract class ImageHandler extends MediaHandler { } elseif ( isset( $params['width'] ) ) { $width = $params['width']; } else { - throw new MWException( 'No width specified to '.__METHOD__ ); + throw new MWException( 'No width specified to ' . __METHOD__ ); } # Removed for ProofreadPage #$width = intval( $width ); @@ -92,7 +92,7 @@ abstract class ImageHandler extends MediaHandler { if ( !isset( $params['page'] ) ) { $params['page'] = 1; - } else { + } else { if ( $params['page'] > $image->pageCount() ) { $params['page'] = $image->pageCount(); } @@ -160,7 +160,7 @@ abstract class ImageHandler extends MediaHandler { $width = intval( $width ); # Sanity check $width - if( $width <= 0) { + if ( $width <= 0 ) { wfDebug( __METHOD__ . ": Invalid destination width: $width\n" ); return false; } @@ -187,9 +187,9 @@ abstract class ImageHandler extends MediaHandler { if ( !$this->normaliseParams( $image, $params ) ) { return false; } - $url = $script . '&' . wfArrayToCgi( $this->getScriptParams( $params ) ); + $url = wfAppendQuery( $script, $this->getScriptParams( $params ) ); - if( $image->mustRender() || $params['width'] < $image->getWidth() ) { + if ( $image->mustRender() || $params['width'] < $image->getWidth() ) { return new ThumbnailImage( $image, $url, false, $params ); } } @@ -200,6 +200,19 @@ abstract class ImageHandler extends MediaHandler { wfRestoreWarnings(); return $gis; } + /** + * Function that returns the number of pixels to be thumbnailed. + * Intended for animated GIFs to multiply by the number of frames. + * + * If the file doesn't support a notion of "area" return 0. + * + * @param File $image + * @return int + */ + function getImageArea( $image ) { + return $image->getWidth() * $image->getHeight(); + } + /** * @param $file File diff --git a/includes/media/Jpeg.php b/includes/media/Jpeg.php index 8b5d6513..fa763668 100644 --- a/includes/media/Jpeg.php +++ b/includes/media/Jpeg.php @@ -32,7 +32,7 @@ */ class JpegHandler extends ExifBitmapHandler { - function getMetadata ( $image, $filename ) { + function getMetadata( $image, $filename ) { try { $meta = BitmapMetadataHandler::Jpeg( $filename ); if ( !is_array( $meta ) ) { @@ -69,18 +69,18 @@ class JpegHandler extends ExifBitmapHandler { public function rotate( $file, $params ) { global $wgJpegTran; - $rotation = ( $params[ 'rotation' ] + $this->getRotation( $file ) ) % 360; + $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360; - if( $wgJpegTran && is_file( $wgJpegTran ) ){ + if ( $wgJpegTran && is_file( $wgJpegTran ) ) { $cmd = wfEscapeShellArg( $wgJpegTran ) . " -rotate " . wfEscapeShellArg( $rotation ) . - " -outfile " . wfEscapeShellArg( $params[ 'dstPath' ] ) . - " " . wfEscapeShellArg( $params[ 'srcPath' ] ) . " 2>&1"; - wfDebug( __METHOD__ . ": running jpgtran: $cmd\n" ); - wfProfileIn( 'jpegtran' ); - $retval = 0; - $err = wfShellExec( $cmd, $retval, $env ); - wfProfileOut( 'jpegtran' ); + " -outfile " . wfEscapeShellArg( $params['dstPath'] ) . + " " . wfEscapeShellArg( $params['srcPath'] ); + wfDebug( __METHOD__ . ": running jpgtran: $cmd\n" ); + wfProfileIn( 'jpegtran' ); + $retval = 0; + $err = wfShellExecWithStderr( $cmd, $retval, $env ); + wfProfileOut( 'jpegtran' ); if ( $retval !== 0 ) { $this->logErrorForExternalProcess( $retval, $err, $cmd ); return new MediaTransformError( 'thumbnail_error', 0, 0, $err ); diff --git a/includes/media/JpegMetadataExtractor.php b/includes/media/JpegMetadataExtractor.php index 6ff07ed2..c7030eba 100644 --- a/includes/media/JpegMetadataExtractor.php +++ b/includes/media/JpegMetadataExtractor.php @@ -47,7 +47,7 @@ class JpegMetadataExtractor { * @return Array of interesting segments. * @throws MWException if given invalid file. */ - static function segmentSplitter ( $filename ) { + static function segmentSplitter( $filename ) { $showXMP = function_exists( 'xml_parser_create_ns' ); $segmentCount = 0; @@ -87,7 +87,7 @@ class JpegMetadataExtractor { } $buffer = fread( $fh, 1 ); - while( $buffer === "\xFF" && !feof( $fh ) ) { + while ( $buffer === "\xFF" && !feof( $fh ) ) { // Skip through any 0xFF padding bytes. $buffer = fread( $fh, 1 ); } @@ -111,7 +111,7 @@ class JpegMetadataExtractor { if ( $com === $oldCom ) { $segments["COM"][] = $oldCom; } else { - wfDebug( __METHOD__ . ' Ignoring JPEG comment as is garbage.' ); + wfDebug( __METHOD__ . " Ignoring JPEG comment as is garbage.\n" ); } } elseif ( $buffer === "\xE1" ) { @@ -140,7 +140,7 @@ class JpegMetadataExtractor { } elseif ( $byteOrderMarker === 'II' ) { $segments['byteOrder'] = 'LE'; } else { - wfDebug( __METHOD__ . ' Invalid byte ordering?!' ); + wfDebug( __METHOD__ . " Invalid byte ordering?!\n" ); } } } elseif ( $buffer === "\xED" ) { @@ -155,7 +155,9 @@ class JpegMetadataExtractor { } else { // segment we don't care about, so skip $size = wfUnpack( "nint", fread( $fh, 2 ), 2 ); - if ( $size['int'] <= 2 ) throw new MWException( "invalid marker size in jpeg" ); + if ( $size['int'] <= 2 ) { + throw new MWException( "invalid marker size in jpeg" ); + } fseek( $fh, $size['int'] - 2, SEEK_CUR ); } @@ -195,7 +197,7 @@ class JpegMetadataExtractor { * @throws MWException (It gets caught next level up though) * @return String if the iptc hash is good or not. */ - public static function doPSIR ( $app13 ) { + public static function doPSIR( $app13 ) { if ( !$app13 ) { throw new MWException( "No App13 segment given" ); } @@ -243,7 +245,9 @@ class JpegMetadataExtractor { // PHP can take issue with very large unsigned ints and make them negative. // Which should never ever happen, as this has to be inside a segment // which is limited to a 16 bit number. - if ( $lenData['len'] < 0 ) throw new MWException( "Too big PSIR (" . $lenData['len'] . ')' ); + if ( $lenData['len'] < 0 ) { + throw new MWException( "Too big PSIR (" . $lenData['len'] . ')' ); + } $offset += 4; // 4bytes length field; @@ -267,7 +271,9 @@ class JpegMetadataExtractor { // if odd, add 1 to length to account for // null pad byte. - if ( $lenData['len'] % 2 == 1 ) $lenData['len']++; + if ( $lenData['len'] % 2 == 1 ) { + $lenData['len']++; + } $offset += $lenData['len']; } diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php index 9a3f645b..779e23c9 100644 --- a/includes/media/MediaHandler.php +++ b/includes/media/MediaHandler.php @@ -78,14 +78,16 @@ abstract class MediaHandler { /** * Merge a parameter array into a string appropriate for inclusion in filenames * - * @param $params array + * @param $params array Array of parameters that have been through normaliseParams. + * @return String */ abstract function makeParamString( $params ); /** * Parse a param string made with makeParamString back into an array * - * @param $str string + * @param $str string The parameter string without file name (e.g. 122px) + * @return Array|Boolean Array of parameters or false on failure. */ abstract function parseParamString( $str ); @@ -116,7 +118,9 @@ abstract class MediaHandler { * @param string $path the filename * @return String */ - function getMetadata( $image, $path ) { return ''; } + function getMetadata( $image, $path ) { + return ''; + } /** * Get metadata version. @@ -133,10 +137,10 @@ abstract class MediaHandler { * * @return string version string */ - static function getMetadataVersion () { + static function getMetadataVersion() { $version = Array( '2' ); // core metadata version wfRunHooks( 'GetMetadataVersion', Array( &$version ) ); - return implode( ';', $version); + return implode( ';', $version ); } /** @@ -166,7 +170,9 @@ abstract class MediaHandler { * * @return string */ - function getMetadataType( $image ) { return false; } + function getMetadataType( $image ) { + return false; + } /** * Check if the metadata string is valid for this handler. @@ -216,6 +222,7 @@ abstract class MediaHandler { * @param string $dstPath filesystem destination path * @param string $dstUrl destination URL to use in output HTML * @param array $params arbitrary set of parameters validated by $this->validateParam() + * Note: These parameters have *not* gone through $this->normaliseParams() * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER * * @return MediaTransformOutput @@ -224,6 +231,10 @@ abstract class MediaHandler { /** * Get the thumbnail extension and MIME type for a given source MIME type + * + * @param String $ext Extension of original file + * @param String $mime Mime type of original file + * @param Array $params Handler specific rendering parameters * @return array thumbnail extension and MIME type */ function getThumbType( $ext, $mime, $params = null ) { @@ -255,63 +266,96 @@ abstract class MediaHandler { * True if the handled types can be transformed * @return bool */ - function canRender( $file ) { return true; } + function canRender( $file ) { + return true; + } + /** * True if handled types cannot be displayed directly in a browser * but can be rendered * @return bool */ - function mustRender( $file ) { return false; } + function mustRender( $file ) { + return false; + } + /** * True if the type has multi-page capabilities * @return bool */ - function isMultiPage( $file ) { return false; } + function isMultiPage( $file ) { + return false; + } + /** * Page count for a multi-page document, false if unsupported or unknown * @return bool */ - function pageCount( $file ) { return false; } + function pageCount( $file ) { + return false; + } + /** * The material is vectorized and thus scaling is lossless * @return bool */ - function isVectorized( $file ) { return false; } + function isVectorized( $file ) { + return false; + } + /** * The material is an image, and is animated. * In particular, video material need not return true. * @note Before 1.20, this was a method of ImageHandler only * @return bool */ - function isAnimatedImage( $file ) { return false; } + function isAnimatedImage( $file ) { + return false; + } + /** * If the material is animated, we can animate the thumbnail * @since 1.20 * @return bool If material is not animated, handler may return any value. */ - function canAnimateThumbnail( $file ) { return true; } + function canAnimateThumbnail( $file ) { + return true; + } + /** * False if the handler is disabled for all files * @return bool */ - function isEnabled() { return true; } + function isEnabled() { + return true; + } /** * Get an associative array of page dimensions * Currently "width" and "height" are understood, but this might be * expanded in the future. - * Returns false if unknown or if the document is not multi-page. + * Returns false if unknown. + * + * It is expected that handlers for paged media (e.g. DjVuHandler) + * will override this method so that it gives the correct results + * for each specific page of the file, using the $page argument. + * + * @note For non-paged media, use getImageSize. * * @param $image File - * @param $page Unused, left for backcompatibility? - * @return array + * @param $page What page to get dimensions of + * @return array|bool */ function getPageDimensions( $image, $page ) { $gis = $this->getImageSize( $image, $image->getLocalRefPath() ); - return array( - 'width' => $gis[0], - 'height' => $gis[1] - ); + if ( $gis ) { + return array( + 'width' => $gis[0], + 'height' => $gis[1] + ); + } else { + return false; + } } /** @@ -394,9 +438,9 @@ abstract class MediaHandler { function visibleMetadataFields() { $fields = array(); $lines = explode( "\n", wfMessage( 'metadata-fields' )->inContentLanguage()->text() ); - foreach( $lines as $line ) { + foreach ( $lines as $line ) { $matches = array(); - if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) { + if ( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) { $fields[] = $matches[1]; } } @@ -448,6 +492,8 @@ abstract class MediaHandler { } /** + * Used instead of getLongDesc if there is no handler registered for file. + * * @param $file File * @return string */ @@ -457,6 +503,8 @@ abstract class MediaHandler { } /** + * Short description. Shown on Special:Search results. + * * @param $file File * @return string */ @@ -467,6 +515,8 @@ abstract class MediaHandler { } /** + * Long description. Shown under image on image description page surounded by (). + * * @param $file File * @return string */ @@ -476,6 +526,8 @@ abstract class MediaHandler { } /** + * Used instead of getShortDesc if there is no handler registered for file. + * * @param $file File * @return string */ @@ -496,19 +548,32 @@ abstract class MediaHandler { public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) { $idealWidth = $boxWidth * $maxHeight / $boxHeight; $roundedUp = ceil( $idealWidth ); - if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) { + if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) { return floor( $idealWidth ); } else { return $roundedUp; } } + /** + * Shown in file history box on image description page. + * + * @param File $file + * @return String Dimensions + */ function getDimensionsString( $file ) { return ''; } /** - * Modify the parser object post-transform + * Modify the parser object post-transform. + * + * This is often used to do $parser->addOutputHook(), + * in order to add some javascript to render a viewer. + * See TimedMediaHandler or OggHandler for an example. + * + * @param Parser $parser + * @param File $file */ function parserTransformHook( $parser, $file ) {} @@ -535,9 +600,9 @@ abstract class MediaHandler { * @return bool True if removed, false otherwise */ function removeBadFile( $dstPath, $retval = 0 ) { - if( file_exists( $dstPath ) ) { + if ( file_exists( $dstPath ) ) { $thumbstat = stat( $dstPath ); - if( $thumbstat['size'] == 0 || $retval != 0 ) { + if ( $thumbstat['size'] == 0 || $retval != 0 ) { $result = unlink( $dstPath ); if ( $result ) { @@ -556,10 +621,17 @@ abstract class MediaHandler { } /** - * Remove files from the purge list + * Remove files from the purge list. + * + * This is used by some video handlers to prevent ?action=purge + * from removing a transcoded video, which is expensive to + * regenerate. + * + * @see LocalFile::purgeThumbnails * * @param array $files - * @param array $options + * @param array $options Purge options. Currently will always be + * an array with a single key 'forThumbRefresh' set to true. */ public function filterThumbnailPurgeList( &$files, $options ) { // Do nothing @@ -573,4 +645,23 @@ abstract class MediaHandler { public static function canRotate() { return false; } + + /** + * 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. + * + * For files we don't know, we return 0. + * + * @param $file File + * @return int 0, 90, 180 or 270 + */ + public function getRotation( $file ) { + return 0; + } + } diff --git a/includes/media/MediaTransformOutput.php b/includes/media/MediaTransformOutput.php index 1f95bc3b..c49d3f20 100644 --- a/includes/media/MediaTransformOutput.php +++ b/includes/media/MediaTransformOutput.php @@ -32,7 +32,7 @@ abstract class MediaTransformOutput { */ var $file; - var $width, $height, $url, $page, $path; + var $width, $height, $url, $page, $path, $lang; /** * @var array Associative array mapping optional supplementary image files @@ -151,7 +151,12 @@ abstract class MediaTransformOutput { if ( $this->isError() ) { return false; } elseif ( $this->path === null ) { - return $this->file->getLocalRefPath(); + return $this->file->getLocalRefPath(); // assume thumb was not scaled + } elseif ( FileBackend::isStoragePath( $this->path ) ) { + $be = $this->file->getRepo()->getBackend(); + // The temp file will be process cached by FileBackend + $fsFile = $be->getLocalReference( array( 'src' => $this->path ) ); + return $fsFile ? $fsFile->getPath() : false; } else { return $this->path; // may return false } @@ -192,17 +197,26 @@ abstract class MediaTransformOutput { /** * @param $title string - * @param $params array + * @param $params string|array Query parameters to add * @return array */ - public function getDescLinkAttribs( $title = null, $params = '' ) { - $query = ''; + public function getDescLinkAttribs( $title = null, $params = array() ) { + if ( is_array( $params ) ) { + $query = $params; + } else { + $query = array(); + } if ( $this->page && $this->page !== 1 ) { - $query = 'page=' . urlencode( $this->page ); + $query['page'] = $this->page; } - if( $params ) { - $query .= $query ? '&' . $params : $params; + if ( $this->lang ) { + $query['lang'] = $this->lang; } + + if ( is_string( $params ) && $params !== '' ) { + $query = $params . '&' . wfArrayToCgi( $query ); + } + $attribs = array( 'href' => $this->file->getTitle()->getLocalURL( $query ), 'class' => 'image', @@ -237,10 +251,12 @@ class ThumbnailImage extends MediaTransformOutput { # Previous parameters: # $file, $url, $width, $height, $path = false, $page = false - if( is_array( $parameters ) ) { - $defaults = array( - 'page' => false - ); + $defaults = array( + 'page' => false, + 'lang' => false + ); + + if ( is_array( $parameters ) ) { $actualParams = $parameters + $defaults; } else { # Using old format, should convert. Later a warning could be added here. @@ -249,7 +265,7 @@ class ThumbnailImage extends MediaTransformOutput { 'width' => $path, 'height' => $parameters, 'page' => ( $numArgs > 5 ) ? func_get_arg( 5 ) : false - ); + ) + $defaults; $path = ( $numArgs > 4 ) ? func_get_arg( 4 ) : false; } @@ -264,6 +280,7 @@ class ThumbnailImage extends MediaTransformOutput { $this->height = round( $actualParams['height'] ); $this->page = $actualParams['page']; + $this->lang = $actualParams['lang']; } /** @@ -281,6 +298,8 @@ class ThumbnailImage extends MediaTransformOutput { * valign vertical-align property, if the output is an inline element * img-class Class applied to the \ tag, if there is such a tag * desc-query String, description link query params + * override-width Override width attribute. Should generally not set + * override-height Override height attribute. Should generally not set * custom-url-link Custom URL to link to * custom-title-link Custom Title object to link to * custom target-link Value of the target attribute, for custom-target-link @@ -296,7 +315,7 @@ class ThumbnailImage extends MediaTransformOutput { */ function toHtml( $options = array() ) { if ( count( func_get_args() ) == 2 ) { - throw new MWException( __METHOD__ .' called in the old style' ); + throw new MWException( __METHOD__ . ' called in the old style' ); } $alt = empty( $options['alt'] ) ? '' : $options['alt']; @@ -342,6 +361,12 @@ class ThumbnailImage extends MediaTransformOutput { if ( !empty( $options['img-class'] ) ) { $attribs['class'] = $options['img-class']; } + if ( isset( $options['override-height'] ) ) { + $attribs['height'] = $options['override-height']; + } + if ( isset( $options['override-width'] ) ) { + $attribs['width'] = $options['override-width']; + } // Additional densities for responsive images, if specified. if ( !empty( $this->responsiveUrls ) ) { diff --git a/includes/media/PNG.php b/includes/media/PNG.php index b8a5b40b..98f13861 100644 --- a/includes/media/PNG.php +++ b/includes/media/PNG.php @@ -38,7 +38,7 @@ class PNGHandler extends BitmapHandler { function getMetadata( $image, $filename ) { try { $metadata = BitmapMetadataHandler::PNG( $filename ); - } catch( Exception $e ) { + } catch ( Exception $e ) { // Broken file? wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); return self::BROKEN_FILE; @@ -76,7 +76,9 @@ class PNGHandler extends BitmapHandler { $ser = $image->getMetadata(); if ( $ser ) { $metadata = unserialize( $ser ); - if( $metadata['frameCount'] > 1 ) return true; + if ( $metadata['frameCount'] > 1 ) { + return true; + } } return false; } @@ -105,13 +107,13 @@ class PNGHandler extends BitmapHandler { wfRestoreWarnings(); if ( !$data || !is_array( $data ) ) { - wfDebug( __METHOD__ . ' invalid png metadata' ); + wfDebug( __METHOD__ . " invalid png metadata\n" ); return self::METADATA_BAD; } if ( !isset( $data['metadata']['_MW_PNG_VERSION'] ) || $data['metadata']['_MW_PNG_VERSION'] != PNGMetadataExtractor::VERSION ) { - wfDebug( __METHOD__ . ' old but compatible png metadata' ); + wfDebug( __METHOD__ . " old but compatible png metadata\n" ); return self::METADATA_COMPATIBLE; } return self::METADATA_GOOD; @@ -129,8 +131,9 @@ class PNGHandler extends BitmapHandler { $metadata = unserialize( $image->getMetadata() ); wfRestoreWarnings(); - if( !$metadata || $metadata['frameCount'] <= 0 ) + if ( !$metadata || $metadata['frameCount'] <= 0 ) { return $original; + } $info = array(); $info[] = $original; diff --git a/includes/media/PNGMetadataExtractor.php b/includes/media/PNGMetadataExtractor.php index 87f705ca..845d212a 100644 --- a/includes/media/PNGMetadataExtractor.php +++ b/includes/media/PNGMetadataExtractor.php @@ -142,7 +142,7 @@ class PNGMetadataExtractor { } } elseif ( $chunk_type == "acTL" ) { $buf = fread( $fh, $chunk_size ); - if( !$buf || strlen( $buf ) < $chunk_size || $chunk_size < 4 ) { + if ( !$buf || strlen( $buf ) < $chunk_size || $chunk_size < 4 ) { throw new Exception( __METHOD__ . ": Read error" ); } @@ -202,21 +202,21 @@ class PNGMetadataExtractor { if ( $items[5] === false ) { // decompression failed - wfDebug( __METHOD__ . ' Error decompressing iTxt chunk - ' . $items[1] ); + wfDebug( __METHOD__ . ' Error decompressing iTxt chunk - ' . $items[1] . "\n" ); fseek( $fh, self::$CRC_size, SEEK_CUR ); continue; } } else { wfDebug( __METHOD__ . ' Skipping compressed png iTXt chunk due to lack of zlib,' - . ' or potentially invalid compression method' ); + . " or potentially invalid compression method\n" ); fseek( $fh, self::$CRC_size, SEEK_CUR ); continue; } } - $finalKeyword = self::$text_chunks[ $items[1] ]; - $text[ $finalKeyword ][ $items[3] ] = $items[5]; - $text[ $finalKeyword ]['_type'] = 'lang'; + $finalKeyword = self::$text_chunks[$items[1]]; + $text[$finalKeyword][$items[3]] = $items[5]; + $text[$finalKeyword]['_type'] = 'lang'; } else { // Error reading iTXt chunk @@ -251,9 +251,9 @@ class PNGMetadataExtractor { throw new Exception( __METHOD__ . ": Read error (error with iconv)" ); } - $finalKeyword = self::$text_chunks[ $keyword ]; - $text[ $finalKeyword ][ 'x-default' ] = $content; - $text[ $finalKeyword ]['_type'] = 'lang'; + $finalKeyword = self::$text_chunks[$keyword]; + $text[$finalKeyword]['x-default'] = $content; + $text[$finalKeyword]['_type'] = 'lang'; } elseif ( $chunk_type == 'zTXt' ) { if ( function_exists( 'gzuncompress' ) ) { @@ -279,7 +279,7 @@ class PNGMetadataExtractor { $compression = substr( $postKeyword, 0, 1 ); $content = substr( $postKeyword, 1 ); if ( $compression !== "\x00" ) { - wfDebug( __METHOD__ . " Unrecognized compression method in zTXt ($keyword). Skipping." ); + wfDebug( __METHOD__ . " Unrecognized compression method in zTXt ($keyword). Skipping.\n" ); fseek( $fh, self::$CRC_size, SEEK_CUR ); continue; } @@ -290,7 +290,7 @@ class PNGMetadataExtractor { if ( $content === false ) { // decompression failed - wfDebug( __METHOD__ . ' Error decompressing zTXt chunk - ' . $keyword ); + wfDebug( __METHOD__ . ' Error decompressing zTXt chunk - ' . $keyword . "\n" ); fseek( $fh, self::$CRC_size, SEEK_CUR ); continue; } @@ -303,12 +303,12 @@ class PNGMetadataExtractor { throw new Exception( __METHOD__ . ": Read error (error with iconv)" ); } - $finalKeyword = self::$text_chunks[ $keyword ]; - $text[ $finalKeyword ][ 'x-default' ] = $content; - $text[ $finalKeyword ]['_type'] = 'lang'; + $finalKeyword = self::$text_chunks[$keyword]; + $text[$finalKeyword]['x-default'] = $content; + $text[$finalKeyword]['_type'] = 'lang'; } else { - wfDebug( __METHOD__ . " Cannot decompress zTXt chunk due to lack of zlib. Skipping." ); + wfDebug( __METHOD__ . " Cannot decompress zTXt chunk due to lack of zlib. Skipping.\n" ); fseek( $fh, $chunk_size, SEEK_CUR ); } } elseif ( $chunk_type == 'tIME' ) { diff --git a/includes/media/SVG.php b/includes/media/SVG.php index cddab51d..72a9696c 100644 --- a/includes/media/SVG.php +++ b/includes/media/SVG.php @@ -56,7 +56,7 @@ class SvgHandler extends ImageHandler { $metadata = $file->getMetadata(); if ( $metadata ) { $metadata = $this->unpackMetadata( $metadata ); - if( isset( $metadata['animated'] ) ) { + if ( isset( $metadata['animated'] ) ) { return $metadata['animated']; } } @@ -115,6 +115,7 @@ class SvgHandler extends ImageHandler { $clientHeight = $params['height']; $physicalWidth = $params['physicalWidth']; $physicalHeight = $params['physicalHeight']; + $lang = isset( $params['lang'] ) ? $params['lang'] : 'en'; if ( $flags & self::TRANSFORM_LATER ) { return new ThumbnailImage( $image, $dstUrl, $dstPath, $params ); @@ -132,7 +133,7 @@ class SvgHandler extends ImageHandler { } $srcPath = $image->getLocalRefPath(); - $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight ); + $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight, $lang ); if ( $status === true ) { return new ThumbnailImage( $image, $dstUrl, $dstPath, $params ); } else { @@ -147,10 +148,11 @@ class SvgHandler extends ImageHandler { * @param string $dstPath * @param string $width * @param string $height + * @param string $lang Language code of the language to render the SVG in * @throws MWException * @return bool|MediaTransformError */ - public function rasterize( $srcPath, $dstPath, $width, $height ) { + public function rasterize( $srcPath, $dstPath, $width, $height, $lang = false ) { global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath; $err = false; $retval = ''; @@ -158,7 +160,7 @@ class SvgHandler extends ImageHandler { if ( is_array( $wgSVGConverters[$wgSVGConverter] ) ) { // This is a PHP callable $func = $wgSVGConverters[$wgSVGConverter][0]; - $args = array_merge( array( $srcPath, $dstPath, $width, $height ), + $args = array_merge( array( $srcPath, $dstPath, $width, $height, $lang ), array_slice( $wgSVGConverters[$wgSVGConverter], 1 ) ); if ( !is_callable( $func ) ) { throw new MWException( "$func is not callable" ); @@ -175,10 +177,16 @@ class SvgHandler extends ImageHandler { wfEscapeShellArg( $srcPath ), wfEscapeShellArg( $dstPath ) ), $wgSVGConverters[$wgSVGConverter] - ) . " 2>&1"; + ); + + $env = array(); + if ( $lang !== false ) { + $env['LANG'] = $lang; + } + wfProfileIn( 'rsvg' ); wfDebug( __METHOD__ . ": $cmd\n" ); - $err = wfShellExec( $cmd, $retval ); + $err = wfShellExecWithStderr( $cmd, $retval, $env ); wfProfileOut( 'rsvg' ); } } @@ -263,11 +271,11 @@ class SvgHandler extends ImageHandler { $metadata = array( 'version' => self::SVG_METADATA_VERSION ); try { $metadata += SVGMetadataExtractor::getMetadata( $filename ); - } catch( MWException $e ) { // @TODO: SVG specific exceptions + } catch ( MWException $e ) { // @todo SVG specific exceptions // File not found, broken, etc. $metadata['error'] = array( 'message' => $e->getMessage(), - 'code' => $e->getCode() + 'code' => $e->getCode() ); wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); } @@ -340,6 +348,7 @@ class SvgHandler extends ImageHandler { 'description' => 'imagedescription', 'title' => 'objectname', ); + $showMeta = false; foreach ( $metadata as $name => $value ) { $tag = strtolower( $name ); if ( isset( $conversion[$tag] ) ) { @@ -348,6 +357,7 @@ class SvgHandler extends ImageHandler { // Do not output other metadata not in list continue; } + $showMeta = true; self::addMeta( $result, in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed', 'exif', @@ -355,6 +365,70 @@ class SvgHandler extends ImageHandler { $value ); } - return $result; + return $showMeta ? $result : false; + } + + + /** + * @param string $name Parameter name + * @param $string $value Parameter value + * @return bool Validity + */ + function validateParam( $name, $value ) { + if ( in_array( $name, array( 'width', 'height' ) ) ) { + // Reject negative heights, widths + return ( $value > 0 ); + } elseif ( $name == 'lang' ) { + // Validate $code + if ( !Language::isValidBuiltinCode( $value ) ) { + wfDebug( "Invalid user language code\n" ); + return false; + } + return true; + } + // Only lang, width and height are acceptable keys + return false; + } + + /** + * @param array $params name=>value pairs of parameters + * @return string Filename to use + */ + function makeParamString( $params ) { + $lang = ''; + if ( isset( $params['lang'] ) && $params['lang'] !== 'en' ) { + $params['lang'] = mb_strtolower( $params['lang'] ); + $lang = "lang{$params['lang']}-"; + } + if ( !isset( $params['width'] ) ) { + return false; + } + return "$lang{$params['width']}px"; + } + + function parseParamString( $str ) { + $m = false; + if ( preg_match( '/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) { + return array( 'width' => array_pop( $m ), 'lang' => $m[1] ); + } elseif ( preg_match( '/^(\d+)px$/', $str, $m ) ) { + return array( 'width' => $m[1], 'lang' => 'en' ); + } else { + return false; + } + } + + function getParamMap() { + return array( 'img_lang' => 'lang', 'img_width' => 'width' ); + } + + /** + * @param $params + * @return array + */ + function getScriptParams( $params ) { + return array( + 'width' => $params['width'], + 'lang' => $params['lang'], + ); } } diff --git a/includes/media/SVGMetadataExtractor.php b/includes/media/SVGMetadataExtractor.php index 0de212b9..2e33bb98 100644 --- a/includes/media/SVGMetadataExtractor.php +++ b/includes/media/SVGMetadataExtractor.php @@ -101,7 +101,7 @@ class SVGReader { wfSuppressWarnings(); try { $this->read(); - } catch( Exception $e ) { + } catch ( Exception $e ) { // Note, if this happens, the width/height will be taken to be 0x0. // Should we consider it the default 512x512 instead? wfRestoreWarnings(); @@ -128,12 +128,12 @@ class SVGReader { $keepReading = $this->reader->read(); /* Skip until first element */ - while( $keepReading && $this->reader->nodeType != XmlReader::ELEMENT ) { + while ( $keepReading && $this->reader->nodeType != XmlReader::ELEMENT ) { $keepReading = $this->reader->read(); } if ( $this->reader->localName != 'svg' || $this->reader->namespaceURI != self::NS_SVG ) { - throw new MWException( "Expected tag, got ". + throw new MWException( "Expected tag, got " . $this->reader->localName . " in NS " . $this->reader->namespaceURI ); } $this->debug( " tag is correct." ); @@ -144,7 +144,7 @@ class SVGReader { while ( $keepReading ) { $tag = $this->reader->localName; $type = $this->reader->nodeType; - $isSVG = ($this->reader->namespaceURI == self::NS_SVG); + $isSVG = ( $this->reader->namespaceURI == self::NS_SVG ); $this->debug( "$tag" ); @@ -185,16 +185,16 @@ class SVGReader { * @param string $name of the element that we are reading from * @param string $metafield that we will fill with the result */ - private function readField( $name, $metafield=null ) { - $this->debug ( "Read field $metafield" ); - if( !$metafield || $this->reader->nodeType != XmlReader::ELEMENT ) { + private function readField( $name, $metafield = null ) { + $this->debug( "Read field $metafield" ); + if ( !$metafield || $this->reader->nodeType != XmlReader::ELEMENT ) { return; } $keepReading = $this->reader->read(); - while( $keepReading ) { - if( $this->reader->localName == $name && $this->reader->namespaceURI == self::NS_SVG && $this->reader->nodeType == XmlReader::END_ELEMENT ) { + while ( $keepReading ) { + if ( $this->reader->localName == $name && $this->reader->namespaceURI == self::NS_SVG && $this->reader->nodeType == XmlReader::END_ELEMENT ) { break; - } elseif( $this->reader->nodeType == XmlReader::TEXT ) { + } elseif ( $this->reader->nodeType == XmlReader::TEXT ) { $this->metadata[$metafield] = trim( $this->reader->value ); } $keepReading = $this->reader->read(); @@ -207,13 +207,13 @@ class SVGReader { * @param string $metafield that we will fill with the result * @throws MWException */ - private function readXml( $metafield=null ) { - $this->debug ( "Read top level metadata" ); - if( !$metafield || $this->reader->nodeType != XmlReader::ELEMENT ) { + private function readXml( $metafield = null ) { + $this->debug( "Read top level metadata" ); + if ( !$metafield || $this->reader->nodeType != XmlReader::ELEMENT ) { return; } // TODO: find and store type of xml snippet. metadata['metadataType'] = "rdf" - if( method_exists( $this->reader, 'readInnerXML' ) ) { + if ( method_exists( $this->reader, 'readInnerXML' ) ) { $this->metadata[$metafield] = trim( $this->reader->readInnerXML() ); } else { throw new MWException( "The PHP XMLReader extension does not come with readInnerXML() method. Your libxml is probably out of date (need 2.6.20 or later)." ); @@ -227,8 +227,8 @@ class SVGReader { * @param string $name of the element that we are reading from */ private function animateFilter( $name ) { - $this->debug ( "animate filter for tag $name" ); - if( $this->reader->nodeType != XmlReader::ELEMENT ) { + $this->debug( "animate filter for tag $name" ); + if ( $this->reader->nodeType != XmlReader::ELEMENT ) { return; } if ( $this->reader->isEmptyElement ) { @@ -236,12 +236,12 @@ class SVGReader { } $exitDepth = $this->reader->depth; $keepReading = $this->reader->read(); - while( $keepReading ) { - if( $this->reader->localName == $name && $this->reader->depth <= $exitDepth + while ( $keepReading ) { + if ( $this->reader->localName == $name && $this->reader->depth <= $exitDepth && $this->reader->nodeType == XmlReader::END_ELEMENT ) { break; } elseif ( $this->reader->namespaceURI == self::NS_SVG && $this->reader->nodeType == XmlReader::ELEMENT ) { - switch( $this->reader->localName ) { + switch ( $this->reader->localName ) { case 'script': // Normally we disallow files with //