diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2013-08-12 09:28:15 +0200 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2013-08-12 09:28:15 +0200 |
commit | 08aa4418c30cfc18ccc69a0f0f9cb9e17be6c196 (patch) | |
tree | 577a29fb579188d16003a209ce2a2e9c5b0aa2bd /includes/media | |
parent | cacc939b34e315b85e2d72997811eb6677996cc1 (diff) |
Update to MediaWiki 1.21.1
Diffstat (limited to 'includes/media')
25 files changed, 1060 insertions, 929 deletions
diff --git a/includes/media/BMP.php b/includes/media/BMP.php index a515c635..46d1b95b 100644 --- a/includes/media/BMP.php +++ b/includes/media/BMP.php @@ -62,7 +62,7 @@ class BmpHandler extends BitmapHandler { 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); diff --git a/includes/media/Bitmap.php b/includes/media/Bitmap.php index 99ac854b..e2dc68b2 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,7 +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. @@ -158,7 +156,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 +261,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 +278,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 +338,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. @@ -380,7 +377,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 */ @@ -457,7 +454,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 */ @@ -500,8 +497,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 +510,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 +619,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 +651,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 ) { @@ -757,6 +756,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' ] ) ) . " 2>&1"; + wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" ); + wfProfileIn( 'convert' ); + $retval = 0; + $err = wfShellExec( $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..345e7869 100644 --- a/includes/media/BitmapMetadataHandler.php +++ b/includes/media/BitmapMetadataHandler.php @@ -47,13 +47,13 @@ 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 - */ + * 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 ); @@ -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(). @@ -91,11 +90,11 @@ 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 - */ + * 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 */ @@ -106,14 +105,14 @@ 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 - */ + * 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. @@ -144,7 +143,7 @@ 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. */ @@ -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,7 +281,7 @@ 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 ) { @@ -300,6 +300,4 @@ class BitmapMetadataHandler { } } - - } diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php index 84672e05..0a39a2cf 100644 --- a/includes/media/DjVu.php +++ b/includes/media/DjVu.php @@ -183,7 +183,7 @@ class DjVuHandler extends ImageHandler { if ( $wgDjvuPostProcessor ) { $cmd .= " | {$wgDjvuPostProcessor}"; } - $cmd .= ' > ' . wfEscapeShellArg($dstPath) . ') 2>&1'; + $cmd .= ' > ' . wfEscapeShellArg( $dstPath ) . ') 2>&1'; wfProfileIn( 'ddjvu' ); wfDebug( __METHOD__.": $cmd\n" ); $retval = ''; @@ -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; } @@ -247,7 +247,7 @@ class DjVuHandler extends ImageHandler { $image->djvuTextTree = false; $tree = new SimpleXMLElement( $metadata ); if( $tree->getName() == 'mw-djvu' ) { - foreach($tree->children() as $b){ + foreach( $tree->children() as $b ) { if( $b->getName() == 'DjVuTxt' ) { $image->djvuTextTree = $b; } @@ -322,7 +322,7 @@ class DjVuHandler extends ImageHandler { } } - function getPageText( $image, $page ){ + function getPageText( $image, $page ) { $tree = $this->getMetaTree( $image, true ); if ( !$tree ) { return false; diff --git a/includes/media/DjVuImage.php b/includes/media/DjVuImage.php index 6aef562b..46989668 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 @@ -56,7 +65,7 @@ class DjVuImage { $data = $this->getInfo(); if( $data !== false ) { - $width = $data['width']; + $width = $data['width']; $height = $data['height']; return array( $width, $height, 'DjVu', @@ -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,12 +256,12 @@ 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) { # Strip some control characters @@ -260,7 +269,7 @@ class DjVuImage { $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..17671808 100644 --- a/includes/media/Exif.php +++ b/includes/media/Exif.php @@ -31,15 +31,15 @@ */ 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 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 +102,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. * @@ -289,8 +290,8 @@ 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. } @@ -321,7 +322,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 +346,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 +387,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'] ) ) { $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" @@ -412,11 +413,11 @@ class Exif { 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 +434,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. - */ + * 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 +466,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 +476,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,21 +495,21 @@ 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 - */ + * 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) - */ + * 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']; @@ -545,7 +545,7 @@ class Exif { * * @deprecated since 1.18 */ - function makeFormattedData( ) { + function makeFormattedData() { wfDeprecated( __METHOD__, '1.18' ); $this->mFormattedExifData = FormatMetadata::getFormattedData( $this->mFilteredExifData ); @@ -580,7 +580,7 @@ class Exif { */ function getFormattedData() { wfDeprecated( __METHOD__, '1.18' ); - if (!$this->mFormattedExifData) { + if ( !$this->mFormattedExifData ) { $this->makeFormattedData(); } return $this->mFormattedExifData; @@ -612,7 +612,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 +648,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 +662,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 +677,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 +727,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 @@ -748,10 +748,10 @@ class Exif { return false; } if( $count > 1 ) { - foreach( $val as $v ) { + foreach( $val as $v ) { if( !$this->validate( $section, $tag, $v, true ) ) { - return false; - } + return false; + } } return true; } @@ -813,13 +813,13 @@ class Exif { } 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 +828,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 +843,3 @@ class Exif { } } } - diff --git a/includes/media/ExifBitmap.php b/includes/media/ExifBitmap.php index 34a1f511..1671ab25 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; } @@ -56,7 +56,7 @@ class ExifBitmapHandler extends BitmapHandler { && is_array( $metadata['Software'][0]) && isset( $metadata['Software'][0][0] ) && isset( $metadata['Software'][0][1]) - ) { + ) { $metadata['Software'] = $metadata['Software'][0][0] . ' (Version ' . $metadata['Software'][0][1] . ')'; } @@ -86,7 +86,7 @@ class ExifBitmapHandler extends BitmapHandler { if ( $metadata === self::OLD_BROKEN_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..1a7d7723 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 @@ -322,7 +321,7 @@ class FormatMetadata { if ( $subTag != 'fired' && $subValue == 0 ) { continue; } - $fullTag = $tag . '-' . $subTag ; + $fullTag = $tag . '-' . $subTag; $flashMsgs[] = self::msg( $fullTag, $subValue ); } $val = $wgLang->commaList( $flashMsgs ); @@ -526,7 +525,6 @@ class FormatMetadata { } break; - case 'GPSTrackRef': case 'GPSImgDirectionRef': case 'GPSDestBearingRef': @@ -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; } @@ -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,7 +848,7 @@ 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]; @@ -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 @@ -951,8 +949,8 @@ class FormatMetadata { */ 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.'); + throw new MWException( '$lang and $default cannot both ' + . 'be false.' ); } if ( $noHtml ) { @@ -1008,16 +1006,16 @@ 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 +1031,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); + $out[] = self::formatNum( $number ); } return $wgLang->commaList( $out ); } @@ -1117,16 +1115,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 +1188,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 +1224,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 +1236,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'] ) @@ -1353,7 +1351,7 @@ class FormatMetadata { * * @deprecated since 1.18 * -**/ + */ class FormatExif { var $meta; @@ -1361,7 +1359,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..2e532feb 100644 --- a/includes/media/GIF.php +++ b/includes/media/GIF.php @@ -29,7 +29,7 @@ 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 ); @@ -39,7 +39,7 @@ class GIFHandler extends BitmapHandler { 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,7 +85,7 @@ class GIFHandler extends BitmapHandler { function isAnimatedImage( $image ) { $ser = $image->getMetadata(); if ( $ser ) { - $metadata = unserialize($ser); + $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' ); 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' ); 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..6a4e753d 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" ); } @@ -93,7 +93,7 @@ class GIFMetadataExtractor { while( !feof( $fh ) ) { $buf = fread( $fh, 1 ); - if ($buf == self::$gif_frame_sep) { + if ( $buf == self::$gif_frame_sep ) { // Found a frame $frameCount++; @@ -107,14 +107,14 @@ 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" ); $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 @@ -132,10 +132,10 @@ class GIFMetadataExtractor { 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 === "" ) { @@ -164,7 +164,7 @@ class GIFMetadataExtractor { // 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 ); @@ -173,7 +173,7 @@ class GIFMetadataExtractor { $blockLength = $blockLength[1]; $data = fread( $fh, $blockLength ); - if ($blockLength != 11 ) { + if ( $blockLength != 11 ) { wfDebug( __METHOD__ . ' GIF application block with wrong length' ); fseek( $fh, -($blockLength + 1), SEEK_CUR ); self::skipBlock( $fh ); @@ -182,23 +182,22 @@ class GIFMetadataExtractor { // 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" ); $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' ) { @@ -232,7 +231,7 @@ class GIFMetadataExtractor { 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 +251,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,6 +259,7 @@ class GIFMetadataExtractor { /** * @param $data + * @throws Exception * @return int */ static function decodeBPP( $data ) { @@ -276,7 +276,7 @@ class GIFMetadataExtractor { /** * @param $fh - * @return + * @throws Exception */ static function skipBlock( $fh ) { while ( !feof( $fh ) ) { @@ -284,12 +284,13 @@ class GIFMetadataExtractor { 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 +302,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 ) { diff --git a/includes/media/IPTC.php b/includes/media/IPTC.php index 8fd3552f..4191cde0 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,8 +59,8 @@ 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 ) { @@ -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; } @@ -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 ) { 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 - */ + * 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..419afeef 100644 --- a/includes/media/ImageHandler.php +++ b/includes/media/ImageHandler.php @@ -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; @@ -162,11 +161,11 @@ abstract class ImageHandler extends MediaHandler { # Sanity check $width if( $width <= 0) { - wfDebug( __METHOD__.": Invalid destination width: $width\n" ); + 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,7 +187,7 @@ abstract class ImageHandler extends MediaHandler { if ( !$this->normaliseParams( $image, $params ) ) { return false; } - $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) ); + $url = $script . '&' . wfArrayToCgi( $this->getScriptParams( $params ) ); if( $image->mustRender() || $params['width'] < $image->getWidth() ) { return new ThumbnailImage( $image, $url, false, $params ); diff --git a/includes/media/Jpeg.php b/includes/media/Jpeg.php index a15b6524..8b5d6513 100644 --- a/includes/media/Jpeg.php +++ b/includes/media/Jpeg.php @@ -37,7 +37,7 @@ class JpegHandler extends ExifBitmapHandler { $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' ] ) . " 2>&1"; + wfDebug( __METHOD__ . ": running jpgtran: $cmd\n" ); + wfProfileIn( 'jpegtran' ); + $retval = 0; + $err = wfShellExec( $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..6ff07ed2 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,16 +37,16 @@ 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. - */ + * 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' ); @@ -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... @@ -165,10 +165,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,18 +183,18 @@ 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. - */ + * 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" ); diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php index 965099fd..9a3f645b 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]; @@ -103,7 +103,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 +113,42 @@ 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 - */ + * 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)); + 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. - */ + } + + /** + * 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 ) ) { @@ -181,7 +181,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 +199,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 +213,9 @@ 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() * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER * * @return MediaTransformOutput @@ -244,6 +243,15 @@ 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 */ @@ -352,11 +360,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() ); @@ -396,7 +404,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 +412,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 (!) @@ -512,7 +519,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,8 +530,8 @@ 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 ) { @@ -557,4 +564,13 @@ abstract class MediaHandler { 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; + } } diff --git a/includes/media/MediaTransformOutput.php b/includes/media/MediaTransformOutput.php index 773824cb..1f95bc3b 100644 --- a/includes/media/MediaTransformOutput.php +++ b/includes/media/MediaTransformOutput.php @@ -33,6 +33,13 @@ abstract class MediaTransformOutput { var $file; var $width, $height, $url, $page, $path; + + /** + * @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. * @@ -153,7 +160,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() ) { @@ -189,9 +196,12 @@ abstract class MediaTransformOutput { * @return array */ public function getDescLinkAttribs( $title = null, $params = '' ) { - $query = $this->page ? ( 'page=' . urlencode( $this->page ) ) : ''; + $query = ''; + if ( $this->page && $this->page !== 1 ) { + $query = 'page=' . urlencode( $this->page ); + } if( $params ) { - $query .= $query ? '&'.$params : $params; + $query .= $query ? '&' . $params : $params; } $attribs = array( 'href' => $this->file->getTitle()->getLocalURL( $query ), @@ -218,16 +228,16 @@ 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 ) ){ + if( is_array( $parameters ) ) { $defaults = array( 'page' => false ); @@ -260,7 +270,7 @@ class ThumbnailImage extends MediaTransformOutput { * 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. * @@ -281,6 +291,7 @@ 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() ) { @@ -290,7 +301,7 @@ class ThumbnailImage extends MediaTransformOutput { $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 +334,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 +342,14 @@ class ThumbnailImage extends MediaTransformOutput { if ( !empty( $options['img-class'] ) ) { $attribs['class'] = $options['img-class']; } + + // 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 +404,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..b8a5b40b 100644 --- a/includes/media/PNG.php +++ b/includes/media/PNG.php @@ -44,7 +44,7 @@ class PNGHandler extends BitmapHandler { return self::BROKEN_FILE; } - return serialize($metadata); + return serialize( $metadata ); } /** @@ -74,8 +74,8 @@ class PNGHandler extends BitmapHandler { */ function isAnimatedImage( $image ) { $ser = $image->getMetadata(); - if ($ser) { - $metadata = unserialize($ser); + if ( $ser ) { + $metadata = unserialize( $ser ); if( $metadata['frameCount'] > 1 ) return true; } return false; @@ -88,11 +88,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 +105,13 @@ class PNGHandler extends BitmapHandler { wfRestoreWarnings(); if ( !$data || !is_array( $data ) ) { - wfDebug(__METHOD__ . ' invalid png metadata' ); + wfDebug( __METHOD__ . ' invalid png metadata' ); 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' ); return self::METADATA_COMPATIBLE; } return self::METADATA_GOOD; @@ -126,7 +126,7 @@ class PNGHandler extends BitmapHandler { $original = parent::getLongDesc( $image ); wfSuppressWarnings(); - $metadata = unserialize($image->getMetadata()); + $metadata = unserialize( $image->getMetadata() ); wfRestoreWarnings(); if( !$metadata || $metadata['frameCount'] <= 0 ) @@ -134,21 +134,21 @@ class PNGHandler extends BitmapHandler { $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..87f705ca 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: @@ -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..cddab51d 100644 --- a/includes/media/SVG.php +++ b/includes/media/SVG.php @@ -120,6 +120,12 @@ class SvgHandler extends ImageHandler { 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() ); @@ -127,7 +133,7 @@ class SvgHandler extends ImageHandler { $srcPath = $image->getLocalRefPath(); $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight ); - if( $status === true ) { + if ( $status === true ) { return new ThumbnailImage( $image, $dstUrl, $dstPath, $params ); } else { return $status; // MediaTransformError @@ -135,14 +141,15 @@ 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 - */ + * 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 + * @throws MWException + * @return bool|MediaTransformError + */ public function rasterize( $srcPath, $dstPath, $width, $height ) { global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath; $err = false; @@ -163,14 +170,14 @@ 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"; wfProfileIn( 'rsvg' ); - wfDebug( __METHOD__.": $cmd\n" ); + wfDebug( __METHOD__ . ": $cmd\n" ); $err = wfShellExec( $cmd, $retval ); wfProfileOut( 'rsvg' ); } @@ -178,7 +185,7 @@ class SvgHandler extends ImageHandler { $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 +220,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 +240,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 +254,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 +320,7 @@ class SvgHandler extends ImageHandler { return false; } $metadata = $this->unpackMetadata( $metadata ); - if ( !$metadata ) { + if ( !$metadata || isset( $metadata['error'] ) ) { return false; } diff --git a/includes/media/SVGMetadataExtractor.php b/includes/media/SVGMetadataExtractor.php index c6f63fd4..0de212b9 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 ); @@ -120,6 +121,7 @@ class SVGReader { /** * Read the SVG + * @throws MWException * @return bool */ protected function read() { @@ -137,7 +139,7 @@ class SVGReader { $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; @@ -180,8 +182,8 @@ 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" ); @@ -192,7 +194,7 @@ class SVGReader { 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,7 +204,8 @@ 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" ); @@ -221,7 +224,7 @@ 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" ); @@ -231,7 +234,7 @@ class SVGReader { 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 @@ -282,16 +285,16 @@ 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') ) ); + $viewBox = preg_split( '/\s+/', trim( $this->reader->getAttribute( 'viewBox' ) ) ); if( count( $viewBox ) == 4 ) { $viewWidth = $this->scaleSVGUnit( $viewBox[2] ); $viewHeight = $this->scaleSVGUnit( $viewBox[3] ); @@ -301,12 +304,12 @@ class SVGReader { } } } - 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' ); } @@ -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, diff --git a/includes/media/Tiff.php b/includes/media/Tiff.php index d95c9074..0042208b 100644 --- a/includes/media/Tiff.php +++ b/includes/media/Tiff.php @@ -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..ba38d158 100644 --- a/includes/media/XCF.php +++ b/includes/media/XCF.php @@ -72,7 +72,7 @@ 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 ) { @@ -84,7 +84,7 @@ class XCFHandler extends BitmapHandler { # 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: # diff --git a/includes/media/XMP.php b/includes/media/XMP.php index 36660b3d..62738a00 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'] ) @@ -201,13 +200,12 @@ class XMPReader { // 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 ); @@ -414,36 +409,33 @@ 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. - */ + * 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. - */ + * 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 ) { @@ -463,22 +455,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 +479,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 +521,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 +569,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 +588,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' @@ -681,16 +674,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 +692,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 +708,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 +729,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 +752,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 +794,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 +818,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 +872,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 +925,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,19 +976,19 @@ 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." ); @@ -1015,19 +1011,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,7 +1054,7 @@ 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" ); } @@ -1095,24 +1092,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 +1123,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 +1150,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..102547ff 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 ( ) { + */ + 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', @@ -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..f98f0b57 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,23 +80,23 @@ 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; @@ -106,12 +106,12 @@ class XMPValidate { // 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,17 +198,17 @@ 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 @@ -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,7 +330,7 @@ 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) @@ -342,7 +341,7 @@ class XMPValidate { } $m = array(); - if ( preg_match( + if ( preg_match( '/(\d{1,3}),(\d{1,2}),(\d{1,2})([NWSE])/D', $val, $m ) ) { @@ -354,7 +353,7 @@ class XMPValidate { } $val = $coord; return; - } elseif ( preg_match( + } elseif ( preg_match( '/(\d{1,3}),(\d{1,2}(?:.\d*)?)([NWSE])/D', $val, $m ) ) { @@ -367,7 +366,7 @@ class XMPValidate { return; } else { - wfDebugLog( 'XMP', __METHOD__ + wfDebugLog( 'XMP', __METHOD__ . " Expected GPSCoordinate, but got $val." ); $val = null; return; |