diff options
Diffstat (limited to 'includes/media')
25 files changed, 1576 insertions, 1240 deletions
diff --git a/includes/media/BMP.php b/includes/media/BMP.php index a515c635..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); + 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 99ac854b..e2444a11 100644 --- a/includes/media/Bitmap.php +++ b/includes/media/Bitmap.php @@ -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 @@ -264,7 +250,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 +267,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 +327,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 +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 ) { @@ -380,7 +366,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 +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 ) { @@ -457,7 +443,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 +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 ) { @@ -500,8 +486,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 +499,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,8 +608,9 @@ 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 $scene string The scene specification, or false if there is none + * @param string $path The file path + * @param bool|string $scene The scene specification, or false if there is none + * @throws MWException * @return string */ function escapeMagickInput( $path, $scene = false ) { @@ -653,8 +640,9 @@ 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 $scene string The scene specification, or false if there is none + * @param string $path The file path + * @param bool|string $scene The scene specification, or false if there is none + * @throws MWException * @return string */ protected function escapeMagickPath( $path, $scene = false ) { @@ -713,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) @@ -757,6 +727,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. * diff --git a/includes/media/BitmapMetadataHandler.php b/includes/media/BitmapMetadataHandler.php index 0a195547..7c39c814 100644 --- a/includes/media/BitmapMetadataHandler.php +++ b/includes/media/BitmapMetadataHandler.php @@ -47,14 +47,14 @@ class BitmapMetadataHandler { private $iptcType = 'iptc-no-hash'; /** - * This does the photoshop image resource app13 block - * of interest, IPTC-IIM metadata is stored here. - * - * Mostly just calls doPSIR and doIPTC - * - * @param String $app13 String containing app13 block from jpeg file - */ - private function doApp13 ( $app13 ) { + * This does the photoshop image resource app13 block + * of interest, IPTC-IIM metadata is stored here. + * + * Mostly just calls doPSIR and doIPTC + * + * @param string $app13 String containing app13 block from jpeg file + */ + private function doApp13( $app13 ) { try { $this->iptcType = JpegMetadataExtractor::doPSIR( $app13 ); } catch ( MWException $e ) { @@ -69,7 +69,6 @@ class BitmapMetadataHandler { $this->addMetadata( $iptc, $this->iptcType ); } - /** * Get exif info using exif class. * Basically what used to be in BitmapHandler::getMetadata(). @@ -80,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 ); @@ -91,12 +90,12 @@ class BitmapMetadataHandler { } } /** Add misc metadata. Warning: atm if the metadata category - * doesn't have a priority, it will be silently discarded. - * - * @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' ) { + * doesn't have a priority, it will be silently discarded. + * + * @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' ) { if ( isset( $this->metadata[$type] ) ) { /* merge with old data */ $metaArray = $metaArray + $this->metadata[$type]; @@ -106,15 +105,15 @@ class BitmapMetadataHandler { } /** - * Merge together the various types of metadata - * the different types have different priorites, - * and are merged in order. - * - * This function is generally called by the media handlers' getMetadata() - * - * @return Array metadata array - */ - function getMetadataArray () { + * Merge together the various types of metadata + * the different types have different priorites, + * and are merged in order. + * + * This function is generally called by the media handlers' getMetadata() + * + * @return Array metadata array + */ + function getMetadataArray() { // this seems a bit ugly... This is all so its merged in right order // based on the MWG recomendation. $temp = Array(); @@ -144,11 +143,11 @@ class BitmapMetadataHandler { /** Main entry point for jpeg's. * - * @param $filename string filename (with full path) + * @param string $filename filename (with full path) * @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(); @@ -157,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 ); } } @@ -187,10 +186,10 @@ class BitmapMetadataHandler { * merge the png various tEXt chunks to that * are interesting, but for now it only does XMP * - * @param $filename String full path to file + * @param string $filename full path to file * @return Array Array for storage in img_metadata. */ - static public function PNG ( $filename ) { + public static function PNG( $filename ) { $showXMP = function_exists( 'xml_parser_create_ns' ); $meta = new self(); @@ -216,10 +215,10 @@ class BitmapMetadataHandler { * They don't really have native metadata, so just merges together * XMP and image comment. * - * @param $filename string full path to file + * @param string $filename full path to file * @return Array metadata array */ - static public function GIF ( $filename ) { + public static function GIF( $filename ) { $meta = new self(); $baseArray = GIFMetadataExtractor::getMetadata( $filename ); @@ -240,7 +239,7 @@ class BitmapMetadataHandler { unset( $baseArray['comment'] ); unset( $baseArray['xmp'] ); - + $baseArray['metadata'] = $meta->getMetadataArray(); $baseArray['metadata']['_MW_GIF_VERSION'] = GIFMetadataExtractor::VERSION; return $baseArray; @@ -257,9 +256,10 @@ class BitmapMetadataHandler { * * The various exceptions this throws are caught later. * @param $filename String + * @throws MWException * @return Array The metadata. */ - static public function Tiff ( $filename ) { + public static function Tiff( $filename ) { if ( file_exists( $filename ) ) { $byteOrder = self::getTiffByteOrder( $filename ); if ( !$byteOrder ) { @@ -281,16 +281,18 @@ class BitmapMetadataHandler { * Read the first 2 bytes of a tiff file to figure out * Little Endian or Big Endian. Needed for exif stuff. * - * @param $filename String The filename + * @param string $filename The filename * @return String 'BE' or 'LE' or false */ 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': @@ -300,6 +302,4 @@ class BitmapMetadataHandler { } } - - } diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php index 84672e05..b9e89d9d 100644 --- a/includes/media/DjVu.php +++ b/includes/media/DjVu.php @@ -183,9 +183,9 @@ class DjVuHandler extends ImageHandler { if ( $wgDjvuPostProcessor ) { $cmd .= " | {$wgDjvuPostProcessor}"; } - $cmd .= ' > ' . wfEscapeShellArg($dstPath) . ') 2>&1'; + $cmd .= ' > ' . wfEscapeShellArg( $dstPath ) . ') 2>&1'; wfProfileIn( 'ddjvu' ); - wfDebug( __METHOD__.": $cmd\n" ); + wfDebug( __METHOD__ . ": $cmd\n" ); $retval = ''; $err = wfShellExec( $cmd, $retval ); wfProfileOut( 'ddjvu' ); @@ -194,7 +194,7 @@ class DjVuHandler extends ImageHandler { if ( $retval != 0 || $removed ) { wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', - wfHostname(), $retval, trim($err), $cmd ) ); + wfHostname(), $retval, trim( $err ), $cmd ) ); return new MediaTransformError( 'thumbnail_error', $width, $height, $err ); } else { $params = array( @@ -228,7 +228,7 @@ class DjVuHandler extends ImageHandler { * @param $gettext Boolean: DOCUMENT (Default: false) * @return bool */ - function getMetaTree( $image , $gettext = false ) { + function getMetaTree( $image, $gettext = false ) { if ( isset( $image->dejaMetaTree ) ) { return $image->dejaMetaTree; } @@ -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'] ), @@ -322,13 +321,13 @@ class DjVuHandler extends ImageHandler { } } - function getPageText( $image, $page ){ + function getPageText( $image, $page ) { $tree = $this->getMetaTree( $image, true ); if ( !$tree ) { 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 6aef562b..54efe7a8 100644 --- a/includes/media/DjVuImage.php +++ b/includes/media/DjVuImage.php @@ -34,11 +34,21 @@ * @ingroup Media */ class DjVuImage { + /** + * Constructor + * + * @param string $filename The DjVu file name. + */ function __construct( $filename ) { $this->mFilename = $filename; } /** + * @const DJVUTXT_MEMORY_LIMIT Memory limit for the DjVu description software + */ + const DJVUTXT_MEMORY_LIMIT = 300000; + + /** * Check if the given file is indeed a valid DjVu image file * @return bool */ @@ -47,7 +57,6 @@ class DjVuImage { return $info !== false; } - /** * Return data in the style of getimagesize() * @return array or false on failure @@ -55,8 +64,8 @@ class DjVuImage { public function getImageSize() { $data = $this->getInfo(); - if( $data !== false ) { - $width = $data['width']; + if ( $data !== false ) { + $width = $data['width']; $height = $data['height']; return array( $width, $height, 'DjVu', @@ -84,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 ); } @@ -109,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; } @@ -117,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" ); } } @@ -141,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. @@ -153,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 ); } @@ -165,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 ); } @@ -180,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; @@ -188,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; } @@ -228,7 +237,7 @@ class DjVuImage { function retrieveMetaData() { global $wgDjvuToXML, $wgDjvuDump, $wgDjvuTxt; wfProfileIn( __METHOD__ ); - + if ( isset( $wgDjvuDump ) ) { # djvudump is faster as of version 3.5 # http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583 @@ -247,20 +256,20 @@ class DjVuImage { $xml = null; } # Text layer - if ( isset( $wgDjvuTxt ) ) { + if ( isset( $wgDjvuTxt ) ) { wfProfileIn( 'djvutxt' ); - $cmd = wfEscapeShellArg( $wgDjvuTxt ) . ' --detail=page ' . wfEscapeShellArg( $this->mFilename ) ; - wfDebug( __METHOD__.": $cmd\n" ); + $cmd = wfEscapeShellArg( $wgDjvuTxt ) . ' --detail=page ' . wfEscapeShellArg( $this->mFilename ); + wfDebug( __METHOD__ . ": $cmd\n" ); $retval = ''; - $txt = wfShellExec( $cmd, $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 = <<<EOR /\(page\s[\d-]*\s[\d-]*\s[\d-]*\s[\d-]*\s*" ((?> # Text to match is composed of atoms of either: - \\\\. # - any escaped character + \\\\. # - any escaped character | # - any character different from " and \ [^"\\\\]+ )*?) @@ -271,7 +280,7 @@ EOR; $txt = preg_replace_callback( $reg, array( $this, 'pageTextCallback' ), $txt ); $txt = "<DjVuTxt>\n<HEAD></HEAD>\n<BODY>\n" . $txt . "</BODY>\n</DjVuTxt>\n"; $xml = preg_replace( "/<DjVuXML>/", "<mw-djvu><DjVuXML>", $xml, 1 ); - $xml = $xml . $txt. '</mw-djvu>' ; + $xml = $xml . $txt . '</mw-djvu>'; } } wfProfileOut( __METHOD__ ); diff --git a/includes/media/Exif.php b/includes/media/Exif.php index 784a6018..9a2794a5 100644 --- a/includes/media/Exif.php +++ b/includes/media/Exif.php @@ -31,15 +31,16 @@ */ class Exif { - const BYTE = 1; //!< An 8-bit (1-byte) unsigned integer. - const ASCII = 2; //!< An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL. - 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 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. - const IGNORE = -1; // A fake value for things we don't want or don't support. + const BYTE = 1; //!< An 8-bit (1-byte) unsigned integer. + const ASCII = 2; //!< An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL. + 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. + const IGNORE = -1; // A fake value for things we don't want or don't support. //@{ /* @var array @@ -102,8 +103,9 @@ class Exif { /** * Constructor * - * @param $file String: filename. - * @param $byteOrder String Type of byte ordering either 'BE' (Big Endian) or 'LE' (Little Endian). Default ''. + * @param string $file filename. + * @param string $byteOrder Type of byte ordering either 'BE' (Big Endian) or 'LE' (Little Endian). Default ''. + * @throws MWException * @todo FIXME: The following are broke: * SubjectArea. Need to test the more obscure tags. * @@ -112,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. @@ -123,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) @@ -133,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 @@ -166,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 @@ -176,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 @@ -217,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 @@ -249,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 @@ -289,13 +291,13 @@ class Exif { // Only give a warning for b/c, since originally we didn't // require this. The number of things affected by this is // rather small. - wfWarn( 'Exif class did not have byte order specified. ' - . 'Some properties may be decoded incorrectly.' ); + wfWarn( 'Exif class did not have byte order specified. ' . + 'Some properties may be decoded incorrectly.' ); $this->byteOrder = 'BE'; // BE seems about twice as popular as LE in jpg's. } $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(); @@ -321,7 +323,7 @@ class Exif { foreach ( array_keys( $this->mRawExifData ) as $section ) { if ( !in_array( $section, array_keys( $this->mExifTags ) ) ) { - $this->debug( $section , __FUNCTION__, "'$section' is not a valid Exif section" ); + $this->debug( $section, __FUNCTION__, "'$section' is not a valid Exif section" ); continue; } @@ -345,24 +347,24 @@ class Exif { } /** - * Collapse some fields together. - * This converts some fields from exif form, to a more friendly form. - * For example GPS latitude to a single number. - * - * The rationale behind this is that we're storing data, not presenting to the user - * For example a longitude is a single number describing how far away you are from - * the prime meridian. Well it might be nice to split it up into minutes and seconds - * for the user, it doesn't really make sense to split a single number into 4 parts - * for storage. (degrees, minutes, second, direction vs single floating point number). - * - * Other things this might do (not really sure if they make sense or not): - * Dates -> mediawiki date format. - * convert values that can be in different units to be in one standardized unit. - * - * As an alternative approach, some of this could be done in the validate phase - * if we make up our own types like Exif::DATE. - */ - function collapseData( ) { + * Collapse some fields together. + * This converts some fields from exif form, to a more friendly form. + * For example GPS latitude to a single number. + * + * The rationale behind this is that we're storing data, not presenting to the user + * For example a longitude is a single number describing how far away you are from + * the prime meridian. Well it might be nice to split it up into minutes and seconds + * for the user, it doesn't really make sense to split a single number into 4 parts + * for storage. (degrees, minutes, second, direction vs single floating point number). + * + * Other things this might do (not really sure if they make sense or not): + * Dates -> mediawiki date format. + * convert values that can be in different units to be in one standardized unit. + * + * As an alternative approach, some of this could be done in the validate phase + * if we make up our own types like Exif::DATE. + */ + function collapseData() { $this->exifGPStoNumber( 'GPSLatitude' ); $this->exifGPStoNumber( 'GPSDestLatitude' ); @@ -386,22 +388,22 @@ class Exif { $this->exifPropToOrd( 'SceneType' ); $this->charCodeString( 'UserComment' ); - $this->charCodeString( 'GPSProcessingMethod'); + $this->charCodeString( 'GPSProcessingMethod' ); $this->charCodeString( 'GPSAreaInformation' ); - + //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++) { - $ccVals[$i] = ord( substr($val, $i, 1) ); + for ( $i = 0; $i < strlen( $val ); $i++ ) { + $ccVals[$i] = ord( substr( $val, $i, 1 ) ); } $ccVals['_type'] = 'ol'; //this is for formatting later. $this->mFilteredExifData['ComponentsConfiguration'] = $ccVals; } - + //GPSVersion(ID) is treated as the wrong type by php exif support. //Go through each byte turning it into a version string. //For example: "\x02\x02\x00\x00" -> "2.2.0.0" @@ -409,14 +411,14 @@ 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++) { + for ( $i = 0; $i < strlen( $val ); $i++ ) { if ( $i !== 0 ) { $newVal .= '.'; } - $newVal .= ord( substr($val, $i, 1) ); + $newVal .= ord( substr( $val, $i, 1 ) ); } if ( $this->byteOrder === 'LE' ) { // Need to reverse the string @@ -433,26 +435,25 @@ class Exif { } /** - * Do userComment tags and similar. See pg. 34 of exif standard. - * basically first 8 bytes is charset, rest is value. - * This has not been tested on any shift-JIS strings. - * @param $prop String prop name. - */ - private function charCodeString ( $prop ) { + * Do userComment tags and similar. See pg. 34 of exif standard. + * basically first 8 bytes is charset, rest is value. + * This has not been tested on any shift-JIS strings. + * @param string $prop prop name. + */ + private function charCodeString( $prop ) { if ( isset( $this->mFilteredExifData[$prop] ) ) { - if ( strlen($this->mFilteredExifData[$prop]) <= 8 ) { + if ( strlen( $this->mFilteredExifData[$prop] ) <= 8 ) { //invalid. Must be at least 9 bytes long. - $this->debug( $this->mFilteredExifData[$prop] , __FUNCTION__, false ); - unset($this->mFilteredExifData[$prop]); + $this->debug( $this->mFilteredExifData[$prop], __FUNCTION__, false ); + unset( $this->mFilteredExifData[$prop] ); return; } - $charCode = substr( $this->mFilteredExifData[$prop], 0, 8); - $val = substr( $this->mFilteredExifData[$prop], 8); - - - switch ($charCode) { + $charCode = substr( $this->mFilteredExifData[$prop], 0, 8 ); + $val = substr( $this->mFilteredExifData[$prop], 8 ); + + switch ( $charCode ) { case "\x4A\x49\x53\x00\x00\x00\x00\x00": //JIS $charset = "Shift-JIS"; @@ -466,9 +467,9 @@ class Exif { } // This could possibly check to see if iconv is really installed // or if we're using the compatibility wrapper in globalFunctions.php - if ($charset) { + if ( $charset ) { wfSuppressWarnings(); - $val = iconv($charset, 'UTF-8//IGNORE', $val); + $val = iconv( $charset, 'UTF-8//IGNORE', $val ); wfRestoreWarnings(); } else { // if valid utf-8, assume that, otherwise assume windows-1252 @@ -476,17 +477,17 @@ class Exif { UtfNormal::quickIsNFCVerify( $valCopy ); //validates $valCopy. if ( $valCopy !== $val ) { wfSuppressWarnings(); - $val = iconv('Windows-1252', 'UTF-8//IGNORE', $val); + $val = iconv( 'Windows-1252', 'UTF-8//IGNORE', $val ); wfRestoreWarnings(); } } - + //trim and check to make sure not only whitespace. - $val = trim($val); + $val = trim( $val ); if ( strlen( $val ) === 0 ) { //only whitespace. - $this->debug( $this->mFilteredExifData[$prop] , __FUNCTION__, "$prop: Is only whitespace" ); - unset($this->mFilteredExifData[$prop]); + $this->debug( $this->mFilteredExifData[$prop], __FUNCTION__, "$prop: Is only whitespace" ); + unset( $this->mFilteredExifData[$prop] ); return; } @@ -495,22 +496,22 @@ class Exif { } } /** - * Convert an Exif::UNDEFINED from a raw binary string - * to its value. This is sometimes needed depending on - * the type of UNDEFINED field - * @param $prop String name of property - */ - private function exifPropToOrd ( $prop ) { + * Convert an Exif::UNDEFINED from a raw binary string + * to its value. This is sometimes needed depending on + * the type of UNDEFINED field + * @param string $prop name of property + */ + private function exifPropToOrd( $prop ) { if ( isset( $this->mFilteredExifData[$prop] ) ) { $this->mFilteredExifData[$prop] = ord( $this->mFilteredExifData[$prop] ); } } /** - * Convert gps in exif form to a single floating point number - * for example 10 degress 20`40`` S -> -10.34444 - * @param String $prop a gps coordinate exif tag name (like GPSLongitude) - */ - private function exifGPStoNumber ( $prop ) { + * Convert gps in exif form to a single floating point number + * for example 10 degress 20`40`` S -> -10.34444 + * @param string $prop a gps coordinate exif tag name (like GPSLongitude) + */ + private function exifGPStoNumber( $prop ) { $loc =& $this->mFilteredExifData[$prop]; $dir =& $this->mFilteredExifData[$prop . 'Ref']; $res = false; @@ -545,7 +546,7 @@ class Exif { * * @deprecated since 1.18 */ - function makeFormattedData( ) { + function makeFormattedData() { wfDeprecated( __METHOD__, '1.18' ); $this->mFormattedExifData = FormatMetadata::getFormattedData( $this->mFilteredExifData ); @@ -580,7 +581,7 @@ class Exif { */ function getFormattedData() { wfDeprecated( __METHOD__, '1.18' ); - if (!$this->mFormattedExifData) { + if ( !$this->mFormattedExifData ) { $this->makeFormattedData(); } return $this->mFormattedExifData; @@ -612,7 +613,7 @@ class Exif { * @return bool */ private function isByte( $in ) { - if ( !is_array( $in ) && sprintf('%d', $in) == $in && $in >= 0 && $in <= 255 ) { + if ( !is_array( $in ) && sprintf( '%d', $in ) == $in && $in >= 0 && $in <= 255 ) { $this->debug( $in, __FUNCTION__, true ); return true; } else { @@ -648,7 +649,7 @@ class Exif { * @return bool */ private function isShort( $in ) { - if ( !is_array( $in ) && sprintf('%d', $in) == $in && $in >= 0 && $in <= 65536 ) { + if ( !is_array( $in ) && sprintf( '%d', $in ) == $in && $in >= 0 && $in <= 65536 ) { $this->debug( $in, __FUNCTION__, true ); return true; } else { @@ -662,7 +663,7 @@ class Exif { * @return bool */ private function isLong( $in ) { - if ( !is_array( $in ) && sprintf('%d', $in) == $in && $in >= 0 && $in <= 4294967296 ) { + if ( !is_array( $in ) && sprintf( '%d', $in ) == $in && $in >= 0 && $in <= 4294967296 ) { $this->debug( $in, __FUNCTION__, true ); return true; } else { @@ -677,7 +678,7 @@ class Exif { */ private function isRational( $in ) { $m = array(); - if ( !is_array( $in ) && @preg_match( '/^(\d+)\/(\d+[1-9]|[1-9]\d*)$/', $in, $m ) ) { # Avoid division by zero + if ( !is_array( $in ) && preg_match( '/^(\d+)\/(\d+[1-9]|[1-9]\d*)$/', $in, $m ) ) { # Avoid division by zero return $this->isLong( $m[1] ) && $this->isLong( $m[2] ); } else { $this->debug( $in, __FUNCTION__, 'fed a non-fraction value' ); @@ -727,8 +728,8 @@ class Exif { * Validates if a tag has a legal value according to the Exif spec * * @private - * @param $section String: section where tag is located. - * @param $tag String: the tag to check. + * @param string $section section where tag is located. + * @param string $tag the tag to check. * @param $val Mixed: the value of the tag. * @param $recursive Boolean: true if called recursively for array types. * @return bool @@ -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 ) ) { - return false; - } + 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,18 +810,18 @@ class Exif { } $type = gettype( $in ); $class = ucfirst( __CLASS__ ); - if ( $type === 'array' ) { + if ( is_array( $in ) ) { $in = print_r( $in, true ); } if ( $action === true ) { - wfDebugLog( $this->log, "$class::$fname: accepted: '$in' (type: $type)\n"); + wfDebugLog( $this->log, "$class::$fname: accepted: '$in' (type: $type)\n" ); } elseif ( $action === false ) { - wfDebugLog( $this->log, "$class::$fname: rejected: '$in' (type: $type)\n"); + wfDebugLog( $this->log, "$class::$fname: rejected: '$in' (type: $type)\n" ); } elseif ( $action === null ) { - wfDebugLog( $this->log, "$class::$fname: input was: '$in' (type: $type)\n"); + wfDebugLog( $this->log, "$class::$fname: input was: '$in' (type: $type)\n" ); } else { - wfDebugLog( $this->log, "$class::$fname: $action (type: $type; content: '$in')\n"); + wfDebugLog( $this->log, "$class::$fname: $action (type: $type; content: '$in')\n" ); } } @@ -828,7 +830,7 @@ class Exif { * * @private * - * @param $fname String: the name of the function calling this function + * @param string $fname the name of the function calling this function * @param $io Boolean: Specify whether we're beginning or ending */ private function debugFile( $fname, $io ) { @@ -843,4 +845,3 @@ class Exif { } } } - diff --git a/includes/media/ExifBitmap.php b/includes/media/ExifBitmap.php index 34a1f511..d8d0bede 100644 --- a/includes/media/ExifBitmap.php +++ b/includes/media/ExifBitmap.php @@ -34,8 +34,8 @@ class ExifBitmapHandler extends BitmapHandler { function convertMetadataVersion( $metadata, $version = 1 ) { // basically flattens arrays. - $version = explode(';', $version, 2); - $version = intval($version[0]); + $version = explode( ';', $version, 2 ); + $version = intval( $version[0] ); if ( $version < 1 || $version >= 2 ) { return $metadata; } @@ -51,12 +51,12 @@ 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,9 +84,9 @@ 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"); + wfDebug( __METHOD__ . ": back-compat version\n" ); return self::METADATA_COMPATIBLE; } if ( $metadata === self::BROKEN_FILE ) { @@ -102,11 +102,11 @@ class ExifBitmapHandler extends BitmapHandler { $exif['MEDIAWIKI_EXIF_VERSION'] == 1 ) { //back-compatible but old - wfDebug( __METHOD__.": back-compat version\n" ); + wfDebug( __METHOD__ . ": back-compat version\n" ); return self::METADATA_COMPATIBLE; } # Wrong (non-compatible) version - wfDebug( __METHOD__.": wrong version\n" ); + wfDebug( __METHOD__ . ": wrong version\n" ); return self::METADATA_BAD; } return self::METADATA_GOOD; @@ -163,7 +163,7 @@ class ExifBitmapHandler extends BitmapHandler { $rotation = 0; } - if ($rotation == 90 || $rotation == 270) { + if ( $rotation == 90 || $rotation == 270 ) { $width = $gis[0]; $gis[0] = $gis[1]; $gis[1] = $width; @@ -225,4 +225,3 @@ class ExifBitmapHandler extends BitmapHandler { return 0; } } - diff --git a/includes/media/FormatMetadata.php b/includes/media/FormatMetadata.php index 843c1fa2..1c5136f5 100644 --- a/includes/media/FormatMetadata.php +++ b/includes/media/FormatMetadata.php @@ -1,6 +1,6 @@ <?php /** - * Formating of image metadata values into human readable form. + * Formatting of image metadata values into human readable form. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ * @file */ - /** * Format Image metadata values into a human readable form. * @@ -53,7 +52,7 @@ class FormatMetadata { * value which most of the time are plain integers. This function * formats Exif (and other metadata) values into human readable form. * - * @param $tags Array: the Exif data to format ( as returned by + * @param array $tags the Exif data to format ( as returned by * Exif::getFilteredData() or BitmapMetadataHandler ) * @return array */ @@ -80,20 +79,20 @@ 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]); - $m = explode('/', $vals[1]); - $s = explode('/', $vals[2]); + $h = explode( '/', $vals[0] ); + $m = explode( '/', $vals[1] ); + $s = explode( '/', $vals[2] ); // this should already be validated // when loaded from file, but it could // come from a foreign repo, so be // paranoid. - if ( !isset($h[1]) - || !isset($m[1]) - || !isset($s[1]) + if ( !isset( $h[1] ) + || !isset( $m[1] ) + || !isset( $s[1] ) || $h[1] == 0 || $m[1] == 0 || $s[1] == 0 @@ -128,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: @@ -143,7 +142,7 @@ class FormatMetadata { break; case 'PhotometricInterpretation': - switch( $val ) { + switch ( $val ) { case 2: case 6: $val = self::msg( $tag, $val ); break; @@ -154,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; @@ -165,7 +164,7 @@ class FormatMetadata { break; case 'PlanarConfiguration': - switch( $val ) { + switch ( $val ) { case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -190,7 +189,7 @@ class FormatMetadata { case 'XResolution': case 'YResolution': - switch( $resolutionunit ) { + switch ( $resolutionunit ) { case 2: $val = self::msg( 'XYResolution', 'i', self::formatNum( $val ) ); break; @@ -209,7 +208,7 @@ class FormatMetadata { break; case 'ColorSpace': - switch( $val ) { + switch ( $val ) { case 1: case 65535: $val = self::msg( $tag, $val ); break; @@ -220,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; @@ -268,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; @@ -283,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; @@ -294,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: @@ -308,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(); @@ -322,14 +321,14 @@ class FormatMetadata { if ( $subTag != 'fired' && $subValue == 0 ) { continue; } - $fullTag = $tag . '-' . $subTag ; + $fullTag = $tag . '-' . $subTag; $flashMsgs[] = self::msg( $fullTag, $subValue ); } $val = $wgLang->commaList( $flashMsgs ); break; case 'FocalPlaneResolutionUnit': - switch( $val ) { + switch ( $val ) { case 2: $val = self::msg( $tag, $val ); break; @@ -340,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; @@ -351,7 +350,7 @@ class FormatMetadata { break; case 'FileSource': - switch( $val ) { + switch ( $val ) { case 3: $val = self::msg( $tag, $val ); break; @@ -362,7 +361,7 @@ class FormatMetadata { break; case 'SceneType': - switch( $val ) { + switch ( $val ) { case 1: $val = self::msg( $tag, $val ); break; @@ -373,7 +372,7 @@ class FormatMetadata { break; case 'CustomRendered': - switch( $val ) { + switch ( $val ) { case 0: case 1: $val = self::msg( $tag, $val ); break; @@ -384,7 +383,7 @@ class FormatMetadata { break; case 'ExposureMode': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -395,7 +394,7 @@ class FormatMetadata { break; case 'WhiteBalance': - switch( $val ) { + switch ( $val ) { case 0: case 1: $val = self::msg( $tag, $val ); break; @@ -406,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; @@ -417,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; @@ -428,7 +427,7 @@ class FormatMetadata { break; case 'Contrast': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -439,7 +438,7 @@ class FormatMetadata { break; case 'Saturation': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -450,7 +449,7 @@ class FormatMetadata { break; case 'Sharpness': - switch( $val ) { + switch ( $val ) { case 0: case 1: case 2: $val = self::msg( $tag, $val ); break; @@ -461,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; @@ -474,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; @@ -486,7 +485,7 @@ class FormatMetadata { case 'GPSLongitudeRef': case 'GPSDestLongitudeRef': - switch( $val ) { + switch ( $val ) { case 'E': case 'W': $val = self::msg( 'GPSLongitude', $val ); break; @@ -505,7 +504,7 @@ class FormatMetadata { break; case 'GPSStatus': - switch( $val ) { + switch ( $val ) { case 'A': case 'V': $val = self::msg( $tag, $val ); break; @@ -516,7 +515,7 @@ class FormatMetadata { break; case 'GPSMeasureMode': - switch( $val ) { + switch ( $val ) { case 2: case 3: $val = self::msg( $tag, $val ); break; @@ -526,11 +525,10 @@ class FormatMetadata { } break; - case 'GPSTrackRef': case 'GPSImgDirectionRef': case 'GPSDestBearingRef': - switch( $val ) { + switch ( $val ) { case 'T': case 'M': $val = self::msg( 'GPSDirection', $val ); break; @@ -550,7 +548,7 @@ class FormatMetadata { break; case 'GPSSpeedRef': - switch( $val ) { + switch ( $val ) { case 'K': case 'M': case 'N': $val = self::msg( 'GPSSpeed', $val ); break; @@ -561,7 +559,7 @@ class FormatMetadata { break; case 'GPSDestDistanceRef': - switch( $val ) { + switch ( $val ) { case 'K': case 'M': case 'N': $val = self::msg( 'GPSDestDistance', $val ); break; @@ -631,7 +629,7 @@ class FormatMetadata { case 'MaxApertureValue': if ( strpos( $val, '/' ) !== false ) { // need to expand this earlier to calculate fNumber - list($n, $d) = explode('/', $val); + list( $n, $d ) = explode( '/', $val ); if ( is_numeric( $n ) && is_numeric( $d ) ) { $val = $n / $d; } @@ -648,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': @@ -684,7 +682,7 @@ class FormatMetadata { $urgency = 'high'; } elseif ( $val == 5 ) { $urgency = 'normal'; - } elseif ( $val <= 8 && $val > 5) { + } elseif ( $val <= 8 && $val > 5 ) { $urgency = 'low'; } @@ -793,7 +791,7 @@ class FormatMetadata { } break; case 'Copyrighted': - switch( $val ) { + switch ( $val ) { case 'True': case 'False': $val = self::msg( $tag, $val ); break; @@ -809,7 +807,7 @@ class FormatMetadata { case 'LanguageCode': $lang = Language::fetchLanguageName( strtolower( $val ), $wgLang->getCode() ); - if ($lang) { + if ( $lang ) { $val = htmlspecialchars( $lang ); } else { $val = htmlspecialchars( $val ); @@ -829,20 +827,20 @@ class FormatMetadata { } /** - * A function to collapse multivalued tags into a single value. - * This turns an array of (for example) authors into a bulleted list. - * - * This is public on the basis it might be useful outside of this class. - * - * @param $vals Array array of values - * @param $type String Type of array (either lang, ul, ol). - * lang = language assoc array with keys being the lang code - * ul = unordered list, ol = ordered list - * type can also come from the '_type' member of $vals. - * @param $noHtml Boolean If to avoid returning anything resembling - * html. (Ugly hack for backwards compatibility with old mediawiki). - * @return String single value (in wiki-syntax). - */ + * A function to collapse multivalued tags into a single value. + * This turns an array of (for example) authors into a bulleted list. + * + * This is public on the basis it might be useful outside of this class. + * + * @param array $vals array of values + * @param string $type Type of array (either lang, ul, ol). + * lang = language assoc array with keys being the lang code + * ul = unordered list, ol = ordered list + * type can also come from the '_type' member of $vals. + * @param $noHtml Boolean If to avoid returning anything resembling + * html. (Ugly hack for backwards compatibility with old mediawiki). + * @return String single value (in wiki-syntax). + */ public static function flattenArray( $vals, $type = 'ul', $noHtml = false ) { if ( isset( $vals['_type'] ) ) { $type = $vals['_type']; @@ -850,13 +848,13 @@ class FormatMetadata { } if ( !is_array( $vals ) ) { - return $vals; // do nothing if not an array; + return $vals; // do nothing if not an array; } elseif ( count( $vals ) === 1 && $type !== 'lang' ) { 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 @@ -865,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 @@ -899,7 +897,7 @@ class FormatMetadata { } $content .= self::langItem( $vals[$cLang], $cLang, - $isDefault, $noHtml ); + $isDefault, $noHtml ); unset( $vals[$cLang] ); } @@ -915,8 +913,8 @@ class FormatMetadata { } if ( $defaultItem !== false ) { $content = self::langItem( $defaultItem, - $defaultLang, true, $noHtml ) - . $content; + $defaultLang, true, $noHtml ) . + $content; } if ( $noHtml ) { return $content; @@ -941,8 +939,8 @@ class FormatMetadata { /** Helper function for creating lists of translations. * - * @param $value String value (this is not escaped) - * @param $lang String lang code of item or false + * @param string $value value (this is not escaped) + * @param string $lang lang code of item or false * @param $default Boolean if it is default value. * @param $noHtml Boolean If to avoid html (for back-compat) * @throws MWException @@ -950,9 +948,9 @@ class FormatMetadata { * this is treated as wikitext not html). */ private static function langItem( $value, $lang, $default = false, $noHtml = false ) { - if ( $lang === false && $default === false) { - throw new MWException('$lang and $default cannot both ' - . 'be false.'); + if ( $lang === false && $default === false ) { + throw new MWException( '$lang and $default cannot both ' + . 'be false.' ); } if ( $noHtml ) { @@ -1008,17 +1006,18 @@ class FormatMetadata { * * @private * - * @param $tag String: the tag name to pass on - * @param $val String: the value of the tag - * @param $arg String: an argument to pass ($1) - * @param $arg2 String: a 2nd argument to pass ($2) + * @param string $tag the tag name to pass on + * @param string $val the value of the tag + * @param string $arg an argument to pass ($1) + * @param string $arg2 a 2nd argument to pass ($2) * @return string A wfMessage of "exif-$tag-$val" in lower case */ 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(); } @@ -1033,10 +1032,10 @@ 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 ) { - $out[] = self::formatNum($number); + foreach ( $num as $number ) { + $out[] = self::formatNum( $number ); } return $wgLang->commaList( $out ); } @@ -1073,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 ); } @@ -1098,7 +1097,7 @@ class FormatMetadata { else return gcd( $b, $a % $b ); */ - while( $b != 0 ) { + while ( $b != 0 ) { $remainder = $a % $b; // tail recursion... @@ -1117,16 +1116,16 @@ class FormatMetadata { * Note, leading 0's are significant, so this is * a string, not an int. * - * @param $val String: The 8 digit news code. + * @param string $val The 8 digit news code. * @return string The human readable form */ - static private function convertNewsCode( $val ) { + private static function convertNewsCode( $val ) { if ( !preg_match( '/^\d{8}$/D', $val ) ) { // Not a valid news code. return $val; } $cat = ''; - switch( substr( $val , 0, 2 ) ) { + switch ( substr( $val, 0, 2 ) ) { case '01': $cat = 'ace'; break; @@ -1190,8 +1189,8 @@ class FormatMetadata { * Format a coordinate value, convert numbers from floating point * into degree minute second representation. * - * @param $coord int degrees, minutes and seconds - * @param $type String: latitude or longitude (for if its a NWS or E) + * @param int $coord degrees, minutes and seconds + * @param string $type latitude or longitude (for if its a NWS or E) * @return mixed A floating point number or whatever we were fed */ static function formatCoords( $coord, $type ) { @@ -1226,7 +1225,7 @@ class FormatMetadata { /** * Format the contact info field into a single value. * - * @param $vals Array array with fields of the ContactInfo + * @param array $vals array with fields of the ContactInfo * struct defined in the IPTC4XMP spec. Or potentially * an array with one element that is a free form text * value from the older iptc iim 1:118 prop. @@ -1238,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'] ) @@ -1256,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 ); @@ -1353,7 +1352,7 @@ class FormatMetadata { * * @deprecated since 1.18 * -**/ + */ class FormatExif { var $meta; @@ -1361,7 +1360,7 @@ class FormatExif { * @param $meta array */ function FormatExif( $meta ) { - wfDeprecated(__METHOD__); + wfDeprecated( __METHOD__, '1.18' ); $this->meta = $meta; } diff --git a/includes/media/GIF.php b/includes/media/GIF.php index 84b9b8ca..608fb257 100644 --- a/includes/media/GIF.php +++ b/includes/media/GIF.php @@ -29,17 +29,17 @@ class GIFHandler extends BitmapHandler { const BROKEN_FILE = '0'; // value to store in img_metadata if error extracting metadata. - + 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; } - return serialize($parsedGIFMetadata); + return serialize( $parsedGIFMetadata ); } /** @@ -53,7 +53,7 @@ class GIFHandler extends BitmapHandler { return false; } $meta = unserialize( $meta ); - if ( !isset( $meta['metadata'] ) || count( $meta['metadata'] ) <= 1 ) { + if ( !isset( $meta['metadata'] ) || count( $meta['metadata'] ) <= 1 ) { return false; } @@ -85,8 +85,8 @@ class GIFHandler extends BitmapHandler { function isAnimatedImage( $image ) { $ser = $image->getMetadata(); if ( $ser ) { - $metadata = unserialize($ser); - if( $metadata['frameCount'] > 1 ) { + $metadata = unserialize( $ser ); + 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; @@ -141,29 +141,29 @@ class GIFHandler extends BitmapHandler { $original = parent::getLongDesc( $image ); wfSuppressWarnings(); - $metadata = unserialize($image->getMetadata()); + $metadata = unserialize( $image->getMetadata() ); wfRestoreWarnings(); - - if (!$metadata || $metadata['frameCount'] <= 1) { + + if ( !$metadata || $metadata['frameCount'] <= 1 ) { return $original; } /* Preserve original image info string, but strip the last char ')' so we can add even more */ $info = array(); $info[] = $original; - + if ( $metadata['looped'] ) { $info[] = wfMessage( 'file-info-gif-looped' )->parse(); } - + if ( $metadata['frameCount'] > 1 ) { $info[] = wfMessage( 'file-info-gif-frames' )->numParams( $metadata['frameCount'] )->parse(); } - + if ( $metadata['duration'] ) { $info[] = $wgLang->formatTimePeriod( $metadata['duration'] ); } - + return $wgLang->commaList( $info ); } } diff --git a/includes/media/GIFMetadataExtractor.php b/includes/media/GIFMetadataExtractor.php index 5fc5c1a7..887afa3f 100644 --- a/includes/media/GIFMetadataExtractor.php +++ b/includes/media/GIFMetadataExtractor.php @@ -49,16 +49,16 @@ class GIFMetadataExtractor { * @return array */ static function getMetadata( $filename ) { - self::$gif_frame_sep = pack( "C", ord("," ) ); - self::$gif_extension_sep = pack( "C", ord("!" ) ); - self::$gif_term = pack( "C", ord(";" ) ); + self::$gif_frame_sep = pack( "C", ord( "," ) ); + self::$gif_extension_sep = pack( "C", ord( "!" ) ); + self::$gif_term = pack( "C", ord( ";" ) ); $frameCount = 0; $duration = 0.0; $isLooped = false; $xmp = ""; $comment = array(); - + if ( !$filename ) { throw new Exception( "No file name specified" ); } elseif ( !file_exists( $filename ) || is_dir( $filename ) ) { @@ -73,7 +73,7 @@ class GIFMetadataExtractor { // Check for the GIF header $buf = fread( $fh, 6 ); - if ( !($buf == 'GIF87a' || $buf == 'GIF89a') ) { + if ( !( $buf == 'GIF87a' || $buf == 'GIF89a' ) ) { throw new Exception( "Not a valid GIF file; header: $buf" ); } @@ -90,10 +90,10 @@ 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) { + if ( $buf == self::$gif_frame_sep ) { // Found a frame $frameCount++; @@ -107,21 +107,25 @@ class GIFMetadataExtractor { ## Read GCT self::readGCT( $fh, $bpp ); fread( $fh, 1 ); - self::skipBlock( $fh ); + 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]; - if ($extension_code == 0xF9) { + if ( $extension_code == 0xF9 ) { // Graphics Control Extension. fread( $fh, 1 ); // Block size 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,13 +133,15 @@ 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 ) { + if ( $term != 0 ) { throw new Exception( "Malformed Graphics Control Extension block" ); } - } elseif ($extension_code == 0xFE) { + } elseif ( $extension_code == 0xFE ) { // Comment block(s). $data = self::readBlock( $fh ); if ( $data === "" ) { @@ -157,48 +163,51 @@ 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 // is identical to the last, only extract once. $comment[] = $data; } - } elseif ($extension_code == 0xFF) { + } elseif ( $extension_code == 0xFF ) { // 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 ); + if ( $blockLength != 11 ) { + wfDebug( __METHOD__ . " GIF application block with wrong length\n" ); + fseek( $fh, -( $blockLength + 1 ), SEEK_CUR ); self::skipBlock( $fh ); continue; } // NETSCAPE2.0 (application name for animated gif) if ( $data == 'NETSCAPE2.0' ) { - $data = fread( $fh, 2 ); // Block length and introduction, should be 03 01 - if ($data != "\x03\x01") { + if ( $data != "\x03\x01" ) { throw new Exception( "Expected \x03\x01, got $data" ); } - + // 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]; - - if ($loopCount != 1) { + + if ( $loopCount != 1 ) { $isLooped = true; } - + // Read out terminator byte fread( $fh, 1 ); } elseif ( $data == 'XMP DataXMP' ) { @@ -219,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; } @@ -229,10 +238,12 @@ 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 ); + throw new Exception( "At position: " . ftell( $fh ) . ", Unknown byte " . $byte ); } } @@ -252,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 ); } } @@ -260,10 +271,13 @@ class GIFMetadataExtractor { /** * @param $data + * @throws Exception * @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; @@ -276,20 +290,23 @@ class GIFMetadataExtractor { /** * @param $fh - * @return + * @throws Exception */ 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) { + if ( $block_len == 0 ) { return; } fread( $fh, $block_len ); } } + /** * Read a block. In the GIF format, a block is made up of * several sub-blocks. Each sub block starts with one byte @@ -301,6 +318,7 @@ class GIFMetadataExtractor { * sub-blocks in the returned value. Normally this is false, * except XMP is weird and does a hack where you need to keep * these length bytes. + * @throws Exception * @return string The data. */ static function readBlock( $fh, $includeLengths = false ) { @@ -308,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 8fd3552f..544dd211 100644 --- a/includes/media/IPTC.php +++ b/includes/media/IPTC.php @@ -29,27 +29,27 @@ class IPTC { /** - * This takes the results of iptcparse() and puts it into a - * form that can be handled by mediawiki. Generally called from - * BitmapMetadataHandler::doApp13. - * - * @see http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf - * - * @param $rawData String app13 block from jpeg containing iptc/iim data - * @return Array iptc metadata array - */ + * This takes the results of iptcparse() and puts it into a + * form that can be handled by mediawiki. Generally called from + * BitmapMetadataHandler::doApp13. + * + * @see http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf + * + * @param string $rawData app13 block from jpeg containing iptc/iim data + * @return Array iptc metadata array + */ static function parse( $rawData ) { $parsed = iptcparse( $rawData ); $data = Array(); - if (!is_array($parsed)) { + if ( !is_array( $parsed ) ) { return $data; } $c = ''; //charset info contained in tag 1:90. - if (isset($parsed['1#090']) && isset($parsed['1#090'][0])) { - $c = self::getCharset($parsed['1#090'][0]); - if ($c === false) { + if ( isset( $parsed['1#090'] ) && isset( $parsed['1#090'][0] ) ) { + $c = self::getCharset( $parsed['1#090'][0] ); + if ( $c === false ) { //Unknown charset. refuse to parse. //note: There is a different between //unknown and no charset specified. @@ -59,11 +59,11 @@ class IPTC { } foreach ( $parsed as $tag => $val ) { - if ( isset( $val[0] ) && trim($val[0]) == '' ) { - wfDebugLog('iptc', "IPTC tag $tag had only whitespace as its value."); + if ( isset( $val[0] ) && trim( $val[0] ) == '' ) { + 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; @@ -175,7 +175,7 @@ class IPTC { if ( isset( $parsed['2#070'] ) ) { //if a version is set for the software. $softwareVersion = self::convIPTC( $parsed['2#070'], $c ); - unset($parsed['2#070']); + unset( $parsed['2#070'] ); $data['Software'] = array( array( $software[0], $softwareVersion[0] ) ); } else { $data['Software'] = $software; @@ -198,7 +198,7 @@ class IPTC { /* original transmission ref. * "A code representing the location of original transmission ac- * cording to practises of the provider." - */ + */ $data['OriginalTransmissionRef'] = self::convIPTC( $val, $c ); break; case '2#118': /*contact*/ @@ -227,8 +227,8 @@ class IPTC { } else { $time = Array(); } - $timestamp = self::timeHelper( $val, $time, $c ); - if ($timestamp) { + $timestamp = self::timeHelper( $val, $time, $c ); + if ( $timestamp ) { $data['DateTimeOriginal'] = $timestamp; } break; @@ -241,8 +241,8 @@ class IPTC { } else { $time = Array(); } - $timestamp = self::timeHelper( $val, $time, $c ); - if ($timestamp) { + $timestamp = self::timeHelper( $val, $time, $c ); + if ( $timestamp ) { $data['DateTimeDigitized'] = $timestamp; } break; @@ -254,8 +254,8 @@ class IPTC { } else { $time = Array(); } - $timestamp = self::timeHelper( $val, $time, $c ); - if ($timestamp) { + $timestamp = self::timeHelper( $val, $time, $c ); + if ( $timestamp ) { $data['DateTimeReleased'] = $timestamp; } break; @@ -267,8 +267,8 @@ class IPTC { } else { $time = Array(); } - $timestamp = self::timeHelper( $val, $time, $c ); - if ($timestamp) { + $timestamp = self::timeHelper( $val, $time, $c ); + if ( $timestamp ) { $data['DateTimeExpires'] = $timestamp; } break; @@ -313,7 +313,7 @@ class IPTC { // describing the subject matter of the content. $codes = self::convIPTC( $val, $c ); foreach ( $codes as $ic ) { - $fields = explode(':', $ic, 3 ); + $fields = explode( ':', $ic, 3 ); if ( count( $fields ) < 2 || $fields[0] !== 'IPTC' ) @@ -350,43 +350,43 @@ class IPTC { } /** - * Convert an iptc date and time tags into the exif format - * - * @todo Potentially this should also capture the timezone offset. - * @param Array $date The date tag - * @param Array $time The time tag - * @param $c - * @return String Date in exif format. - */ + * Convert an iptc date and time tags into the exif format + * + * @todo Potentially this should also capture the timezone offset. + * @param array $date The date tag + * @param array $time The time tag + * @param $c + * @return String Date in exif format. + */ private static function timeHelper( $date, $time, $c ) { if ( count( $date ) === 1 ) { //the standard says this should always be 1 //just double checking. - list($date) = self::convIPTC( $date, $c ); + list( $date ) = self::convIPTC( $date, $c ); } else { return null; } if ( count( $time ) === 1 ) { - list($time) = self::convIPTC( $time, $c ); + list( $time ) = self::convIPTC( $time, $c ); $dateOnly = false; } else { $time = '000000+0000'; //placeholder $dateOnly = true; } - if ( ! ( preg_match('/\d\d\d\d\d\d[-+]\d\d\d\d/', $time) - && preg_match('/\d\d\d\d\d\d\d\d/', $date) - && substr($date, 0, 4) !== '0000' - && substr($date, 4, 2) !== '00' - && substr($date, 6, 2) !== '00' - ) ) { + if ( !( preg_match( '/\d\d\d\d\d\d[-+]\d\d\d\d/', $time ) + && preg_match( '/\d\d\d\d\d\d\d\d/', $date ) + && substr( $date, 0, 4 ) !== '0000' + && substr( $date, 4, 2 ) !== '00' + && substr( $date, 6, 2 ) !== '00' + ) ) { //something wrong. // Note, this rejects some valid dates according to iptc spec // for example: the date 00000400 means the photo was taken in // April, but the year and day is unknown. We don't process these // types of incomplete dates atm. - wfDebugLog( 'iptc', "IPTC: invalid time ( $time ) or date ( $date )"); + wfDebugLog( 'iptc', "IPTC: invalid time ( $time ) or date ( $date )" ); return null; } @@ -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 ) === '-' ) { @@ -417,15 +417,15 @@ class IPTC { } /** - * Helper function to convert charset for iptc values. - * @param $data string|array The iptc string - * @param $charset String: The charset + * Helper function to convert charset for iptc values. + * @param string|array $data The iptc string + * @param string $charset The charset * * @return string|array - */ - private static function convIPTC ( $data, $charset ) { + */ + private static function convIPTC( $data, $charset ) { if ( is_array( $data ) ) { - foreach ($data as &$val) { + foreach ( $data as &$val ) { $val = self::convIPTCHelper( $val, $charset ); } } else { @@ -435,27 +435,27 @@ class IPTC { return $data; } /** - * Helper function of a helper function to convert charset for iptc values. - * @param $data Mixed String or Array: The iptc string - * @param $charset String: The charset - * - * @return string - */ - private static function convIPTCHelper ( $data, $charset ) { + * Helper function of a helper function to convert charset for iptc values. + * @param $data Mixed String or Array: The iptc string + * @param string $charset The charset + * + * @return string + */ + private static function convIPTCHelper( $data, $charset ) { if ( $charset ) { wfSuppressWarnings(); - $data = iconv($charset, "UTF-8//IGNORE", $data); + $data = iconv( $charset, "UTF-8//IGNORE", $data ); wfRestoreWarnings(); - if ($data === false) { + if ( $data === false ) { $data = ""; - wfDebugLog('iptc', __METHOD__ . " Error converting iptc data charset $charset to utf-8"); + wfDebugLog( 'iptc', __METHOD__ . " Error converting iptc data charset $charset to utf-8" ); } } else { //treat as utf-8 if is valid utf-8. otherwise pretend its windows-1252 // most of the time if there is no 1:90 tag, it is either ascii, latin1, or utf-8 $oldData = $data; UtfNormal::quickIsNFCVerify( $data ); //make $data valid utf-8 - if ($data === $oldData) { + if ( $data === $oldData ) { return $data; //if validation didn't change $data } else { return self::convIPTCHelper( $oldData, 'Windows-1252' ); @@ -465,14 +465,14 @@ class IPTC { } /** - * take the value of 1:90 tag and returns a charset - * @param String $tag 1:90 tag. - * @return string charset name or "?" - * Warning, this function does not (and is not intended to) detect - * all iso 2022 escape codes. In practise, the code for utf-8 is the - * only code that seems to have wide use. It does detect that code. - */ - static function getCharset($tag) { + * take the value of 1:90 tag and returns a charset + * @param string $tag 1:90 tag. + * @return string charset name or "?" + * Warning, this function does not (and is not intended to) detect + * all iso 2022 escape codes. In practise, the code for utf-8 is the + * only code that seems to have wide use. It does detect that code. + */ + static function getCharset( $tag ) { //According to iim standard, charset is defined by the tag 1:90. //in which there are iso 2022 escape sequences to specify the character set. @@ -530,7 +530,7 @@ class IPTC { case "\x1b(K": $c = "ISO646-DE"; break; - case "\x1b(N": //crylic + case "\x1b(N": //crylic $c = "ISO_5427"; break; case "\x1b(`": //iso646-NO @@ -590,7 +590,7 @@ class IPTC { $c = 'CSN_369103'; break; default: - wfDebugLog('iptc', __METHOD__ . 'Unknown charset in iptc 1:90: ' . bin2hex( $tag ) ); + wfDebugLog( 'iptc', __METHOD__ . 'Unknown charset in iptc 1:90: ' . bin2hex( $tag ) ); //at this point just give up and refuse to parse iptc? $c = false; } diff --git a/includes/media/ImageHandler.php b/includes/media/ImageHandler.php index 61759074..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(); } @@ -139,7 +139,6 @@ abstract class ImageHandler extends MediaHandler { $params['height'] = $params['physicalHeight']; } - if ( !$this->validateThumbParams( $params['physicalWidth'], $params['physicalHeight'], $srcWidth, $srcHeight, $mimeType ) ) { return false; @@ -161,12 +160,12 @@ abstract class ImageHandler extends MediaHandler { $width = intval( $width ); # Sanity check $width - if( $width <= 0) { - wfDebug( __METHOD__.": Invalid destination width: $width\n" ); + if ( $width <= 0 ) { + wfDebug( __METHOD__ . ": Invalid destination width: $width\n" ); return false; } if ( $srcWidth <= 0 ) { - wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" ); + wfDebug( __METHOD__ . ": Invalid source width: $srcWidth\n" ); return false; } @@ -188,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 ); } } @@ -201,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 a15b6524..fa763668 100644 --- a/includes/media/Jpeg.php +++ b/includes/media/Jpeg.php @@ -32,12 +32,12 @@ */ class JpegHandler extends ExifBitmapHandler { - function getMetadata ( $image, $filename ) { + function getMetadata( $image, $filename ) { try { $meta = BitmapMetadataHandler::Jpeg( $filename ); if ( !is_array( $meta ) ) { // This should never happen, but doesn't hurt to be paranoid. - throw new MWException('Metadata array is not an array'); + throw new MWException( 'Metadata array is not an array' ); } $meta['MEDIAWIKI_EXIF_VERSION'] = Exif::version(); return serialize( $meta ); @@ -59,5 +59,36 @@ class JpegHandler extends ExifBitmapHandler { } } -} + /** + * @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 $wgJpegTran; + + $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360; + if ( $wgJpegTran && is_file( $wgJpegTran ) ) { + $cmd = wfEscapeShellArg( $wgJpegTran ) . + " -rotate " . wfEscapeShellArg( $rotation ) . + " -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 ); + } + return false; + } else { + return parent::rotate( $file, $params ); + } + } + +} diff --git a/includes/media/JpegMetadataExtractor.php b/includes/media/JpegMetadataExtractor.php index 8d7e43b9..c7030eba 100644 --- a/includes/media/JpegMetadataExtractor.php +++ b/includes/media/JpegMetadataExtractor.php @@ -25,7 +25,7 @@ * Class for reading jpegs and extracting metadata. * see also BitmapMetadataHandler. * - * Based somewhat on GIFMetadataExtrator. + * Based somewhat on GIFMetadataExtractor. * * @ingroup Media */ @@ -37,17 +37,17 @@ class JpegMetadataExtractor { // that many segments. Your average file has about 10. /** Function to extract metadata segments of interest from jpeg files - * based on GIFMetadataExtractor. - * - * we can almost use getimagesize to do this - * but gis doesn't support having multiple app1 segments - * and those can't extract xmp on files containing both exif and xmp data - * - * @param String $filename name of jpeg file - * @return Array of interesting segments. - * @throws MWException if given invalid file. - */ - static function segmentSplitter ( $filename ) { + * based on GIFMetadataExtractor. + * + * we can almost use getimagesize to do this + * but gis doesn't support having multiple app1 segments + * and those can't extract xmp on files containing both exif and xmp data + * + * @param string $filename name of jpeg file + * @return Array of interesting segments. + * @throws MWException if given invalid file. + */ + 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" ) { @@ -129,7 +129,7 @@ class JpegMetadataExtractor { // whatever... $segments["XMP"] = substr( $temp, 29 ); wfDebug( __METHOD__ . ' Found XMP section with wrong app identifier ' - . "Using anyways.\n" ); + . "Using anyways.\n" ); } elseif ( substr( $temp, 0, 6 ) === "Exif\0\0" ) { // Just need to find out what the byte order is. // because php's exif plugin sucks... @@ -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 ); } @@ -165,10 +167,11 @@ class JpegMetadataExtractor { } /** - * Helper function for jpegSegmentSplitter - * @param &$fh FileHandle for jpeg file - * @return string data content of segment. - */ + * Helper function for jpegSegmentSplitter + * @param &$fh FileHandle for jpeg file + * @throws MWException + * @return string data content of segment. + */ private static function jpegExtractMarker( &$fh ) { $size = wfUnpack( "nint", fread( $fh, 2 ), 2 ); if ( $size['int'] <= 2 ) { @@ -182,19 +185,19 @@ class JpegMetadataExtractor { } /** - * This reads the photoshop image resource. - * Currently it only compares the iptc/iim hash - * with the stored hash, which is used to determine the precedence - * of the iptc data. In future it may extract some other info, like - * url of copyright license. - * - * This should generally be called by BitmapMetadataHandler::doApp13() - * - * @param String $app13 photoshop psir app13 block from jpg. - * @throws MWException (It gets caught next level up though) - * @return String if the iptc hash is good or not. - */ - public static function doPSIR ( $app13 ) { + * This reads the photoshop image resource. + * Currently it only compares the iptc/iim hash + * with the stored hash, which is used to determine the precedence + * of the iptc data. In future it may extract some other info, like + * url of copyright license. + * + * This should generally be called by BitmapMetadataHandler::doApp13() + * + * @param string $app13 photoshop psir app13 block from jpg. + * @throws MWException (It gets caught next level up though) + * @return String if the iptc hash is good or not. + */ + public static function doPSIR( $app13 ) { if ( !$app13 ) { throw new MWException( "No App13 segment given" ); } @@ -242,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; @@ -266,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 965099fd..779e23c9 100644 --- a/includes/media/MediaHandler.php +++ b/includes/media/MediaHandler.php @@ -46,7 +46,7 @@ abstract class MediaHandler { static function getHandler( $type ) { global $wgMediaHandlers; if ( !isset( $wgMediaHandlers[$type] ) ) { - wfDebug( __METHOD__ . ": no handler found for $type.\n"); + wfDebug( __METHOD__ . ": no handler found for $type.\n" ); return false; } $class = $wgMediaHandlers[$type]; @@ -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 ); @@ -103,7 +105,7 @@ abstract class MediaHandler { * can't be determined. * * @param $image File: the image object, or false if there isn't one - * @param $path String: the filename + * @param string $path the filename * @return Array Follow the format of PHP getimagesize() internal function. See http://www.php.net/getimagesize */ abstract function getImageSize( $image, $path ); @@ -113,42 +115,44 @@ abstract class MediaHandler { * * @param $image File: the image object, or false if there isn't one. * Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!) - * @param $path String: the filename + * @param string $path the filename * @return String */ - function getMetadata( $image, $path ) { return ''; } - - /** - * Get metadata version. - * - * This is not used for validating metadata, this is used for the api when returning - * metadata, since api content formats should stay the same over time, and so things - * using ForiegnApiRepo can keep backwards compatibility - * - * All core media handlers share a common version number, and extensions can - * use the GetMetadataVersion hook to append to the array (they should append a unique - * string so not to get confusing). If there was a media handler named 'foo' with metadata - * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata - * version is 2, the end version string would look like '2;foo=3'. - * - * @return string version string - */ - static function getMetadataVersion () { + function getMetadata( $image, $path ) { + return ''; + } + + /** + * Get metadata version. + * + * This is not used for validating metadata, this is used for the api when returning + * metadata, since api content formats should stay the same over time, and so things + * using ForiegnApiRepo can keep backwards compatibility + * + * All core media handlers share a common version number, and extensions can + * use the GetMetadataVersion hook to append to the array (they should append a unique + * string so not to get confusing). If there was a media handler named 'foo' with metadata + * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata + * version is 2, the end version string would look like '2;foo=3'. + * + * @return string version string + */ + static function getMetadataVersion() { $version = Array( '2' ); // core metadata version - wfRunHooks('GetMetadataVersion', Array(&$version)); - return implode( ';', $version); - } - - /** - * Convert metadata version. - * - * By default just returns $metadata, but can be used to allow - * media handlers to convert between metadata versions. - * - * @param $metadata Mixed String or Array metadata array (serialized if string) - * @param $version Integer target version - * @return Array serialized metadata in specified version, or $metadata on fail. - */ + wfRunHooks( 'GetMetadataVersion', Array( &$version ) ); + return implode( ';', $version ); + } + + /** + * Convert metadata version. + * + * By default just returns $metadata, but can be used to allow + * media handlers to convert between metadata versions. + * + * @param $metadata Mixed String or Array metadata array (serialized if string) + * @param $version Integer target version + * @return Array serialized metadata in specified version, or $metadata on fail. + */ function convertMetadataVersion( $metadata, $version = 1 ) { if ( !is_array( $metadata ) ) { @@ -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. @@ -181,7 +187,6 @@ abstract class MediaHandler { return self::METADATA_GOOD; } - /** * Get a MediaTransformOutput object representing an alternate of the transformed * output which will call an intermediary thumbnail assist script. @@ -200,9 +205,9 @@ abstract class MediaHandler { * actually do the transform. * * @param $image File: the image object - * @param $dstPath String: filesystem destination path - * @param $dstUrl String: Destination URL to use in output HTML - * @param $params Array: Arbitrary set of parameters validated by $this->validateParam() + * @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() * @return MediaTransformOutput */ final function getTransform( $image, $dstPath, $dstUrl, $params ) { @@ -214,9 +219,10 @@ abstract class MediaHandler { * transform unless $flags contains self::TRANSFORM_LATER. * * @param $image File: the image object - * @param $dstPath String: filesystem destination path - * @param $dstUrl String: destination URL to use in output HTML - * @param $params Array: arbitrary set of parameters validated by $this->validateParam() + * @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 @@ -225,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 ) { @@ -244,66 +254,108 @@ abstract class MediaHandler { } /** + * Get useful response headers for GET/HEAD requests for a file with the given metadata + * @param $metadata mixed Result of the getMetadata() function of this handler for a file + * @return Array + */ + public function getStreamHeaders( $metadata ) { + return array(); + } + + /** * 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; + } } /** @@ -352,11 +404,11 @@ abstract class MediaHandler { * * This is used by the media handlers that use the FormatMetadata class * - * @param $metadataArray Array metadata array + * @param array $metadataArray metadata array * @return array for use displaying metadata. */ function formatMetadataHelper( $metadataArray ) { - $result = array( + $result = array( 'visible' => array(), 'collapsed' => array() ); @@ -386,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]; } } @@ -396,7 +448,6 @@ abstract class MediaHandler { return $fields; } - /** * This is used to generate an array element for each metadata value * That array is then used to generate the table of metadata values @@ -405,17 +456,17 @@ abstract class MediaHandler { * @param &$array Array An array containing elements for each type of visibility * and each of those elements being an array of metadata items. This function adds * a value to that array. - * @param $visibility string ('visible' or 'collapsed') if this value is hidden + * @param string $visibility ('visible' or 'collapsed') if this value is hidden * by default. - * @param $type String type of metadata tag (currently always 'exif') - * @param $id String the name of the metadata tag (like 'artist' for example). + * @param string $type type of metadata tag (currently always 'exif') + * @param string $id the name of the metadata tag (like 'artist' for example). * its name in the table displayed is the message "$type-$id" (Ex exif-artist ). - * @param $value String thingy goes into a wikitext table; it used to be escaped but + * @param string $value thingy goes into a wikitext table; it used to be escaped but * that was incompatible with previous practise of customized display * with wikitext formatting via messages such as 'exif-model-value'. * So the escaping is taken back out, but generally this seems a confusing * interface. - * @param $param String value to pass to the message for the name of the field + * @param string $param value to pass to the message for the name of the field * as $1. Currently this parameter doesn't seem to ever be used. * * Note, everything here is passed through the parser later on (!) @@ -441,6 +492,8 @@ abstract class MediaHandler { } /** + * Used instead of getLongDesc if there is no handler registered for file. + * * @param $file File * @return string */ @@ -450,6 +503,8 @@ abstract class MediaHandler { } /** + * Short description. Shown on Special:Search results. + * * @param $file File * @return string */ @@ -460,6 +515,8 @@ abstract class MediaHandler { } /** + * Long description. Shown under image on image description page surounded by (). + * * @param $file File * @return string */ @@ -469,6 +526,8 @@ abstract class MediaHandler { } /** + * Used instead of getShortDesc if there is no handler registered for file. + * * @param $file File * @return string */ @@ -489,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 ) {} @@ -512,7 +584,7 @@ abstract class MediaHandler { * match the handler class, a Status object should be returned containing * relevant errors. * - * @param $fileName string The local path to the file. + * @param string $fileName The local path to the file. * @return Status object */ function verifyUpload( $fileName ) { @@ -523,14 +595,14 @@ abstract class MediaHandler { * Check for zero-sized thumbnails. These can be generated when * no disk space is available or some other error occurs * - * @param $dstPath string The location of the suspect file - * @param $retval int Return value of some shell process, file will be deleted if this is non-zero + * @param string $dstPath The location of the suspect file + * @param int $retval Return value of some shell process, file will be deleted if this is non-zero * @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 ) { @@ -549,12 +621,47 @@ 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 } + + /* + * True if the handler can rotate the media + * @since 1.21 + * @return bool + */ + 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 773824cb..c49d3f20 100644 --- a/includes/media/MediaTransformOutput.php +++ b/includes/media/MediaTransformOutput.php @@ -32,7 +32,14 @@ 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 + * from pixel density (eg 1.5 or 2) to additional URLs. + */ + public $responsiveUrls = array(); + protected $storagePath = false; /** @@ -73,7 +80,7 @@ abstract class MediaTransformOutput { } /** - * @param $storagePath string The permanent storage path + * @param string $storagePath The permanent storage path * @return void */ public function setStoragePath( $storagePath ) { @@ -83,7 +90,7 @@ abstract class MediaTransformOutput { /** * Fetch HTML for this transform output * - * @param $options array Associative array of options. Boolean options + * @param array $options Associative array of options. Boolean options * should be indicated with a value of true for true, and false or * absent for false. * @@ -144,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 } @@ -153,7 +165,7 @@ abstract class MediaTransformOutput { /** * Stream the file if there were no errors * - * @param $headers Array Additional HTTP headers to send on success + * @param array $headers Additional HTTP headers to send on success * @return Bool success */ public function streamFile( $headers = array() ) { @@ -185,14 +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 = $this->page ? ( 'page=' . urlencode( $this->page ) ) : ''; - if( $params ) { - $query .= $query ? '&'.$params : $params; + public function getDescLinkAttribs( $title = null, $params = array() ) { + if ( is_array( $params ) ) { + $query = $params; + } else { + $query = array(); + } + if ( $this->page && $this->page !== 1 ) { + $query['page'] = $this->page; + } + 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', @@ -218,19 +242,21 @@ class ThumbnailImage extends MediaTransformOutput { * It may also include a 'page' parameter for multipage files. * * @param $file File object - * @param $url String: URL path to the thumb + * @param string $url URL path to the thumb * @param $path String|bool|null: filesystem path to the thumb - * @param $parameters Array: Associative array of parameters + * @param array $parameters Associative array of parameters * @private */ function __construct( $file, $url, $path = false, $parameters = array() ) { # 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. @@ -239,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; } @@ -254,13 +280,14 @@ class ThumbnailImage extends MediaTransformOutput { $this->height = round( $actualParams['height'] ); $this->page = $actualParams['page']; + $this->lang = $actualParams['lang']; } /** * Return HTML <img ... /> tag for the thumbnail, will include * width and height attributes and a blank alt text (as required). * - * @param $options array Associative array of options. Boolean options + * @param array $options Associative array of options. Boolean options * should be indicated with a value of true for true, and false or * absent for false. * @@ -271,6 +298,8 @@ class ThumbnailImage extends MediaTransformOutput { * valign vertical-align property, if the output is an inline element * img-class Class applied to the \<img\> 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 @@ -281,16 +310,17 @@ class ThumbnailImage extends MediaTransformOutput { * For images, desc-link and file-link are implemented as a click-through. For * sounds and videos, they may be displayed in other ways. * + * @throws MWException * @return string */ 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']; - $query = empty( $options['desc-query'] ) ? '' : $options['desc-query']; + $query = empty( $options['desc-query'] ) ? '' : $options['desc-query']; if ( !empty( $options['custom-url-link'] ) ) { $linkAttribs = array( 'href' => $options['custom-url-link'] ); @@ -323,7 +353,7 @@ class ThumbnailImage extends MediaTransformOutput { 'alt' => $alt, 'src' => $this->url, 'width' => $this->width, - 'height' => $this->height, + 'height' => $this->height ); if ( !empty( $options['valign'] ) ) { $attribs['style'] = "vertical-align: {$options['valign']}"; @@ -331,6 +361,20 @@ 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 ) ) { + $attribs['srcset'] = Html::srcSet( $this->responsiveUrls ); + } + + wfRunHooks( 'ThumbnailBeforeProduceHTML', array( $this, &$attribs, &$linkAttribs ) ); + return $this->linkWrap( $linkAttribs, Xml::element( 'img', $attribs ) ); } @@ -385,7 +429,7 @@ class MediaTransformError extends MediaTransformOutput { class TransformParameterError extends MediaTransformError { function __construct( $params ) { parent::__construct( 'thumbnail_error', - max( isset( $params['width'] ) ? $params['width'] : 0, 120 ), + max( isset( $params['width'] ) ? $params['width'] : 0, 120 ), max( isset( $params['height'] ) ? $params['height'] : 0, 120 ), wfMessage( 'thumbnail_invalid_params' )->text() ); } diff --git a/includes/media/PNG.php b/includes/media/PNG.php index 1b329e57..98f13861 100644 --- a/includes/media/PNG.php +++ b/includes/media/PNG.php @@ -38,13 +38,13 @@ 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; } - return serialize($metadata); + return serialize( $metadata ); } /** @@ -74,9 +74,11 @@ class PNGHandler extends BitmapHandler { */ function isAnimatedImage( $image ) { $ser = $image->getMetadata(); - if ($ser) { - $metadata = unserialize($ser); - if( $metadata['frameCount'] > 1 ) return true; + if ( $ser ) { + $metadata = unserialize( $ser ); + if ( $metadata['frameCount'] > 1 ) { + return true; + } } return false; } @@ -88,11 +90,11 @@ class PNGHandler extends BitmapHandler { function canAnimateThumbnail( $image ) { return false; } - + function getMetadataType( $image ) { return 'parsed-png'; } - + function isMetadataValid( $image, $metadata ) { if ( $metadata === self::BROKEN_FILE ) { @@ -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; @@ -126,29 +128,30 @@ class PNGHandler extends BitmapHandler { $original = parent::getLongDesc( $image ); wfSuppressWarnings(); - $metadata = unserialize($image->getMetadata()); + $metadata = unserialize( $image->getMetadata() ); wfRestoreWarnings(); - if( !$metadata || $metadata['frameCount'] <= 0 ) + if ( !$metadata || $metadata['frameCount'] <= 0 ) { return $original; + } $info = array(); $info[] = $original; - + if ( $metadata['loopCount'] == 0 ) { $info[] = wfMessage( 'file-info-png-looped' )->parse(); } elseif ( $metadata['loopCount'] > 1 ) { $info[] = wfMessage( 'file-info-png-repeat' )->numParams( $metadata['loopCount'] )->parse(); } - + if ( $metadata['frameCount'] > 0 ) { $info[] = wfMessage( 'file-info-png-frames' )->numParams( $metadata['frameCount'] )->parse(); } - + if ( $metadata['duration'] ) { $info[] = $wgLang->formatTimePeriod( $metadata['duration'] ); } - + return $wgLang->commaList( $info ); } diff --git a/includes/media/PNGMetadataExtractor.php b/includes/media/PNGMetadataExtractor.php index 9dcde406..845d212a 100644 --- a/includes/media/PNGMetadataExtractor.php +++ b/includes/media/PNGMetadataExtractor.php @@ -124,7 +124,7 @@ class PNGMetadataExtractor { case 0: $colorType = 'greyscale'; break; - case 2: + case 2: $colorType = 'truecolour'; break; case 3: @@ -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' ) { @@ -417,7 +417,7 @@ class PNGMetadataExtractor { * @throws Exception if too big. * @return String The chunk. */ - static private function read( $fh, $size ) { + private static function read( $fh, $size ) { if ( $size > self::MAX_CHUNK_SIZE ) { throw new Exception( __METHOD__ . ': Chunk size of ' . $size . ' too big. Max size is: ' . self::MAX_CHUNK_SIZE ); diff --git a/includes/media/SVG.php b/includes/media/SVG.php index 55fa5547..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,19 +115,26 @@ 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 ); } + $metadata = $this->unpackMetadata( $image->getMetadata() ); + if ( isset( $metadata['error'] ) ) { // sanity check + $err = wfMessage( 'svg-long-error', $metadata['error']['message'] )->text(); + return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err ); + } + if ( !wfMkdirParents( dirname( $dstPath ), null, __METHOD__ ) ) { return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, wfMessage( 'thumbnail_dest_directory' )->text() ); } $srcPath = $image->getLocalRefPath(); - $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight ); - if( $status === true ) { + $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight, $lang ); + if ( $status === true ) { return new ThumbnailImage( $image, $dstUrl, $dstPath, $params ); } else { return $status; // MediaTransformError @@ -135,15 +142,17 @@ class SvgHandler extends ImageHandler { } /** - * Transform an SVG file to PNG - * This function can be called outside of thumbnail contexts - * @param string $srcPath - * @param string $dstPath - * @param string $width - * @param string $height - * @return bool|MediaTransformError - */ - public function rasterize( $srcPath, $dstPath, $width, $height ) { + * Transform an SVG file to PNG + * This function can be called outside of thumbnail contexts + * @param string $srcPath + * @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, $lang = false ) { global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath; $err = false; $retval = ''; @@ -151,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" ); @@ -163,22 +172,28 @@ class SvgHandler extends ImageHandler { $cmd = str_replace( array( '$path/', '$width', '$height', '$input', '$output' ), array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "", - intval( $width ), - intval( $height ), - wfEscapeShellArg( $srcPath ), - wfEscapeShellArg( $dstPath ) ), + intval( $width ), + intval( $height ), + 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 ); + wfDebug( __METHOD__ . ": $cmd\n" ); + $err = wfShellExecWithStderr( $cmd, $retval, $env ); wfProfileOut( 'rsvg' ); } } $removed = $this->removeBadFile( $dstPath, $retval ); if ( $retval != 0 || $removed ) { wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', - wfHostname(), $retval, trim($err), $cmd ) ); + wfHostname(), $retval, trim( $err ), $cmd ) ); return new MediaTransformError( 'thumbnail_error', $width, $height, $err ); } return true; @@ -213,6 +228,8 @@ class SvgHandler extends ImageHandler { if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) { return array( $metadata['width'], $metadata['height'], 'SVG', "width=\"{$metadata['width']}\" height=\"{$metadata['height']}\"" ); + } else { // error + return array( 0, 0, 'SVG', "width=\"0\" height=\"0\"" ); } } @@ -231,6 +248,12 @@ class SvgHandler extends ImageHandler { */ function getLongDesc( $file ) { global $wgLang; + + $metadata = $this->unpackMetadata( $file->getMetadata() ); + if ( isset( $metadata['error'] ) ) { + return wfMessage( 'svg-long-error', $metadata['error']['message'] )->text(); + } + $size = $wgLang->formatSize( $file->getSize() ); if ( $this->isAnimatedImage( $file ) ) { @@ -239,23 +262,23 @@ class SvgHandler extends ImageHandler { $msg = wfMessage( 'svg-long-desc' ); } - $msg->numParams( - $file->getWidth(), - $file->getHeight() - ); - $msg->Params( $size ); + $msg->numParams( $file->getWidth(), $file->getHeight() )->params( $size ); + return $msg->parse(); } function getMetadata( $file, $filename ) { + $metadata = array( 'version' => self::SVG_METADATA_VERSION ); try { - $metadata = SVGMetadataExtractor::getMetadata( $filename ); - } catch( Exception $e ) { - // Broken file? + $metadata += SVGMetadataExtractor::getMetadata( $filename ); + } catch ( MWException $e ) { // @todo SVG specific exceptions + // File not found, broken, etc. + $metadata['error'] = array( + 'message' => $e->getMessage(), + 'code' => $e->getCode() + ); wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); - return '0'; } - $metadata['version'] = self::SVG_METADATA_VERSION; return serialize( $metadata ); } @@ -305,7 +328,7 @@ class SvgHandler extends ImageHandler { return false; } $metadata = $this->unpackMetadata( $metadata ); - if ( !$metadata ) { + if ( !$metadata || isset( $metadata['error'] ) ) { return false; } @@ -325,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] ) ) { @@ -333,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', @@ -340,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 c6f63fd4..2e33bb98 100644 --- a/includes/media/SVGMetadataExtractor.php +++ b/includes/media/SVGMetadataExtractor.php @@ -51,7 +51,8 @@ class SVGReader { * Constructor * * Creates an SVGReader drawing from the source provided - * @param $source String: URI from which to read + * @param string $source URI from which to read + * @throws MWException|Exception */ function __construct( $source ) { global $wgSVGMetadataCutoff; @@ -66,7 +67,7 @@ class SVGReader { if ( $size > $wgSVGMetadataCutoff ) { $this->debug( "SVG is $size bytes, which is bigger than $wgSVGMetadataCutoff. Truncating." ); $contents = file_get_contents( $source, false, null, -1, $wgSVGMetadataCutoff ); - if ($contents === false) { + if ( $contents === false ) { throw new MWException( 'Error reading SVG file.' ); } $this->reader->XML( $contents, null, LIBXML_NOERROR | LIBXML_NOWARNING ); @@ -100,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(); @@ -120,29 +121,30 @@ class SVGReader { /** * Read the SVG + * @throws MWException * @return bool */ protected function read() { $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 <svg> tag, got ". + throw new MWException( "Expected <svg> tag, got " . $this->reader->localName . " in NS " . $this->reader->namespaceURI ); } $this->debug( "<svg> tag is correct." ); $this->handleSVGAttribs(); - $exitDepth = $this->reader->depth; + $exitDepth = $this->reader->depth; $keepReading = $this->reader->read(); 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" ); @@ -180,19 +182,19 @@ class SVGReader { /** * Read a textelement from an element * - * @param String $name of the element that we are reading from - * @param String $metafield that we will fill with the result + * @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(); @@ -202,15 +204,16 @@ class SVGReader { /** * Read an XML snippet from an element * - * @param String $metafield that we will fill with the result + * @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)." ); @@ -221,24 +224,24 @@ class SVGReader { /** * Filter all children, looking for animate elements * - * @param String $name of the element that we are reading from + * @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 ) { return; } - $exitDepth = $this->reader->depth; + $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 // <script>, but its possible @@ -264,7 +267,7 @@ class SVGReader { } private function debug( $data ) { - if( $this->mDebug ) { + if ( $this->mDebug ) { wfDebug( "SVGReader: $data\n" ); } } @@ -282,44 +285,44 @@ class SVGReader { * * The parser has to be in the start element of "<svg>" */ - private function handleSVGAttribs( ) { + private function handleSVGAttribs() { $defaultWidth = self::DEFAULT_WIDTH; $defaultHeight = self::DEFAULT_HEIGHT; $aspect = 1.0; $width = null; $height = null; - if( $this->reader->getAttribute('viewBox') ) { + if ( $this->reader->getAttribute( 'viewBox' ) ) { // min-x min-y width height - $viewBox = preg_split( '/\s+/', trim( $this->reader->getAttribute('viewBox') ) ); - if( count( $viewBox ) == 4 ) { + $viewBox = preg_split( '/\s+/', trim( $this->reader->getAttribute( 'viewBox' ) ) ); + if ( count( $viewBox ) == 4 ) { $viewWidth = $this->scaleSVGUnit( $viewBox[2] ); $viewHeight = $this->scaleSVGUnit( $viewBox[3] ); - if( $viewWidth > 0 && $viewHeight > 0 ) { + if ( $viewWidth > 0 && $viewHeight > 0 ) { $aspect = $viewWidth / $viewHeight; $defaultHeight = $defaultWidth / $aspect; } } } - if( $this->reader->getAttribute('width') ) { - $width = $this->scaleSVGUnit( $this->reader->getAttribute('width'), $defaultWidth ); + if ( $this->reader->getAttribute( 'width' ) ) { + $width = $this->scaleSVGUnit( $this->reader->getAttribute( 'width' ), $defaultWidth ); $this->metadata['originalWidth'] = $this->reader->getAttribute( 'width' ); } - if( $this->reader->getAttribute('height') ) { - $height = $this->scaleSVGUnit( $this->reader->getAttribute('height'), $defaultHeight ); + if ( $this->reader->getAttribute( 'height' ) ) { + $height = $this->scaleSVGUnit( $this->reader->getAttribute( 'height' ), $defaultHeight ); $this->metadata['originalHeight'] = $this->reader->getAttribute( 'height' ); } - if( !isset( $width ) && !isset( $height ) ) { + if ( !isset( $width ) && !isset( $height ) ) { $width = $defaultWidth; $height = $width / $aspect; - } elseif( isset( $width ) && !isset( $height ) ) { + } elseif ( isset( $width ) && !isset( $height ) ) { $height = $width / $aspect; - } elseif( isset( $height ) && !isset( $width ) ) { + } elseif ( isset( $height ) && !isset( $width ) ) { $width = $height * $aspect; } - if( $width > 0 && $height > 0 ) { + if ( $width > 0 && $height > 0 ) { $this->metadata['width'] = intval( round( $width ) ); $this->metadata['height'] = intval( round( $height ) ); } @@ -329,11 +332,11 @@ class SVGReader { * Return a rounded pixel equivalent for a labeled CSS/SVG length. * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers * - * @param $length String: CSS/SVG length. + * @param string $length CSS/SVG length. * @param $viewportSize: Float optional scale for percentage units... * @return float: length in pixels */ - static function scaleSVGUnit( $length, $viewportSize=512 ) { + static function scaleSVGUnit( $length, $viewportSize = 512 ) { static $unitLength = array( 'px' => 1.0, 'pt' => 1.25, @@ -343,13 +346,13 @@ class SVGReader { 'in' => 90.0, 'em' => 16.0, // fake it? 'ex' => 12.0, // fake it? - '' => 1.0, // "User units" pixels by default + '' => 1.0, // "User units" pixels by default ); $matches = array(); - if( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) { + if ( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) { $length = floatval( $matches[1] ); $unit = $matches[2]; - if( $unit == '%' ) { + if ( $unit == '%' ) { return $length * 0.01 * $viewportSize; } else { return $length * $unitLength[$unit]; diff --git a/includes/media/Tiff.php b/includes/media/Tiff.php index d95c9074..55acb120 100644 --- a/includes/media/Tiff.php +++ b/includes/media/Tiff.php @@ -43,7 +43,7 @@ class TiffHandler extends ExifBitmapHandler { function canRender( $file ) { global $wgTiffThumbnailType; return (bool)$wgTiffThumbnailType - || ($file->getRepo() instanceof ForeignAPIRepo); + || $file->getRepo() instanceof ForeignAPIRepo; } /** @@ -70,8 +70,9 @@ class TiffHandler extends ExifBitmapHandler { } /** - * @param $image - * @param $filename + * @param File $image + * @param string $filename + * @throws MWException * @return string */ function getMetadata( $image, $filename ) { @@ -81,7 +82,7 @@ class TiffHandler extends ExifBitmapHandler { $meta = BitmapMetadataHandler::Tiff( $filename ); if ( !is_array( $meta ) ) { // This should never happen, but doesn't hurt to be paranoid. - throw new MWException('Metadata array is not an array'); + throw new MWException( 'Metadata array is not an array' ); } $meta['MEDIAWIKI_EXIF_VERSION'] = Exif::version(); return serialize( $meta ); diff --git a/includes/media/XCF.php b/includes/media/XCF.php index 555fa1fb..e77d3842 100644 --- a/includes/media/XCF.php +++ b/includes/media/XCF.php @@ -72,19 +72,19 @@ class XCFHandler extends BitmapHandler { * @author Hexmode * @author Hashar * - * @param $filename String Full path to a XCF file + * @param string $filename Full path to a XCF file * @return bool|array metadata array just like PHP getimagesize() */ static function getXCFMetaData( $filename ) { # Decode master structure $f = fopen( $filename, 'rb' ); - if( !$f ) { + if ( !$f ) { return false; } # The image structure always starts at offset 0 in the XCF file. # So we just read it :-) $binaryHeader = fread( $f, 26 ); - fclose($f); + fclose( $f ); # Master image structure: # @@ -110,12 +110,12 @@ class XCFHandler extends BitmapHandler { . "/Nbase_type" # / , $binaryHeader ); - } catch( MWException $mwe ) { + } catch ( MWException $mwe ) { return false; } # Check values - if( $header['magic'] !== 'gimp xcf' ) { + if ( $header['magic'] !== 'gimp xcf' ) { wfDebug( __METHOD__ . " '$filename' has invalid magic signature.\n" ); return false; } diff --git a/includes/media/XMP.php b/includes/media/XMP.php index 36660b3d..7eb3d19e 100644 --- a/includes/media/XMP.php +++ b/includes/media/XMP.php @@ -22,30 +22,30 @@ */ /** -* Class for reading xmp data containing properties relevant to -* images, and spitting out an array that FormatExif accepts. -* -* Note, this is not meant to recognize every possible thing you can -* encode in XMP. It should recognize all the properties we want. -* For example it doesn't have support for structures with multiple -* nesting levels, as none of the properties we're supporting use that -* feature. If it comes across properties it doesn't recognize, it should -* ignore them. -* -* The public methods one would call in this class are -* - parse( $content ) -* Reads in xmp content. -* Can potentially be called multiple times with partial data each time. -* - parseExtended( $content ) -* Reads XMPExtended blocks (jpeg files only). -* - getResults -* Outputs a results array. -* -* Note XMP kind of looks like rdf. They are not the same thing - XMP is -* encoded as a specific subset of rdf. This class can read XMP. It cannot -* read rdf. -* -*/ + * Class for reading xmp data containing properties relevant to + * images, and spitting out an array that FormatExif accepts. + * + * Note, this is not meant to recognize every possible thing you can + * encode in XMP. It should recognize all the properties we want. + * For example it doesn't have support for structures with multiple + * nesting levels, as none of the properties we're supporting use that + * feature. If it comes across properties it doesn't recognize, it should + * ignore them. + * + * The public methods one would call in this class are + * - parse( $content ) + * Reads in xmp content. + * Can potentially be called multiple times with partial data each time. + * - parseExtended( $content ) + * Reads XMPExtended blocks (jpeg files only). + * - getResults + * Outputs a results array. + * + * Note XMP kind of looks like rdf. They are not the same thing - XMP is + * encoded as a specific subset of rdf. This class can read XMP. It cannot + * read rdf. + * + */ class XMPReader { private $curItem = array(); // array to hold the current element (and previous element, and so on) @@ -63,39 +63,38 @@ class XMPReader { protected $items; /** - * These are various mode constants. - * they are used to figure out what to do - * with an element when its encountered. - * - * For example, MODE_IGNORE is used when processing - * a property we're not interested in. So if a new - * element pops up when we're in that mode, we ignore it. - */ + * These are various mode constants. + * they are used to figure out what to do + * with an element when its encountered. + * + * For example, MODE_IGNORE is used when processing + * a property we're not interested in. So if a new + * element pops up when we're in that mode, we ignore it. + */ const MODE_INITIAL = 0; - const MODE_IGNORE = 1; - const MODE_LI = 2; + const MODE_IGNORE = 1; + const MODE_LI = 2; const MODE_LI_LANG = 3; - const MODE_QDESC = 4; + const MODE_QDESC = 4; // The following MODE constants are also used in the // $items array to denote what type of property the item is. - const MODE_SIMPLE = 10; - const MODE_STRUCT = 11; // structure (associative array) - const MODE_SEQ = 12; // ordered list - const MODE_BAG = 13; // unordered list - const MODE_LANG = 14; - const MODE_ALT = 15; // non-language alt. Currently not implemented, and not needed atm. + const MODE_SIMPLE = 10; + const MODE_STRUCT = 11; // structure (associative array) + const MODE_SEQ = 12; // ordered list + const MODE_BAG = 13; // unordered list + const MODE_LANG = 14; + const MODE_ALT = 15; // non-language alt. Currently not implemented, and not needed atm. const MODE_BAGSTRUCT = 16; // A BAG of Structs. const NS_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; const NS_XML = 'http://www.w3.org/XML/1998/namespace'; - /** - * Constructor. - * - * Primary job is to initialize the XMLParser - */ + * Constructor. + * + * Primary job is to initialize the XMLParser + */ function __construct() { if ( !function_exists( 'xml_parser_create_ns' ) ) { @@ -109,12 +108,12 @@ class XMPReader { } /** - * Main use is if a single item has multiple xmp documents describing it. - * For example in jpeg's with extendedXMP - */ + * Main use is if a single item has multiple xmp documents describing it. + * For example in jpeg's with extendedXMP + */ private function resetXMLParser() { - if ($this->xmlParser) { + if ( $this->xmlParser ) { //is this needed? xml_parser_free( $this->xmlParser ); } @@ -131,20 +130,20 @@ class XMPReader { } /** Destroy the xml parser - * - * Not sure if this is actually needed. - */ + * + * Not sure if this is actually needed. + */ function __destruct() { // not sure if this is needed. xml_parser_free( $this->xmlParser ); } /** Get the result array. Do some post-processing before returning - * the array, and transform any metadata that is special-cased. - * - * @return Array array of results as an array of arrays suitable for - * FormatMetadata::getFormattedData(). - */ + * the array, and transform any metadata that is special-cased. + * + * @return Array array of results as an array of arrays suitable for + * FormatMetadata::getFormattedData(). + */ public function getResults() { // xmp-special is for metadata that affects how stuff // is extracted. For example xmpNote:HasExtendedXMP. @@ -156,7 +155,7 @@ class XMPReader { $data = $this->results; - wfRunHooks('XMPGetResults', Array(&$data)); + wfRunHooks( 'XMPGetResults', Array( &$data ) ); if ( isset( $data['xmp-special']['AuthorsPosition'] ) && is_string( $data['xmp-special']['AuthorsPosition'] ) @@ -181,12 +180,12 @@ class XMPReader { ) { // the is_array is just paranoia. It should always // be an array. - foreach( $data['xmp-special']['LocationShown'] as $loc ) { + foreach ( $data['xmp-special']['LocationShown'] as $loc ) { if ( !is_array( $loc ) ) { // To avoid copying over the _type meta-fields. continue; } - foreach( $loc as $field => $val ) { + foreach ( $loc as $field => $val ) { $data['xmp-general'][$field . 'Dest'][] = $val; } } @@ -196,18 +195,17 @@ class XMPReader { ) { // the is_array is just paranoia. It should always // be an array. - foreach( $data['xmp-special']['LocationCreated'] as $loc ) { + foreach ( $data['xmp-special']['LocationCreated'] as $loc ) { if ( !is_array( $loc ) ) { // To avoid copying over the _type meta-fields. continue; } - foreach( $loc as $field => $val ) { + foreach ( $loc as $field => $val ) { $data['xmp-general'][$field . 'Created'][] = $val; } } } - // We don't want to return the special values, since they're // special and not info to be stored about the file. unset( $data['xmp-special'] ); @@ -232,17 +230,18 @@ class XMPReader { } /** - * Main function to call to parse XMP. Use getResults to - * get results. - * - * Also catches any errors during processing, writes them to - * debug log, blanks result array and returns false. - * - * @param $content String: XMP data - * @param $allOfIt Boolean: If this is all the data (true) or if its split up (false). Default true - * @param $reset Boolean: does xml parser need to be reset. Default false - * @return Boolean success. - */ + * Main function to call to parse XMP. Use getResults to + * get results. + * + * Also catches any errors during processing, writes them to + * debug log, blanks result array and returns false. + * + * @param string $content XMP data + * @param $allOfIt Boolean: If this is all the data (true) or if its split up (false). Default true + * @param $reset Boolean: does xml parser need to be reset. Default false + * @throws MWException + * @return Boolean success. + */ public function parse( $content, $allOfIt = true, $reset = false ) { if ( $reset ) { $this->resetXMLParser(); @@ -254,7 +253,7 @@ class XMPReader { if ( !$this->charset ) { $bom = array(); if ( preg_match( '/\xEF\xBB\xBF|\xFE\xFF|\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\xFF\xFE/', - $content, $bom ) + $content, $bom ) ) { switch ( $bom[0] ) { case "\xFE\xFF": @@ -274,11 +273,8 @@ class XMPReader { break; default: //this should be impossible to get to - throw new MWException("Invalid BOM"); - break; - + throw new MWException( "Invalid BOM" ); } - } else { // standard specifically says, if no bom assume utf-8 $this->charset = 'UTF-8'; @@ -314,7 +310,7 @@ class XMPReader { * * @todo In serious need of testing * @see http://www.adobe.ge/devnet/xmp/pdfs/XMPSpecificationPart3.pdf XMP spec part 3 page 20 - * @param String $content XMPExtended block minus the namespace signature + * @param string $content XMPExtended block minus the namespace signature * @return Boolean If it succeeded. */ public function parseExtended( $content ) { @@ -323,17 +319,16 @@ class XMPReader { $guid = substr( $content, 0, 32 ); if ( !isset( $this->results['xmp-special']['HasExtendedXMP'] ) || $this->results['xmp-special']['HasExtendedXMP'] !== $guid ) { - wfDebugLog('XMP', __METHOD__ . " Ignoring XMPExtended block due to wrong guid (guid= '$guid' )"); + wfDebugLog( 'XMP', __METHOD__ . " Ignoring XMPExtended block due to wrong guid (guid= '$guid')" ); return false; } - $len = unpack( 'Nlength/Noffset', substr( $content, 32, 8 ) ); + $len = unpack( 'Nlength/Noffset', substr( $content, 32, 8 ) ); - if (!$len || $len['length'] < 4 || $len['offset'] < 0 || $len['offset'] > $len['length'] ) { - wfDebugLog('XMP', __METHOD__ . 'Error reading extended XMP block, invalid length or offset.'); + if ( !$len || $len['length'] < 4 || $len['offset'] < 0 || $len['offset'] > $len['length'] ) { + wfDebugLog( 'XMP', __METHOD__ . 'Error reading extended XMP block, invalid length or offset.' ); return false; } - // we're not very robust here. we should accept it in the wrong order. To quote // the xmp standard: // "A JPEG writer should write the ExtendedXMP marker segments in order, immediately following the @@ -344,8 +339,8 @@ class XMPReader { // so the probability that it will have > 128k, and be in the wrong order is very low... if ( $len['offset'] !== $this->extendedXMPOffset ) { - wfDebugLog('XMP', __METHOD__ . 'Ignoring XMPExtended block due to wrong order. (Offset was ' - . $len['offset'] . ' but expected ' . $this->extendedXMPOffset . ')'); + wfDebugLog( 'XMP', __METHOD__ . 'Ignoring XMPExtended block due to wrong order. (Offset was ' + . $len['offset'] . ' but expected ' . $this->extendedXMPOffset . ')' ); return false; } @@ -365,26 +360,26 @@ class XMPReader { $atEnd = false; } - wfDebugLog('XMP', __METHOD__ . 'Parsing a XMPExtended block'); + wfDebugLog( 'XMP', __METHOD__ . 'Parsing a XMPExtended block' ); return $this->parse( $actualContent, $atEnd ); } /** - * Character data handler - * Called whenever character data is found in the xmp document. - * - * does nothing if we're in MODE_IGNORE or if the data is whitespace - * throws an error if we're not in MODE_SIMPLE (as we're not allowed to have character - * data in the other modes). - * - * As an example, this happens when we encounter XMP like: - * <exif:DigitalZoomRatio>0/10</exif:DigitalZoomRatio> - * and are processing the 0/10 bit. - * - * @param $parser XMLParser reference to the xml parser - * @param $data String Character data - * @throws MWException on invalid data - */ + * Character data handler + * Called whenever character data is found in the xmp document. + * + * does nothing if we're in MODE_IGNORE or if the data is whitespace + * throws an error if we're not in MODE_SIMPLE (as we're not allowed to have character + * data in the other modes). + * + * As an example, this happens when we encounter XMP like: + * <exif:DigitalZoomRatio>0/10</exif:DigitalZoomRatio> + * and are processing the 0/10 bit. + * + * @param $parser XMLParser reference to the xml parser + * @param string $data Character data + * @throws MWException on invalid data + */ function char( $parser, $data ) { $data = trim( $data ); @@ -396,7 +391,9 @@ class XMPReader { throw new MWException( 'Unexpected character data before first rdf:Description element' ); } - if ( $this->mode[0] === self::MODE_IGNORE ) return; + if ( $this->mode[0] === self::MODE_IGNORE ) { + return; + } if ( $this->mode[0] !== self::MODE_SIMPLE && $this->mode[0] !== self::MODE_QDESC @@ -414,37 +411,34 @@ class XMPReader { } /** When we hit a closing element in MODE_IGNORE - * Check to see if this is the element we started to ignore, - * in which case we get out of MODE_IGNORE - * - * @param $elm String Namespace of element followed by a space and then tag name of element. - */ - private function endElementModeIgnore ( $elm ) { - + * Check to see if this is the element we started to ignore, + * in which case we get out of MODE_IGNORE + * + * @param string $elm Namespace of element followed by a space and then tag name of element. + */ + private function endElementModeIgnore( $elm ) { if ( $this->curItem[0] === $elm ) { array_shift( $this->curItem ); array_shift( $this->mode ); } - return; - } /** - * Hit a closing element when in MODE_SIMPLE. - * This generally means that we finished processing a - * property value, and now have to save the result to the - * results array - * - * For example, when processing: - * <exif:DigitalZoomRatio>0/10</exif:DigitalZoomRatio> - * this deals with when we hit </exif:DigitalZoomRatio>. - * - * Or it could be if we hit the end element of a property - * of a compound data structure (like a member of an array). - * - * @param $elm String namespace, space, and tag name. - */ - private function endElementModeSimple ( $elm ) { + * Hit a closing element when in MODE_SIMPLE. + * This generally means that we finished processing a + * property value, and now have to save the result to the + * results array + * + * For example, when processing: + * <exif:DigitalZoomRatio>0/10</exif:DigitalZoomRatio> + * this deals with when we hit </exif:DigitalZoomRatio>. + * + * Or it could be if we hit the end element of a property + * of a compound data structure (like a member of an array). + * + * @param string $elm namespace, space, and tag name. + */ + private function endElementModeSimple( $elm ) { if ( $this->charContent !== false ) { if ( $this->processingArray ) { // if we're processing an array, use the original element @@ -463,22 +457,23 @@ class XMPReader { } /** - * Hit a closing element in MODE_STRUCT, MODE_SEQ, MODE_BAG - * generally means we've finished processing a nested structure. - * resets some internal variables to indicate that. - * - * Note this means we hit the closing element not the "</rdf:Seq>". - * - * @par For example, when processing: - * @code{,xml} - * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li> - * </rdf:Seq> </exif:ISOSpeedRatings> - * @endcode - * - * This method is called when we hit the "</exif:ISOSpeedRatings>" tag. - * - * @param $elm String namespace . space . tag name. - */ + * Hit a closing element in MODE_STRUCT, MODE_SEQ, MODE_BAG + * generally means we've finished processing a nested structure. + * resets some internal variables to indicate that. + * + * Note this means we hit the closing element not the "</rdf:Seq>". + * + * @par For example, when processing: + * @code{,xml} + * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li> + * </rdf:Seq> </exif:ISOSpeedRatings> + * @endcode + * + * This method is called when we hit the "</exif:ISOSpeedRatings>" tag. + * + * @param string $elm namespace . space . tag name. + * @throws MWException + */ private function endElementNested( $elm ) { /* cur item must be the same as $elm, unless if in MODE_STRUCT @@ -486,7 +481,7 @@ class XMPReader { if ( $this->curItem[0] !== $elm && !( $elm === self::NS_RDF . ' Description' && $this->mode[0] === self::MODE_STRUCT ) - ) { + ) { throw new MWException( "nesting mismatch. got a </$elm> but expected a </" . $this->curItem[0] . '>' ); } @@ -528,23 +523,24 @@ class XMPReader { } /** - * Hit a closing element in MODE_LI (either rdf:Seq, or rdf:Bag ) - * Add information about what type of element this is. - * - * Note we still have to hit the outer "</property>" - * - * @par For example, when processing: - * @code{,xml} - * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li> - * </rdf:Seq> </exif:ISOSpeedRatings> - * @endcode - * - * This method is called when we hit the "</rdf:Seq>". - * (For comparison, we call endElementModeSimple when we - * hit the "</rdf:li>") - * - * @param $elm String namespace . ' ' . element name - */ + * Hit a closing element in MODE_LI (either rdf:Seq, or rdf:Bag ) + * Add information about what type of element this is. + * + * Note we still have to hit the outer "</property>" + * + * @par For example, when processing: + * @code{,xml} + * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li> + * </rdf:Seq> </exif:ISOSpeedRatings> + * @endcode + * + * This method is called when we hit the "</rdf:Seq>". + * (For comparison, we call endElementModeSimple when we + * hit the "</rdf:li>") + * + * @param string $elm namespace . ' ' . element name + * @throws MWException + */ private function endElementModeLi( $elm ) { list( $ns, $tag ) = explode( ' ', $this->curItem[0], 2 ); @@ -575,15 +571,15 @@ class XMPReader { } /** - * End element while in MODE_QDESC - * mostly when ending an element when we have a simple value - * that has qualifiers. - * - * Qualifiers aren't all that common, and we don't do anything - * with them. - * - * @param $elm String namespace and element - */ + * End element while in MODE_QDESC + * mostly when ending an element when we have a simple value + * that has qualifiers. + * + * Qualifiers aren't all that common, and we don't do anything + * with them. + * + * @param string $elm namespace and element + */ private function endElementModeQDesc( $elm ) { if ( $elm === self::NS_RDF . ' value' ) { @@ -594,22 +590,21 @@ class XMPReader { array_shift( $this->mode ); array_shift( $this->curItem ); } - - } /** - * Handler for hitting a closing element. - * - * generally just calls a helper function depending on what - * mode we're in. - * - * Ignores the outer wrapping elements that are optional in - * xmp and have no meaning. - * - * @param $parser XMLParser - * @param $elm String namespace . ' ' . element name - */ + * Handler for hitting a closing element. + * + * generally just calls a helper function depending on what + * mode we're in. + * + * Ignores the outer wrapping elements that are optional in + * xmp and have no meaning. + * + * @param $parser XMLParser + * @param string $elm namespace . ' ' . element name + * @throws MWException + */ function endElement( $parser, $elm ) { if ( $elm === ( self::NS_RDF . ' RDF' ) || $elm === 'adobe:ns:meta/ xmpmeta' @@ -646,7 +641,7 @@ class XMPReader { throw new MWException( "Hit end element </$elm> but no curItem" ); } - switch( $this->mode[0] ) { + switch ( $this->mode[0] ) { case self::MODE_IGNORE: $this->endElementModeIgnore( $elm ); break; @@ -681,16 +676,16 @@ class XMPReader { } /** - * Hit an opening element while in MODE_IGNORE - * - * XMP is extensible, so ignore any tag we don't understand. - * - * Mostly ignores, unless we encounter the element that we are ignoring. - * in which case we add it to the item stack, so we can ignore things - * that are nested, correctly. - * - * @param $elm String namespace . ' ' . tag name - */ + * Hit an opening element while in MODE_IGNORE + * + * XMP is extensible, so ignore any tag we don't understand. + * + * Mostly ignores, unless we encounter the element that we are ignoring. + * in which case we add it to the item stack, so we can ignore things + * that are nested, correctly. + * + * @param string $elm namespace . ' ' . tag name + */ private function startElementModeIgnore( $elm ) { if ( $elm === $this->curItem[0] ) { array_unshift( $this->curItem, $elm ); @@ -699,12 +694,12 @@ class XMPReader { } /** - * Start element in MODE_BAG (unordered array) - * this should always be <rdf:Bag> - * - * @param $elm String namespace . ' ' . tag - * @throws MWException if we have an element that's not <rdf:Bag> - */ + * Start element in MODE_BAG (unordered array) + * this should always be <rdf:Bag> + * + * @param string $elm namespace . ' ' . tag + * @throws MWException if we have an element that's not <rdf:Bag> + */ private function startElementModeBag( $elm ) { if ( $elm === self::NS_RDF . ' Bag' ) { array_unshift( $this->mode, self::MODE_LI ); @@ -715,12 +710,12 @@ class XMPReader { } /** - * Start element in MODE_SEQ (ordered array) - * this should always be <rdf:Seq> - * - * @param $elm String namespace . ' ' . tag - * @throws MWException if we have an element that's not <rdf:Seq> - */ + * Start element in MODE_SEQ (ordered array) + * this should always be <rdf:Seq> + * + * @param string $elm namespace . ' ' . tag + * @throws MWException if we have an element that's not <rdf:Seq> + */ private function startElementModeSeq( $elm ) { if ( $elm === self::NS_RDF . ' Seq' ) { array_unshift( $this->mode, self::MODE_LI ); @@ -736,19 +731,19 @@ class XMPReader { } /** - * Start element in MODE_LANG (language alternative) - * this should always be <rdf:Alt> - * - * This tag tends to be used for metadata like describe this - * picture, which can be translated into multiple languages. - * - * XMP supports non-linguistic alternative selections, - * which are really only used for thumbnails, which - * we don't care about. - * - * @param $elm String namespace . ' ' . tag - * @throws MWException if we have an element that's not <rdf:Alt> - */ + * Start element in MODE_LANG (language alternative) + * this should always be <rdf:Alt> + * + * This tag tends to be used for metadata like describe this + * picture, which can be translated into multiple languages. + * + * XMP supports non-linguistic alternative selections, + * which are really only used for thumbnails, which + * we don't care about. + * + * @param string $elm namespace . ' ' . tag + * @throws MWException if we have an element that's not <rdf:Alt> + */ private function startElementModeLang( $elm ) { if ( $elm === self::NS_RDF . ' Alt' ) { array_unshift( $this->mode, self::MODE_LI_LANG ); @@ -759,22 +754,23 @@ class XMPReader { } /** - * Handle an opening element when in MODE_SIMPLE - * - * This should not happen often. This is for if a simple element - * already opened has a child element. Could happen for a - * qualified element. - * - * For example: - * <exif:DigitalZoomRatio><rdf:Description><rdf:value>0/10</rdf:value> - * <foo:someQualifier>Bar</foo:someQualifier> </rdf:Description> - * </exif:DigitalZoomRatio> - * - * This method is called when processing the <rdf:Description> element - * - * @param $elm String namespace and tag names separated by space. - * @param $attribs Array Attributes of the element. - */ + * Handle an opening element when in MODE_SIMPLE + * + * This should not happen often. This is for if a simple element + * already opened has a child element. Could happen for a + * qualified element. + * + * For example: + * <exif:DigitalZoomRatio><rdf:Description><rdf:value>0/10</rdf:value> + * <foo:someQualifier>Bar</foo:someQualifier> </rdf:Description> + * </exif:DigitalZoomRatio> + * + * This method is called when processing the <rdf:Description> element + * + * @param string $elm namespace and tag names separated by space. + * @param array $attribs Attributes of the element. + * @throws MWException + */ private function startElementModeSimple( $elm, $attribs ) { if ( $elm === self::NS_RDF . ' Description' ) { // If this value has qualifiers @@ -800,19 +796,19 @@ class XMPReader { } /** - * Start an element when in MODE_QDESC. - * This generally happens when a simple element has an inner - * rdf:Description to hold qualifier elements. - * - * For example in: - * <exif:DigitalZoomRatio><rdf:Description><rdf:value>0/10</rdf:value> - * <foo:someQualifier>Bar</foo:someQualifier> </rdf:Description> - * </exif:DigitalZoomRatio> - * Called when processing the <rdf:value> or <foo:someQualifier>. - * - * @param $elm String namespace and tag name separated by a space. - * - */ + * Start an element when in MODE_QDESC. + * This generally happens when a simple element has an inner + * rdf:Description to hold qualifier elements. + * + * For example in: + * <exif:DigitalZoomRatio><rdf:Description><rdf:value>0/10</rdf:value> + * <foo:someQualifier>Bar</foo:someQualifier> </rdf:Description> + * </exif:DigitalZoomRatio> + * Called when processing the <rdf:value> or <foo:someQualifier>. + * + * @param string $elm namespace and tag name separated by a space. + * + */ private function startElementModeQDesc( $elm ) { if ( $elm === self::NS_RDF . ' value' ) { return; // do nothing @@ -824,16 +820,17 @@ class XMPReader { } /** - * Starting an element when in MODE_INITIAL - * This usually happens when we hit an element inside - * the outer rdf:Description - * - * This is generally where most properties start. - * - * @param $ns String Namespace - * @param $tag String tag name (without namespace prefix) - * @param $attribs Array array of attributes - */ + * Starting an element when in MODE_INITIAL + * This usually happens when we hit an element inside + * the outer rdf:Description + * + * This is generally where most properties start. + * + * @param string $ns Namespace + * @param string $tag tag name (without namespace prefix) + * @param array $attribs array of attributes + * @throws MWException + */ private function startElementModeInitial( $ns, $tag, $attribs ) { if ( $ns !== self::NS_RDF ) { @@ -877,23 +874,24 @@ class XMPReader { } /** - * Hit an opening element when in a Struct (MODE_STRUCT) - * This is generally for fields of a compound property. - * - * Example of a struct (abbreviated; flash has more properties): - * - * <exif:Flash> <rdf:Description> <exif:Fired>True</exif:Fired> - * <exif:Mode>1</exif:Mode></rdf:Description></exif:Flash> - * - * or: - * - * <exif:Flash rdf:parseType='Resource'> <exif:Fired>True</exif:Fired> - * <exif:Mode>1</exif:Mode></exif:Flash> - * - * @param $ns String namespace - * @param $tag String tag name (no ns) - * @param $attribs Array array of attribs w/ values. - */ + * Hit an opening element when in a Struct (MODE_STRUCT) + * This is generally for fields of a compound property. + * + * Example of a struct (abbreviated; flash has more properties): + * + * <exif:Flash> <rdf:Description> <exif:Fired>True</exif:Fired> + * <exif:Mode>1</exif:Mode></rdf:Description></exif:Flash> + * + * or: + * + * <exif:Flash rdf:parseType='Resource'> <exif:Fired>True</exif:Fired> + * <exif:Mode>1</exif:Mode></exif:Flash> + * + * @param string $ns namespace + * @param string $tag tag name (no ns) + * @param array $attribs array of attribs w/ values. + * @throws MWException + */ private function startElementModeStruct( $ns, $tag, $attribs ) { if ( $ns !== self::NS_RDF ) { @@ -929,18 +927,18 @@ class XMPReader { } /** - * opening element in MODE_LI - * process elements of arrays. - * - * Example: - * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li> - * </rdf:Seq> </exif:ISOSpeedRatings> - * This method is called when we hit the <rdf:li> element. - * - * @param $elm String: namespace . ' ' . tagname - * @param $attribs Array: Attributes. (needed for BAGSTRUCTS) - * @throws MWException if gets a tag other than <rdf:li> - */ + * opening element in MODE_LI + * process elements of arrays. + * + * Example: + * <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>64</rdf:li> + * </rdf:Seq> </exif:ISOSpeedRatings> + * This method is called when we hit the <rdf:li> element. + * + * @param string $elm namespace . ' ' . tagname + * @param array $attribs Attributes. (needed for BAGSTRUCTS) + * @throws MWException if gets a tag other than <rdf:li> + */ private function startElementModeLi( $elm, $attribs ) { if ( ( $elm ) !== self::NS_RDF . ' li' ) { throw new MWException( "<rdf:li> expected but got $elm." ); @@ -980,32 +978,32 @@ class XMPReader { } /** - * Opening element in MODE_LI_LANG. - * process elements of language alternatives - * - * Example: - * <dc:title> <rdf:Alt> <rdf:li xml:lang="x-default">My house - * </rdf:li> </rdf:Alt> </dc:title> - * - * This method is called when we hit the <rdf:li> element. - * - * @param $elm String namespace . ' ' . tag - * @param $attribs array array of elements (most importantly xml:lang) - * @throws MWException if gets a tag other than <rdf:li> or if no xml:lang - */ + * Opening element in MODE_LI_LANG. + * process elements of language alternatives + * + * Example: + * <dc:title> <rdf:Alt> <rdf:li xml:lang="x-default">My house + * </rdf:li> </rdf:Alt> </dc:title> + * + * This method is called when we hit the <rdf:li> element. + * + * @param string $elm namespace . ' ' . tag + * @param array $attribs array of elements (most importantly xml:lang) + * @throws MWException if gets a tag other than <rdf:li> or if no xml:lang + */ private function startElementModeLiLang( $elm, $attribs ) { if ( $elm !== self::NS_RDF . ' li' ) { throw new MWException( __METHOD__ . " <rdf:li> expected but got $elm." ); } - if ( !isset( $attribs[ self::NS_XML . ' lang'] ) - || !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $attribs[ self::NS_XML . ' lang' ] ) ) + if ( !isset( $attribs[self::NS_XML . ' lang'] ) + || !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $attribs[self::NS_XML . ' lang'] ) ) { throw new MWException( __METHOD__ . " <rdf:li> did not contain, or has invalid xml:lang attribute in lang alternative" ); } // Lang is case-insensitive. - $this->itemLang = strtolower( $attribs[ self::NS_XML . ' lang' ] ); + $this->itemLang = strtolower( $attribs[self::NS_XML . ' lang'] ); // need to add curItem[0] on again since one is for the specific item // and one is for the entire group. @@ -1015,19 +1013,20 @@ class XMPReader { } /** - * Hits an opening element. - * Generally just calls a helper based on what MODE we're in. - * Also does some initial set up for the wrapper element - * - * @param $parser XMLParser - * @param $elm String namespace "<space>" element - * @param $attribs Array attribute name => value - */ + * Hits an opening element. + * Generally just calls a helper based on what MODE we're in. + * Also does some initial set up for the wrapper element + * + * @param $parser XMLParser + * @param string $elm namespace "<space>" element + * @param array $attribs attribute name => value + * @throws MWException + */ function startElement( $parser, $elm, $attribs ) { if ( $elm === self::NS_RDF . ' RDF' || $elm === 'adobe:ns:meta/ xmpmeta' - || $elm === 'adobe:ns:meta/ xapmeta') + || $elm === 'adobe:ns:meta/ xapmeta' ) { /* ignore. */ return; @@ -1057,11 +1056,11 @@ class XMPReader { if ( count( $this->mode ) === 0 ) { // This should not happen. - throw new MWException('Error extracting XMP, ' + throw new MWException( 'Error extracting XMP, ' . "encountered <$elm> with no mode" ); } - switch( $this->mode[0] ) { + switch ( $this->mode[0] ) { case self::MODE_IGNORE: $this->startElementModeIgnore( $elm ); break; @@ -1095,24 +1094,24 @@ class XMPReader { break; default: throw new MWException( 'StartElement in unknown mode: ' . $this->mode[0] ); - break; } } /** - * Process attributes. - * Simple values can be stored as either a tag or attribute - * - * Often the initial "<rdf:Description>" tag just has all the simple - * properties as attributes. - * - * @par Example: - * @code - * <rdf:Description rdf:about="" xmlns:exif="http://ns.adobe.com/exif/1.0/" exif:DigitalZoomRatio="0/10"> - * @endcode - * - * @param $attribs Array attribute=>value array. - */ + * Process attributes. + * Simple values can be stored as either a tag or attribute + * + * Often the initial "<rdf:Description>" tag just has all the simple + * properties as attributes. + * + * @par Example: + * @code + * <rdf:Description rdf:about="" xmlns:exif="http://ns.adobe.com/exif/1.0/" exif:DigitalZoomRatio="0/10"> + * @endcode + * + * @param array $attribs attribute=>value array. + * @throws MWException + */ private function doAttribs( $attribs ) { // first check for rdf:parseType attribute, as that can change @@ -1126,8 +1125,6 @@ class XMPReader { $this->mode[0] = self::MODE_QDESC; } foreach ( $attribs as $name => $val ) { - - if ( strpos( $name, ' ' ) === false ) { // This shouldn't happen, but so far some old software forgets namespace // on rdf:about. @@ -1155,16 +1152,16 @@ class XMPReader { } /** - * Given an extracted value, save it to results array - * - * note also uses $this->ancestorStruct and - * $this->processingArray to determine what name to - * save the value under. (in addition to $tag). - * - * @param $ns String namespace of tag this is for - * @param $tag String tag name - * @param $val String value to save - */ + * Given an extracted value, save it to results array + * + * note also uses $this->ancestorStruct and + * $this->processingArray to determine what name to + * save the value under. (in addition to $tag). + * + * @param string $ns namespace of tag this is for + * @param string $tag tag name + * @param string $val value to save + */ private function saveValue( $ns, $tag, $val ) { $info =& $this->items[$ns][$tag]; diff --git a/includes/media/XMPInfo.php b/includes/media/XMPInfo.php index 83b8a102..f0b2cb5e 100644 --- a/includes/media/XMPInfo.php +++ b/includes/media/XMPInfo.php @@ -22,20 +22,20 @@ */ /** -* This class is just a container for a big array -* used by XMPReader to determine which XMP items to -* extract. -*/ + * This class is just a container for a big array + * used by XMPReader to determine which XMP items to + * extract. + */ class XMPInfo { /** get the items array * @return Array XMP item configuration array. - */ - public static function getItems ( ) { - if( !self::$ranHooks ) { + */ + public static function getItems() { + if ( !self::$ranHooks ) { // This is for if someone makes a custom metadata extension. // For example, a medical wiki might want to decode DICOM xmp properties. - wfRunHooks('XMPGetInfo', Array(&self::$items)); + wfRunHooks( 'XMPGetInfo', Array( &self::$items ) ); self::$ranHooks = true; // Only want to do this once. } return self::$items; @@ -44,26 +44,25 @@ class XMPInfo { static private $ranHooks = false; /** - * XMPInfo::$items keeps a list of all the items - * we are interested to extract, as well as - * information about the item like what type - * it is. - * - * Format is an array of namespaces, - * each containing an array of tags - * each tag is an array of information about the - * tag, including: - * * map_group - what group (used for precedence during conflicts) - * * mode - What type of item (self::MODE_SIMPLE usually, see above for all values) - * * validate - method to validate input. Could also post-process the input. A string value is assumed to be a static method of XMPValidate. Can also take a array( 'className', 'methodName' ). - * * choices - array of potential values (format of 'value' => true ). Only used with validateClosed - * * rangeLow and rangeHigh - alternative to choices for numeric ranges. Again for validateClosed only. - * * children - for MODE_STRUCT items, allowed children. - * * structPart - Indicates that this element can only appear as a member of a structure. - * - * currently this just has a bunch of exif values as this class is only half-done - */ - + * XMPInfo::$items keeps a list of all the items + * we are interested to extract, as well as + * information about the item like what type + * it is. + * + * Format is an array of namespaces, + * each containing an array of tags + * each tag is an array of information about the + * tag, including: + * * map_group - what group (used for precedence during conflicts) + * * mode - What type of item (self::MODE_SIMPLE usually, see above for all values) + * * validate - method to validate input. Could also post-process the input. A string value is assumed to be a static method of XMPValidate. Can also take a array( 'className', 'methodName' ). + * * choices - array of potential values (format of 'value' => true ). Only used with validateClosed + * * rangeLow and rangeHigh - alternative to choices for numeric ranges. Again for validateClosed only. + * * children - for MODE_STRUCT items, allowed children. + * * structPart - Indicates that this element can only appear as a member of a structure. + * + * currently this just has a bunch of exif values as this class is only half-done + */ static private $items = array( 'http://ns.adobe.com/exif/1.0/' => array( 'ApertureValue' => array( @@ -260,7 +259,7 @@ class XMPInfo { 'mode' => XMPReader::MODE_SIMPLE, 'validate' => 'validateDate', ), - 'DateTimeDigitized' => array( /* xmp:CreateDate */ + 'DateTimeDigitized' => array( /* xmp:CreateDate */ 'map_group' => 'exif', 'mode' => XMPReader::MODE_SIMPLE, 'validate' => 'validateDate', @@ -429,7 +428,7 @@ class XMPInfo { 'mode' => XMPReader::MODE_SIMPLE, 'validate' => 'validateClosed', /* can't use a range, as it skips... */ - 'choices' => array( '0' => true, '1' => true, + 'choices' => array( '0' => true, '1' => true, '2' => true, '3' => true, '4' => true, '9' => true, '10' => true, '11' => true, '12' => true, '13' => true, @@ -552,12 +551,12 @@ class XMPInfo { 'map_group' => 'exif', 'mode' => XMPReader::MODE_LANG, ), - 'DateTime' => array( /* proper prop is xmp:ModifyDate */ + 'DateTime' => array( /* proper prop is xmp:ModifyDate */ 'map_group' => 'exif', 'mode' => XMPReader::MODE_SIMPLE, 'validate' => 'validateDate', ), - 'ImageDescription' => array( /* proper one is dc:description */ + 'ImageDescription' => array( /* proper one is dc:description */ 'map_group' => 'exif', 'mode' => XMPReader::MODE_LANG, ), @@ -622,7 +621,7 @@ class XMPInfo { 'mode' => XMPReader::MODE_SIMPLE, 'validate' => 'validateInteger', ), - 'Software' => array( /* see xmp:CreatorTool */ + 'Software' => array( /* see xmp:CreatorTool */ 'map_group' => 'exif', 'mode' => XMPReader::MODE_SIMPLE, ), @@ -669,7 +668,7 @@ class XMPInfo { * 'validate' => 'validateClosed', * 'choices' => array( '1' => true, '2' => true ), * ), - */ + */ ), 'http://ns.adobe.com/exif/1.0/aux/' => array( 'Lens' => array( diff --git a/includes/media/XMPValidate.php b/includes/media/XMPValidate.php index 5ce3c00b..87f8abfe 100644 --- a/includes/media/XMPValidate.php +++ b/includes/media/XMPValidate.php @@ -22,32 +22,32 @@ */ /** -* This contains some static methods for -* validating XMP properties. See XMPInfo and XMPReader classes. -* -* Each of these functions take the same parameters -* * an info array which is a subset of the XMPInfo::items array -* * A value (passed as reference) to validate. This can be either a -* simple value or an array -* * A boolean to determine if this is validating a simple or complex values -* -* It should be noted that when an array is being validated, typically the validation -* function is called once for each value, and then once at the end for the entire array. -* -* These validation functions can also be used to modify the data. See the gps and flash one's -* for example. -* -* @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart1.pdf starting at pg 28 -* @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart2.pdf starting at pg 11 -*/ + * This contains some static methods for + * validating XMP properties. See XMPInfo and XMPReader classes. + * + * Each of these functions take the same parameters + * * an info array which is a subset of the XMPInfo::items array + * * A value (passed as reference) to validate. This can be either a + * simple value or an array + * * A boolean to determine if this is validating a simple or complex values + * + * It should be noted that when an array is being validated, typically the validation + * function is called once for each value, and then once at the end for the entire array. + * + * These validation functions can also be used to modify the data. See the gps and flash one's + * for example. + * + * @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart1.pdf starting at pg 28 + * @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart2.pdf starting at pg 11 + */ class XMPValidate { /** - * function to validate boolean properties ( True or False ) - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate boolean properties ( True or False ) + * + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateBoolean( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc @@ -61,12 +61,12 @@ class XMPValidate { } /** - * function to validate rational properties ( 12/10 ) - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate rational properties ( 12/10 ) + * + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateRational( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc @@ -80,38 +80,38 @@ class XMPValidate { } /** - * function to validate rating properties -1, 0-5 - * - * if its outside of range put it into range. - * - * @see MWG spec - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate rating properties -1, 0-5 + * + * if its outside of range put it into range. + * + * @see MWG spec + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateRating( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc return; } if ( !preg_match( '/^[-+]?\d*(?:\.?\d*)$/D', $val ) - || !is_numeric($val) - ) { + || !is_numeric( $val ) + ) { wfDebugLog( 'XMP', __METHOD__ . " Expected rating but got $val" ); $val = null; return; } else { - $nVal = (float) $val; + $nVal = (float)$val; if ( $nVal < 0 ) { // We do < 0 here instead of < -1 here, since // the values between 0 and -1 are also illegal // as -1 is meant as a special reject rating. - wfDebugLog( 'XMP', __METHOD__ . " Rating too low, setting to -1 (Rejected)"); + wfDebugLog( 'XMP', __METHOD__ . " Rating too low, setting to -1 (Rejected)" ); $val = '-1'; return; } if ( $nVal > 5 ) { - wfDebugLog( 'XMP', __METHOD__ . " Rating too high, setting to 5"); + wfDebugLog( 'XMP', __METHOD__ . " Rating too high, setting to 5" ); $val = '5'; return; } @@ -119,12 +119,12 @@ class XMPValidate { } /** - * function to validate integers - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate integers + * + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateInteger( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc @@ -138,13 +138,13 @@ class XMPValidate { } /** - * function to validate properties with a fixed number of allowed - * choices. (closed choice) - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate properties with a fixed number of allowed + * choices. (closed choice) + * + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateClosed( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc @@ -153,7 +153,7 @@ class XMPValidate { //check if its in a numeric range $inRange = false; - if ( isset( $info['rangeLow'] ) + if ( isset( $info['rangeLow'] ) && isset( $info['rangeHigh'] ) && is_numeric( $val ) && ( intval( $val ) <= $info['rangeHigh'] ) @@ -169,12 +169,12 @@ class XMPValidate { } /** - * function to validate and modify flash structure - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate and modify flash structure + * + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateFlash( $info, &$val, $standalone ) { if ( $standalone ) { // this only validates flash structs, not individual properties @@ -198,23 +198,23 @@ class XMPValidate { } /** - * function to validate LangCode properties ( en-GB, etc ) - * - * This is just a naive check to make sure it somewhat looks like a lang code. - * - * @see rfc 3066 - * @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart1.pdf page 30 (section 8.2.2.5) - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate LangCode properties ( en-GB, etc ) + * + * This is just a naive check to make sure it somewhat looks like a lang code. + * + * @see rfc 3066 + * @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart1.pdf page 30 (section 8.2.2.5) + * + * @param array $info information about current property + * @param &$val Mixed current value to validate + * @param $standalone Boolean if this is a simple property or array + */ public static function validateLangCode( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc return; } - if ( !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $val) ) { + if ( !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $val ) ) { //this is a rather naive check. wfDebugLog( 'XMP', __METHOD__ . " Expected Lang code but got $val" ); $val = null; @@ -223,22 +223,22 @@ class XMPValidate { } /** - * function to validate date properties, and convert to (partial) Exif format. - * - * Dates can be one of the following formats: - * YYYY - * YYYY-MM - * YYYY-MM-DD - * YYYY-MM-DDThh:mmTZD - * YYYY-MM-DDThh:mm:ssTZD - * YYYY-MM-DDThh:mm:ss.sTZD - * - * @param $info Array information about current property - * @param &$val Mixed current value to validate. Converts to TS_EXIF as a side-effect. - * in cases where there's only a partial date, it will give things like - * 2011:04. - * @param $standalone Boolean if this is a simple property or array - */ + * function to validate date properties, and convert to (partial) Exif format. + * + * Dates can be one of the following formats: + * YYYY + * YYYY-MM + * YYYY-MM-DD + * YYYY-MM-DDThh:mmTZD + * YYYY-MM-DDThh:mm:ssTZD + * YYYY-MM-DDThh:mm:ss.sTZD + * + * @param array $info information about current property + * @param &$val Mixed current value to validate. Converts to TS_EXIF as a side-effect. + * in cases where there's only a partial date, it will give things like + * 2011:04. + * @param $standalone Boolean if this is a simple property or array + */ public static function validateDate( $info, &$val, $standalone ) { if ( !$standalone ) { // this only validates standalone properties, not arrays, etc @@ -247,8 +247,8 @@ class XMPValidate { $res = array(); if ( !preg_match( /* ahh! scary regex... */ - '/^([0-3]\d{3})(?:-([01]\d)(?:-([0-3]\d)(?:T([0-2]\d):([0-6]\d)(?::([0-6]\d)(?:\.\d+)?)?([-+]\d{2}:\d{2}|Z)?)?)?)?$/D' - , $val, $res) + '/^([0-3]\d{3})(?:-([01]\d)(?:-([0-3]\d)(?:T([0-2]\d):([0-6]\d)(?::([0-6]\d)(?:\.\d+)?)?([-+]\d{2}:\d{2}|Z)?)?)?)?$/D', + $val, $res ) ) { wfDebugLog( 'XMP', __METHOD__ . " Expected date but got $val" ); $val = null; @@ -295,7 +295,6 @@ class XMPValidate { return; } - // Extra check for empty string necessary due to TZ but no second case. $stripSeconds = false; if ( !isset( $res[6] ) || $res[6] === '' ) { @@ -331,35 +330,35 @@ class XMPValidate { * @see http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart2.pdf * section 1.2.7.4 on page 23 * - * @param $info Array unused (info about prop) + * @param array $info unused (info about prop) * @param &$val String GPS string in either DDD,MM,SSk or * or DDD,MM.mmk form * @param $standalone Boolean if its a simple prop (should always be true) */ - public static function validateGPS ( $info, &$val, $standalone ) { + public static function validateGPS( $info, &$val, $standalone ) { if ( !$standalone ) { return; } $m = array(); - if ( preg_match( + if ( preg_match( '/(\d{1,3}),(\d{1,2}),(\d{1,2})([NWSE])/D', $val, $m ) ) { $coord = intval( $m[1] ); - $coord += intval( $m[2] ) * (1/60); - $coord += intval( $m[3] ) * (1/3600); + $coord += intval( $m[2] ) * ( 1 / 60 ); + $coord += intval( $m[3] ) * ( 1 / 3600 ); if ( $m[4] === 'S' || $m[4] === 'W' ) { $coord = -$coord; } $val = $coord; return; - } elseif ( preg_match( + } elseif ( preg_match( '/(\d{1,3}),(\d{1,2}(?:.\d*)?)([NWSE])/D', $val, $m ) ) { $coord = intval( $m[1] ); - $coord += floatval( $m[2] ) * (1/60); + $coord += floatval( $m[2] ) * ( 1 / 60 ); if ( $m[3] === 'S' || $m[3] === 'W' ) { $coord = -$coord; } @@ -367,7 +366,7 @@ class XMPValidate { return; } else { - wfDebugLog( 'XMP', __METHOD__ + wfDebugLog( 'XMP', __METHOD__ . " Expected GPSCoordinate, but got $val." ); $val = null; return; |