summaryrefslogtreecommitdiff
path: root/includes/media
diff options
context:
space:
mode:
Diffstat (limited to 'includes/media')
-rw-r--r--includes/media/Bitmap.php7
-rw-r--r--includes/media/BitmapMetadataHandler.php8
-rw-r--r--includes/media/DjVu.php96
-rw-r--r--includes/media/DjVuImage.php10
-rw-r--r--includes/media/Exif.php12
-rw-r--r--includes/media/ExifBitmap.php78
-rw-r--r--includes/media/FormatMetadata.php35
-rw-r--r--includes/media/GIF.php8
-rw-r--r--includes/media/GIFMetadataExtractor.php4
-rw-r--r--includes/media/IPTC.php4
-rw-r--r--includes/media/ImageHandler.php4
-rw-r--r--includes/media/Jpeg.php2
-rw-r--r--includes/media/JpegMetadataExtractor.php4
-rw-r--r--includes/media/MediaHandler.php4
-rw-r--r--includes/media/MediaTransformInvalidParametersException.php3
-rw-r--r--includes/media/PNG.php8
-rw-r--r--includes/media/PNGMetadataExtractor.php16
-rw-r--r--includes/media/SVG.php15
-rw-r--r--includes/media/SVGMetadataExtractor.php17
-rw-r--r--includes/media/TransformationalImageHandler.php2
-rw-r--r--includes/media/WebP.php306
-rw-r--r--includes/media/XCF.php6
-rw-r--r--includes/media/XMP.php245
-rw-r--r--includes/media/XMPInfo.php11
-rw-r--r--includes/media/XMPValidate.php60
-rw-r--r--includes/media/tinyrgb.iccbin0 -> 524 bytes
26 files changed, 692 insertions, 273 deletions
diff --git a/includes/media/Bitmap.php b/includes/media/Bitmap.php
index 3b1d978e..0cc093bf 100644
--- a/includes/media/Bitmap.php
+++ b/includes/media/Bitmap.php
@@ -92,9 +92,8 @@ class BitmapHandler extends TransformationalImageHandler {
// JPEG decoder hint to reduce memory, available since IM 6.5.6-2
$decoderHint = array( '-define', "jpeg:size={$params['physicalDimensions']}" );
}
- } elseif ( $params['mimeType'] == 'image/png' ) {
+ } elseif ( $params['mimeType'] == 'image/png' || $params['mimeType'] == 'image/webp' ) {
$quality = array( '-quality', '95' ); // zlib 9, adaptive filtering
-
} elseif ( $params['mimeType'] == 'image/gif' ) {
if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
// Extract initial frame only; we're so big it'll
@@ -121,9 +120,9 @@ class BitmapHandler extends TransformationalImageHandler {
'-layers', 'merge',
'-background', 'white',
);
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$xcfMeta = unserialize( $image->getMetadata() );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $xcfMeta
&& isset( $xcfMeta['colorType'] )
&& $xcfMeta['colorType'] === 'greyscale-alpha'
diff --git a/includes/media/BitmapMetadataHandler.php b/includes/media/BitmapMetadataHandler.php
index c8d37bbb..a5cddac9 100644
--- a/includes/media/BitmapMetadataHandler.php
+++ b/includes/media/BitmapMetadataHandler.php
@@ -21,6 +21,8 @@
* @ingroup Media
*/
+use MediaWiki\Logger\LoggerFactory;
+
/**
* Class to deal with reconciling and extracting metadata from bitmap images.
* This is meant to comply with http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
@@ -167,7 +169,7 @@ class BitmapMetadataHandler {
}
}
if ( isset( $seg['XMP'] ) && $showXMP ) {
- $xmp = new XMPReader();
+ $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ) );
$xmp->parse( $seg['XMP'] );
foreach ( $seg['XMP_ext'] as $xmpExt ) {
/* Support for extended xmp in jpeg files
@@ -203,7 +205,7 @@ class BitmapMetadataHandler {
if ( isset( $array['text']['xmp']['x-default'] )
&& $array['text']['xmp']['x-default'] !== '' && $showXMP
) {
- $xmp = new XMPReader();
+ $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ) );
$xmp->parse( $array['text']['xmp']['x-default'] );
$xmpRes = $xmp->getResults();
foreach ( $xmpRes as $type => $xmpSection ) {
@@ -237,7 +239,7 @@ class BitmapMetadataHandler {
}
if ( $baseArray['xmp'] !== '' && XMPReader::isSupported() ) {
- $xmp = new XMPReader();
+ $xmp = new XMPReader( LoggerFactory::getInstance( 'XMP' ) );
$xmp->parse( $baseArray['xmp'] );
$xmpRes = $xmp->getResults();
foreach ( $xmpRes as $type => $xmpSection ) {
diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php
index 1b0eb492..b422bfa2 100644
--- a/includes/media/DjVu.php
+++ b/includes/media/DjVu.php
@@ -27,6 +27,8 @@
* @ingroup Media
*/
class DjVuHandler extends ImageHandler {
+ const EXPENSIVE_SIZE_LIMIT = 10485760; // 10MiB
+
/**
* @return bool
*/
@@ -50,6 +52,15 @@ class DjVuHandler extends ImageHandler {
}
/**
+ * True if creating thumbnails from the file is large or otherwise resource-intensive.
+ * @param File $file
+ * @return bool
+ */
+ public function isExpensiveToThumbnail( $file ) {
+ return $file->getSize() > static::EXPENSIVE_SIZE_LIMIT;
+ }
+
+ /**
* @param File $file
* @return bool
*/
@@ -137,31 +148,12 @@ class DjVuHandler extends ImageHandler {
function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
global $wgDjvuRenderer, $wgDjvuPostProcessor;
- // Fetch XML and check it, to give a more informative error message than the one which
- // normaliseParams will inevitably give.
- $xml = $image->getMetadata();
- if ( !$xml ) {
- $width = isset( $params['width'] ) ? $params['width'] : 0;
- $height = isset( $params['height'] ) ? $params['height'] : 0;
-
- return new MediaTransformError( 'thumbnail_error', $width, $height,
- wfMessage( 'djvu_no_xml' )->text() );
- }
-
if ( !$this->normaliseParams( $image, $params ) ) {
return new TransformParameterError( $params );
}
$width = $params['width'];
$height = $params['height'];
$page = $params['page'];
- if ( $page > $this->pageCount( $image ) ) {
- return new MediaTransformError(
- 'thumbnail_error',
- $width,
- $height,
- wfMessage( 'djvu_page_error' )->text()
- );
- }
if ( $flags & self::TRANSFORM_LATER ) {
$params = array(
@@ -273,9 +265,9 @@ class DjVuHandler extends ImageHandler {
return $metadata;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$unser = unserialize( $metadata );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( is_array( $unser ) ) {
if ( isset( $unser['error'] ) ) {
return false;
@@ -312,7 +304,7 @@ class DjVuHandler extends ImageHandler {
return false;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
try {
// Set to false rather than null to avoid further attempts
$image->dejaMetaTree = false;
@@ -335,7 +327,7 @@ class DjVuHandler extends ImageHandler {
} catch ( Exception $e ) {
wfDebug( "Bogus multipage XML metadata on '{$image->getName()}'\n" );
}
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $gettext ) {
return $image->djvuTextTree;
} else {
@@ -384,29 +376,55 @@ class DjVuHandler extends ImageHandler {
}
function pageCount( $image ) {
- $tree = $this->getMetaTree( $image );
- if ( !$tree ) {
- return false;
+ global $wgMemc;
+
+ $key = wfMemcKey( 'file-djvu', 'pageCount', $image->getSha1() );
+
+ $count = $wgMemc->get( $key );
+ if ( $count === false ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+ $count = count( $tree->xpath( '//OBJECT' ) );
+ $wgMemc->set( $key, $count );
}
- return count( $tree->xpath( '//OBJECT' ) );
+ return $count;
}
function getPageDimensions( $image, $page ) {
- $tree = $this->getMetaTree( $image );
- if ( !$tree ) {
- return false;
- }
+ global $wgMemc;
- $o = $tree->BODY[0]->OBJECT[$page - 1];
- if ( $o ) {
- return array(
- 'width' => intval( $o['width'] ),
- 'height' => intval( $o['height'] )
- );
- } else {
- return false;
+ $key = wfMemcKey( 'file-djvu', 'dimensions', $image->getSha1() );
+
+ $dimsByPage = $wgMemc->get( $key );
+ if ( !is_array( $dimsByPage ) ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+
+ $dimsByPage = array();
+ $count = count( $tree->xpath( '//OBJECT' ) );
+ for ( $i = 0; $i < $count; ++$i ) {
+ $o = $tree->BODY[0]->OBJECT[$i];
+ if ( $o ) {
+ $dimsByPage[$i] = array(
+ 'width' => (int)$o['width'],
+ 'height' => (int)$o['height']
+ );
+ } else {
+ $dimsByPage[$i] = false;
+ }
+ }
+
+ $wgMemc->set( $key, $dimsByPage );
}
+
+ $index = $page - 1; // MW starts pages at 1
+
+ return isset( $dimsByPage[$index] ) ? $dimsByPage[$index] : false;
}
/**
diff --git a/includes/media/DjVuImage.php b/includes/media/DjVuImage.php
index e8faa70a..dac76fad 100644
--- a/includes/media/DjVuImage.php
+++ b/includes/media/DjVuImage.php
@@ -123,9 +123,9 @@ class DjVuImage {
}
function getInfo() {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$file = fopen( $this->mFilename, 'rb' );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $file === false ) {
wfDebug( __METHOD__ . ": missing or failed file read\n" );
@@ -150,7 +150,7 @@ class DjVuImage {
wfDebug( __METHOD__ . ": not a DjVu file\n" );
} elseif ( $subtype == 'DJVU' ) {
// Single-page document
- $info = $this->getPageInfo( $file, $formLength );
+ $info = $this->getPageInfo( $file );
} elseif ( $subtype == 'DJVM' ) {
// Multi-page document
$info = $this->getMultiPageInfo( $file, $formLength );
@@ -202,7 +202,7 @@ class DjVuImage {
if ( $subtype == 'DJVU' ) {
wfDebug( __METHOD__ . ": found first subpage\n" );
- return $this->getPageInfo( $file, $length );
+ return $this->getPageInfo( $file );
}
$this->skipChunk( $file, $length - 4 );
} else {
@@ -216,7 +216,7 @@ class DjVuImage {
return false;
}
- private function getPageInfo( $file, $formLength ) {
+ private function getPageInfo( $file ) {
list( $chunk, $length ) = $this->readChunk( $file );
if ( $chunk != 'INFO' ) {
wfDebug( __METHOD__ . ": expected INFO chunk, got '$chunk'\n" );
diff --git a/includes/media/Exif.php b/includes/media/Exif.php
index 33868689..b4cc43e5 100644
--- a/includes/media/Exif.php
+++ b/includes/media/Exif.php
@@ -294,9 +294,9 @@ class Exif {
$this->debugFile( $this->basename, __FUNCTION__, true );
if ( function_exists( 'exif_read_data' ) ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$data = exif_read_data( $this->file, 0, true );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
} else {
throw new MWException( "Internal error: exif_read_data not present. " .
"\$wgShowEXIF may be incorrectly set or not checked by an extension." );
@@ -471,17 +471,17 @@ class Exif {
break;
}
if ( $charset ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$val = iconv( $charset, 'UTF-8//IGNORE', $val );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
} else {
// if valid utf-8, assume that, otherwise assume windows-1252
$valCopy = $val;
UtfNormal\Validator::quickIsNFCVerify( $valCopy ); //validates $valCopy.
if ( $valCopy !== $val ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$val = iconv( 'Windows-1252', 'UTF-8//IGNORE', $val );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
}
diff --git a/includes/media/ExifBitmap.php b/includes/media/ExifBitmap.php
index f56a947f..5ba5c68c 100644
--- a/includes/media/ExifBitmap.php
+++ b/includes/media/ExifBitmap.php
@@ -30,6 +30,7 @@
class ExifBitmapHandler extends BitmapHandler {
const BROKEN_FILE = '-1'; // error extracting metadata
const OLD_BROKEN_FILE = '0'; // outdated error extracting metadata.
+ const SRGB_ICC_PROFILE_NAME = 'IEC 61966-2.1 Default RGB colour space - sRGB';
function convertMetadataVersion( $metadata, $version = 1 ) {
// basically flattens arrays.
@@ -100,9 +101,9 @@ class ExifBitmapHandler extends BitmapHandler {
if ( $metadata === self::BROKEN_FILE ) {
return self::METADATA_GOOD;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$exif = unserialize( $metadata );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !isset( $exif['MEDIAWIKI_EXIF_VERSION'] )
|| $exif['MEDIAWIKI_EXIF_VERSION'] != Exif::version()
) {
@@ -224,9 +225,9 @@ class ExifBitmapHandler extends BitmapHandler {
if ( !$data ) {
return 0;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$data = unserialize( $data );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( isset( $data['Orientation'] ) ) {
# See http://sylvana.net/jpegcrop/exif_orientation.html
switch ( $data['Orientation'] ) {
@@ -243,4 +244,73 @@ class ExifBitmapHandler extends BitmapHandler {
return 0;
}
+
+ protected function transformImageMagick( $image, $params ) {
+ global $wgUseTinyRGBForJPGThumbnails;
+
+ $ret = parent::transformImageMagick( $image, $params );
+
+ if ( $ret ) {
+ return $ret;
+ }
+
+ if ( $params['mimeType'] === 'image/jpeg' && $wgUseTinyRGBForJPGThumbnails ) {
+ // T100976 If the profile embedded in the JPG is sRGB, swap it for the smaller
+ // (and free) TinyRGB
+
+ $this->swapICCProfile(
+ $params['dstPath'],
+ self::SRGB_ICC_PROFILE_NAME,
+ realpath( __DIR__ ) . '/tinyrgb.icc'
+ );
+ }
+
+ return false;
+ }
+
+ /**
+ * Swaps an embedded ICC profile for another, if found. Depends on exiftool, no-op if not installed.
+ * @param string $filepath File to be manipulated (will be overwritten)
+ * @param string $oldProfileString Exact name of color profile to look for (the one that will be replaced)
+ * @param string $profileFilepath ICC profile file to apply to the file
+ * @since 1.26
+ * @return bool
+ */
+ public function swapICCProfile( $filepath, $oldProfileString, $profileFilepath ) {
+ global $wgExiftool;
+
+ if ( !$wgExiftool || !is_executable( $wgExiftool ) ) {
+ return false;
+ }
+
+ $cmd = wfEscapeShellArg( $wgExiftool,
+ '-DeviceModelDesc',
+ '-S',
+ '-T',
+ $filepath
+ );
+
+ $output = wfShellExecWithStderr( $cmd, $retval );
+
+ if ( $retval !== 0 || strcasecmp( trim( $output ), $oldProfileString ) !== 0 ) {
+ // We can't establish that this file has the expected ICC profile, don't process it
+ return false;
+ }
+
+ $cmd = wfEscapeShellArg( $wgExiftool,
+ '-overwrite_original',
+ '-icc_profile<=' . $profileFilepath,
+ $filepath
+ );
+
+ $output = wfShellExecWithStderr( $cmd, $retval );
+
+ if ( $retval !== 0 ) {
+ $this->logErrorForExternalProcess( $retval, $output, $cmd );
+
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/includes/media/FormatMetadata.php b/includes/media/FormatMetadata.php
index 501bb9c2..0fee8cc0 100644
--- a/includes/media/FormatMetadata.php
+++ b/includes/media/FormatMetadata.php
@@ -706,7 +706,7 @@ class FormatMetadata extends ContextSource {
break;
case 'GPSDOP':
- // See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)
+ // See https://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)
if ( $val <= 2 ) {
$val = $this->exifMsg( $tag, 'excellent', $this->formatNum( $val ) );
} elseif ( $val <= 5 ) {
@@ -1004,28 +1004,6 @@ class FormatMetadata extends ContextSource {
}
/**
- * Flatten an array, using the user language for any messages.
- *
- * @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 bool $noHtml If to avoid returning anything resembling HTML.
- * (Ugly hack for backwards compatibility with old MediaWiki).
- * @param bool|IContextSource $context
- * @return string Single value (in wiki-syntax).
- */
- public static function flattenArray( $vals, $type = 'ul', $noHtml = false, $context = false ) {
- $obj = new FormatMetadata;
- if ( $context ) {
- $obj->setContext( $context );
- }
-
- return $obj->flattenArrayReal( $vals, $type, $noHtml );
- }
-
- /**
* A function to collapse multivalued tags into a single value.
* This turns an array of (for example) authors into a bulleted list.
*
@@ -1305,7 +1283,7 @@ class FormatMetadata extends ContextSource {
*/
private function gcd( $a, $b ) {
/*
- // http://en.wikipedia.org/wiki/Euclidean_algorithm
+ // https://en.wikipedia.org/wiki/Euclidean_algorithm
// Recursive form would be:
if( $b == 0 )
return $a;
@@ -1597,7 +1575,6 @@ class FormatMetadata extends ContextSource {
// If revision deleted, exit immediately
if ( $file->isDeleted( File::DELETED_FILE ) ) {
-
return array();
}
@@ -1755,8 +1732,9 @@ class FormatMetadata extends ContextSource {
}
/**
- * Turns an XMP-style multivalue array into a single value by dropping all but the first value.
- * If the value is not a multivalue array (or a multivalue array inside a multilang array), it is returned unchanged.
+ * Turns an XMP-style multivalue array into a single value by dropping all but the first
+ * value. If the value is not a multivalue array (or a multivalue array inside a multilang
+ * array), it is returned unchanged.
* See mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format
* @param mixed $value
* @return mixed The value, or the first value if there were multiple ones
@@ -1765,7 +1743,8 @@ class FormatMetadata extends ContextSource {
protected function resolveMultivalueValue( $value ) {
if ( !is_array( $value ) ) {
return $value;
- } elseif ( isset( $value['_type'] ) && $value['_type'] === 'lang' ) { // if this is a multilang array, process fields separately
+ } elseif ( isset( $value['_type'] ) && $value['_type'] === 'lang' ) {
+ // if this is a multilang array, process fields separately
$newValue = array();
foreach ( $value as $k => $v ) {
$newValue[$k] = $this->resolveMultivalueValue( $v );
diff --git a/includes/media/GIF.php b/includes/media/GIF.php
index e3621fbd..94aca615 100644
--- a/includes/media/GIF.php
+++ b/includes/media/GIF.php
@@ -131,9 +131,9 @@ class GIFHandler extends BitmapHandler {
return self::METADATA_GOOD;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$data = unserialize( $metadata );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$data || !is_array( $data ) ) {
wfDebug( __METHOD__ . " invalid GIF metadata\n" );
@@ -161,9 +161,9 @@ class GIFHandler extends BitmapHandler {
$original = parent::getLongDesc( $image );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$metadata = unserialize( $image->getMetadata() );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$metadata || $metadata['frameCount'] <= 1 ) {
return $original;
diff --git a/includes/media/GIFMetadataExtractor.php b/includes/media/GIFMetadataExtractor.php
index 5c370465..6ee23cda 100644
--- a/includes/media/GIFMetadataExtractor.php
+++ b/includes/media/GIFMetadataExtractor.php
@@ -161,9 +161,9 @@ class GIFMetadataExtractor {
UtfNormal\Validator::quickIsNFCVerify( $dataCopy );
if ( $dataCopy !== $data ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$data = iconv( 'windows-1252', 'UTF-8', $data );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
$commentCount = count( $comment );
diff --git a/includes/media/IPTC.php b/includes/media/IPTC.php
index 0eb27cc8..c3d58b8b 100644
--- a/includes/media/IPTC.php
+++ b/includes/media/IPTC.php
@@ -445,9 +445,9 @@ class IPTC {
*/
private static function convIPTCHelper( $data, $charset ) {
if ( $charset ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$data = iconv( $charset, "UTF-8//IGNORE", $data );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $data === false ) {
$data = "";
wfDebugLog( 'iptc', __METHOD__ . " Error converting iptc data charset $charset to utf-8" );
diff --git a/includes/media/ImageHandler.php b/includes/media/ImageHandler.php
index 968e4243..db74bb35 100644
--- a/includes/media/ImageHandler.php
+++ b/includes/media/ImageHandler.php
@@ -201,9 +201,9 @@ abstract class ImageHandler extends MediaHandler {
}
function getImageSize( $image, $path ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$gis = getimagesize( $path );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $gis;
}
diff --git a/includes/media/Jpeg.php b/includes/media/Jpeg.php
index 5463922b..040ff96e 100644
--- a/includes/media/Jpeg.php
+++ b/includes/media/Jpeg.php
@@ -137,7 +137,7 @@ class JpegHandler extends ExifBitmapHandler {
$rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;
- if ( $wgJpegTran && is_file( $wgJpegTran ) ) {
+ if ( $wgJpegTran && is_executable( $wgJpegTran ) ) {
$cmd = wfEscapeShellArg( $wgJpegTran ) .
" -rotate " . wfEscapeShellArg( $rotation ) .
" -outfile " . wfEscapeShellArg( $params['dstPath'] ) .
diff --git a/includes/media/JpegMetadataExtractor.php b/includes/media/JpegMetadataExtractor.php
index ae4af8d1..5069f180 100644
--- a/includes/media/JpegMetadataExtractor.php
+++ b/includes/media/JpegMetadataExtractor.php
@@ -102,9 +102,9 @@ class JpegMetadataExtractor {
// turns $com to valid utf-8.
// thus if no change, its utf-8, otherwise its something else.
if ( $com !== $oldCom ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$com = $oldCom = iconv( 'windows-1252', 'UTF-8//IGNORE', $oldCom );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
// Try it again, if its still not a valid string, then probably
// binary junk or some really weird encoding, so don't extract.
diff --git a/includes/media/MediaHandler.php b/includes/media/MediaHandler.php
index 33aed34f..3a59996c 100644
--- a/includes/media/MediaHandler.php
+++ b/includes/media/MediaHandler.php
@@ -181,9 +181,9 @@ abstract class MediaHandler {
if ( !is_array( $metadata ) ) {
//unserialize to keep return parameter consistent.
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$ret = unserialize( $metadata );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
return $ret;
}
diff --git a/includes/media/MediaTransformInvalidParametersException.php b/includes/media/MediaTransformInvalidParametersException.php
index 15a2ca55..6f9c2916 100644
--- a/includes/media/MediaTransformInvalidParametersException.php
+++ b/includes/media/MediaTransformInvalidParametersException.php
@@ -23,4 +23,5 @@
*
* @ingroup Exception
*/
-class MediaTransformInvalidParametersException extends MWException {}
+class MediaTransformInvalidParametersException extends MWException {
+}
diff --git a/includes/media/PNG.php b/includes/media/PNG.php
index 5f1aca57..c3f08325 100644
--- a/includes/media/PNG.php
+++ b/includes/media/PNG.php
@@ -118,9 +118,9 @@ class PNGHandler extends BitmapHandler {
return self::METADATA_GOOD;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$data = unserialize( $metadata );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$data || !is_array( $data ) ) {
wfDebug( __METHOD__ . " invalid png metadata\n" );
@@ -147,9 +147,9 @@ class PNGHandler extends BitmapHandler {
global $wgLang;
$original = parent::getLongDesc( $image );
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$metadata = unserialize( $image->getMetadata() );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$metadata || $metadata['frameCount'] <= 0 ) {
return $original;
diff --git a/includes/media/PNGMetadataExtractor.php b/includes/media/PNGMetadataExtractor.php
index bccd36c1..ac92460e 100644
--- a/includes/media/PNGMetadataExtractor.php
+++ b/includes/media/PNGMetadataExtractor.php
@@ -201,9 +201,9 @@ class PNGMetadataExtractor {
// if compressed
if ( $items[2] == "\x01" ) {
if ( function_exists( 'gzuncompress' ) && $items[4] === "\x00" ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$items[5] = gzuncompress( $items[5] );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $items[5] === false ) {
// decompression failed
@@ -245,9 +245,9 @@ class PNGMetadataExtractor {
fseek( $fh, self::$crcSize, SEEK_CUR );
continue;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$content = iconv( 'ISO-8859-1', 'UTF-8', $content );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $content === false ) {
throw new Exception( __METHOD__ . ": Read error (error with iconv)" );
@@ -285,9 +285,9 @@ class PNGMetadataExtractor {
continue;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$content = gzuncompress( $content );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $content === false ) {
// decompression failed
@@ -296,9 +296,9 @@ class PNGMetadataExtractor {
continue;
}
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$content = iconv( 'ISO-8859-1', 'UTF-8', $content );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( $content === false ) {
throw new Exception( __METHOD__ . ": Read error (error with iconv)" );
diff --git a/includes/media/SVG.php b/includes/media/SVG.php
index 8fdfa474..1118598f 100644
--- a/includes/media/SVG.php
+++ b/includes/media/SVG.php
@@ -95,7 +95,7 @@ class SvgHandler extends ImageHandler {
$metadata = $this->unpackMetadata( $metadata );
if ( isset( $metadata['translations'] ) ) {
foreach ( $metadata['translations'] as $lang => $langType ) {
- if ( $langType === SvgReader::LANG_FULL_MATCH ) {
+ if ( $langType === SVGReader::LANG_FULL_MATCH ) {
$langList[] = $lang;
}
}
@@ -205,11 +205,12 @@ class SvgHandler extends ImageHandler {
$tmpDir = wfTempDir() . '/svg_' . wfRandomString( 24 );
$lnPath = "$tmpDir/" . basename( $srcPath );
$ok = mkdir( $tmpDir, 0771 ) && symlink( $srcPath, $lnPath );
+ /** @noinspection PhpUnusedLocalVariableInspection */
$cleaner = new ScopedCallback( function () use ( $tmpDir, $lnPath ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
unlink( $lnPath );
rmdir( $tmpDir );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
} );
if ( !$ok ) {
wfDebugLog( 'thumbnail',
@@ -307,9 +308,9 @@ class SvgHandler extends ImageHandler {
*/
function getImageSize( $file, $path, $metadata = false ) {
if ( $metadata === false ) {
- $metadata = $file->getMetaData();
+ $metadata = $file->getMetadata();
}
- $metadata = $this->unpackMetaData( $metadata );
+ $metadata = $this->unpackMetadata( $metadata );
if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) {
return array( $metadata['width'], $metadata['height'], 'SVG',
@@ -375,9 +376,9 @@ class SvgHandler extends ImageHandler {
}
function unpackMetadata( $metadata ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$unser = unserialize( $metadata );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( isset( $unser['version'] ) && $unser['version'] == self::SVG_METADATA_VERSION ) {
return $unser;
} else {
diff --git a/includes/media/SVGMetadataExtractor.php b/includes/media/SVGMetadataExtractor.php
index 2037c331..1ec2f814 100644
--- a/includes/media/SVGMetadataExtractor.php
+++ b/includes/media/SVGMetadataExtractor.php
@@ -108,17 +108,17 @@ class SVGReader {
// Because we cut off the end of the svg making an invalid one. Complicated
// try catch thing to make sure warnings get restored. Seems like there should
// be a better way.
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
try {
$this->read();
} catch ( Exception $e ) {
// Note, if this happens, the width/height will be taken to be 0x0.
// Should we consider it the default 512x512 instead?
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
libxml_disable_entity_loader( $oldDisable );
throw $e;
}
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
libxml_disable_entity_loader( $oldDisable );
}
@@ -262,7 +262,6 @@ class SVGReader {
} elseif ( $this->reader->namespaceURI == self::NS_SVG
&& $this->reader->nodeType == XMLReader::ELEMENT
) {
-
$sysLang = $this->reader->getAttribute( 'systemLanguage' );
if ( !is_null( $sysLang ) && $sysLang !== '' ) {
// See http://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
@@ -320,16 +319,6 @@ class SVGReader {
}
}
- // @todo FIXME: Unused, remove?
- private function warn( $data ) {
- wfDebug( "SVGReader: $data\n" );
- }
-
- // @todo FIXME: Unused, remove?
- private function notice( $data ) {
- wfDebug( "SVGReader WARN: $data\n" );
- }
-
/**
* Parse the attributes of an SVG element
*
diff --git a/includes/media/TransformationalImageHandler.php b/includes/media/TransformationalImageHandler.php
index fd8d81d2..15753a96 100644
--- a/includes/media/TransformationalImageHandler.php
+++ b/includes/media/TransformationalImageHandler.php
@@ -167,7 +167,7 @@ abstract class TransformationalImageHandler extends ImageHandler {
return $this->getClientScalingThumbnailImage( $image, $scalerParams );
}
- if ( !$this->isImageAreaOkForThumbnaling( $image, $params ) ) {
+ if ( $image->isTransformedLocally() && !$this->isImageAreaOkForThumbnaling( $image, $params ) ) {
global $wgMaxImageArea;
return new TransformTooBigImageAreaError( $params, $wgMaxImageArea );
}
diff --git a/includes/media/WebP.php b/includes/media/WebP.php
new file mode 100644
index 00000000..ff4dcee2
--- /dev/null
+++ b/includes/media/WebP.php
@@ -0,0 +1,306 @@
+<?php
+/**
+ * Handler for Google's WebP format <https://developers.google.com/speed/webp/>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Media
+ */
+
+/**
+ * Handler for Google's WebP format <https://developers.google.com/speed/webp/>
+ *
+ * @ingroup Media
+ */
+class WebPHandler extends BitmapHandler {
+ const BROKEN_FILE = '0'; // value to store in img_metadata if error extracting metadata.
+ /**
+ * @var int Minimum chunk header size to be able to read all header types
+ */
+ const MINIMUM_CHUNK_HEADER_LENGTH = 18;
+ /**
+ * @var int version of the metadata stored in db records
+ */
+ const _MW_WEBP_VERSION = 1;
+
+ const VP8X_ICC = 32;
+ const VP8X_ALPHA = 16;
+ const VP8X_EXIF = 8;
+ const VP8X_XMP = 4;
+ const VP8X_ANIM = 2;
+
+ public function getMetadata( $image, $filename ) {
+ $parsedWebPData = self::extractMetadata( $filename );
+ if ( !$parsedWebPData ) {
+ return self::BROKEN_FILE;
+ }
+
+ $parsedWebPData['metadata']['_MW_WEBP_VERSION'] = self::_MW_WEBP_VERSION;
+ return serialize( $parsedWebPData );
+ }
+
+ public function getMetadataType( $image ) {
+ return 'parsed-webp';
+ }
+
+ public function isMetadataValid( $image, $metadata ) {
+ if ( $metadata === self::BROKEN_FILE ) {
+ // Do not repetitivly regenerate metadata on broken file.
+ return self::METADATA_GOOD;
+ }
+
+ wfSuppressWarnings();
+ $data = unserialize( $metadata );
+ wfRestoreWarnings();
+
+ if ( !$data || !is_array( $data ) ) {
+ wfDebug( __METHOD__ . " invalid WebP metadata\n" );
+
+ return self::METADATA_BAD;
+ }
+
+ if ( !isset( $data['metadata']['_MW_WEBP_VERSION'] )
+ || $data['metadata']['_MW_WEBP_VERSION'] != self::_MW_WEBP_VERSION
+ ) {
+ wfDebug( __METHOD__ . " old but compatible WebP metadata\n" );
+
+ return self::METADATA_COMPATIBLE;
+ }
+ return self::METADATA_GOOD;
+ }
+
+ /**
+ * Extracts the image size and WebP type from a file
+ *
+ * @param string $chunks Chunks as extracted by RiffExtractor
+ * @return array|bool Header data array with entries 'compression', 'width' and 'height',
+ * where 'compression' can be 'lossy', 'lossless', 'animated' or 'unknown'. False if
+ * file is not a valid WebP file.
+ */
+ public static function extractMetadata( $filename ) {
+ wfDebugLog( 'WebP', __METHOD__ . ": Extracting metadata from $filename\n" );
+
+ $info = RiffExtractor::findChunksFromFile( $filename, 100 );
+ if ( $info === false ) {
+ wfDebugLog( 'WebP', __METHOD__ . ": Not a valid RIFF file\n" );
+ return false;
+ }
+
+ if ( $info['fourCC'] != 'WEBP' ) {
+ wfDebugLog( 'WebP', __METHOD__ . ': FourCC was not WEBP: ' .
+ bin2hex( $info['fourCC'] ) . " \n" );
+ return false;
+ }
+
+ $metadata = self::extractMetadataFromChunks( $info['chunks'], $filename );
+ if ( !$metadata ) {
+ wfDebugLog( 'WebP', __METHOD__ . ": No VP8 chunks found\n" );
+ return false;
+ }
+
+ return $metadata;
+ }
+
+ /**
+ * Extracts the image size and WebP type from a file based on the chunk list
+ * @param array $chunks Chunks as extracted by RiffExtractor
+ * @return array Header data array with entries 'compression', 'width' and 'height', where
+ * 'compression' can be 'lossy', 'lossless', 'animated' or 'unknown'
+ */
+ public static function extractMetadataFromChunks( $chunks, $filename ) {
+ $vp8Info = array();
+
+ foreach ( $chunks as $chunk ) {
+ if ( !in_array( $chunk['fourCC'], array( 'VP8 ', 'VP8L', 'VP8X' ) ) ) {
+ // Not a chunk containing interesting metadata
+ continue;
+ }
+
+ $chunkHeader = file_get_contents( $filename, false, null,
+ $chunk['start'], self::MINIMUM_CHUNK_HEADER_LENGTH );
+ wfDebugLog( 'WebP', __METHOD__ . ": {$chunk['fourCC']}\n" );
+
+ switch ( $chunk['fourCC'] ) {
+ case 'VP8 ':
+ return array_merge( $vp8Info,
+ self::decodeLossyChunkHeader( $chunkHeader ) );
+ case 'VP8L':
+ return array_merge( $vp8Info,
+ self::decodeLosslessChunkHeader( $chunkHeader ) );
+ case 'VP8X':
+ $vp8Info = array_merge( $vp8Info,
+ self::decodeExtendedChunkHeader( $chunkHeader ) );
+ // Continue looking for other chunks to improve the metadata
+ break;
+ }
+ }
+ return $vp8Info;
+ }
+
+ /**
+ * Decodes a lossy chunk header
+ * @param string $header Header string
+ * @return boolean|array See WebPHandler::decodeHeader
+ */
+ protected static function decodeLossyChunkHeader( $header ) {
+ // Bytes 0-3 are 'VP8 '
+ // Bytes 4-7 are the VP8 stream size
+ // Bytes 8-10 are the frame tag
+ // Bytes 11-13 are 0x9D 0x01 0x2A called the sync code
+ $syncCode = substr( $header, 11, 3 );
+ if ( $syncCode != "\x9D\x01\x2A" ) {
+ wfDebugLog( 'WebP', __METHOD__ . ': Invalid sync code: ' .
+ bin2hex( $syncCode ) . "\n" );
+ return array();
+ }
+ // Bytes 14-17 are image size
+ $imageSize = unpack( 'v2', substr( $header, 14, 4 ) );
+ // Image sizes are 14 bit, 2 MSB are scaling parameters which are ignored here
+ return array(
+ 'compression' => 'lossy',
+ 'width' => $imageSize[1] & 0x3FFF,
+ 'height' => $imageSize[2] & 0x3FFF
+ );
+ }
+
+ /**
+ * Decodes a lossless chunk header
+ * @param string $header Header string
+ * @return boolean|array See WebPHandler::decodeHeader
+ */
+ public static function decodeLosslessChunkHeader( $header ) {
+ // Bytes 0-3 are 'VP8L'
+ // Bytes 4-7 are chunk stream size
+ // Byte 8 is 0x2F called the signature
+ if ( $header{8} != "\x2F" ) {
+ wfDebugLog( 'WebP', __METHOD__ . ': Invalid signature: ' .
+ bin2hex( $header{8} ) . "\n" );
+ return array();
+ }
+ // Bytes 9-12 contain the image size
+ // Bits 0-13 are width-1; bits 15-27 are height-1
+ $imageSize = unpack( 'C4', substr( $header, 9, 4 ) );
+ return array(
+ 'compression' => 'lossless',
+ 'width' => ( $imageSize[1] | ( ( $imageSize[2] & 0x3F ) << 8 ) ) + 1,
+ 'height' => ( ( ( $imageSize[2] & 0xC0 ) >> 6 ) |
+ ( $imageSize[3] << 2 ) | ( ( $imageSize[4] & 0x03 ) << 10 ) ) + 1
+ );
+ }
+
+ /**
+ * Decodes an extended chunk header
+ * @param string $header Header string
+ * @return boolean|array See WebPHandler::decodeHeader
+ */
+ public static function decodeExtendedChunkHeader( $header ) {
+ // Bytes 0-3 are 'VP8X'
+ // Byte 4-7 are chunk length
+ // Byte 8-11 are a flag bytes
+ $flags = unpack( 'c', substr( $header, 8, 1 ) );
+
+ // Byte 12-17 are image size (24 bits)
+ $width = unpack( 'V', substr( $header, 12, 3 ) . "\x00" );
+ $height = unpack( 'V', substr( $header, 15, 3 ) . "\x00" );
+
+ return array(
+ 'compression' => 'unknown',
+ 'animated' => ( $flags[1] & self::VP8X_ANIM ) == self::VP8X_ANIM,
+ 'transparency' => ( $flags[1] & self::VP8X_ALPHA ) == self::VP8X_ALPHA,
+ 'width' => ( $width[1] & 0xFFFFFF ) + 1,
+ 'height' => ( $height[1] & 0xFFFFFF ) + 1
+ );
+ }
+
+ public function getImageSize( $file, $path, $metadata = false ) {
+ if ( $file === null ) {
+ $metadata = self::getMetadata( $file, $path );
+ }
+ if ( $metadata === false ) {
+ $metadata = $file->getMetadata();
+ }
+
+ wfSuppressWarnings();
+ $metadata = unserialize( $metadata );
+ wfRestoreWarnings();
+
+ if ( $metadata == false ) {
+ return false;
+ }
+ return array( $metadata['width'], $metadata['height'] );
+ }
+
+ /**
+ * @param $file
+ * @return bool True, not all browsers support WebP
+ */
+ public function mustRender( $file ) {
+ return true;
+ }
+
+ /**
+ * @param $file
+ * @return bool False if we are unable to render this image
+ */
+ public function canRender( $file ) {
+ if ( self::isAnimatedImage( $file ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param File $image
+ * @return bool
+ */
+ public function isAnimatedImage( $image ) {
+ $ser = $image->getMetadata();
+ if ( $ser ) {
+ $metadata = unserialize( $ser );
+ if ( isset( $metadata['animated'] ) && $metadata['animated'] === true ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function canAnimateThumbnail( $file ) {
+ return false;
+ }
+
+ /**
+ * Render files as PNG
+ *
+ * @param $ext
+ * @param $mime
+ * @param $params
+ * @return array
+ */
+ public function getThumbType( $ext, $mime, $params = null ) {
+ return array( 'png', 'image/png' );
+ }
+
+ /**
+ * Must use "im" for XCF
+ *
+ * @return string
+ */
+ protected function getScalerType( $dstPath, $checkDstPath = true ) {
+ return 'im';
+ }
+}
diff --git a/includes/media/XCF.php b/includes/media/XCF.php
index 6544d5cf..16e11dc4 100644
--- a/includes/media/XCF.php
+++ b/includes/media/XCF.php
@@ -3,7 +3,7 @@
* Handler for the Gimp's native file format (XCF)
*
* Overview:
- * http://en.wikipedia.org/wiki/XCF_(file_format)
+ * https://en.wikipedia.org/wiki/XCF_(file_format)
* Specification in Gnome repository:
* http://svn.gnome.org/viewvc/gimp/trunk/devel-docs/xcf.txt?view=markup
*
@@ -222,9 +222,9 @@ class XCFHandler extends BitmapHandler {
* @return bool
*/
public function canRender( $file ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$xcfMeta = unserialize( $file->getMetadata() );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( isset( $xcfMeta['colorType'] ) && $xcfMeta['colorType'] === 'index-coloured' ) {
return false;
}
diff --git a/includes/media/XMP.php b/includes/media/XMP.php
index 50f04ae9..64a7e8a8 100644
--- a/includes/media/XMP.php
+++ b/includes/media/XMP.php
@@ -21,6 +21,10 @@
* @ingroup Media
*/
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
/**
* Class for reading xmp data containing properties relevant to
* images, and spitting out an array that FormatMetadata accepts.
@@ -46,7 +50,7 @@
* read rdf.
*
*/
-class XMPReader {
+class XMPReader implements LoggerAwareInterface {
/** @var array XMP item configuration array */
protected $items;
@@ -121,15 +125,25 @@ class XMPReader {
const PARSABLE_NO = 3;
/**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ /**
* Constructor.
*
* Primary job is to initialize the XMLParser
*/
- function __construct() {
+ function __construct( LoggerInterface $logger = null ) {
if ( !function_exists( 'xml_parser_create_ns' ) ) {
// this should already be checked by this point
- throw new MWException( 'XMP support requires XML Parser' );
+ throw new RuntimeException( 'XMP support requires XML Parser' );
+ }
+ if ( $logger ) {
+ $this->setLogger( $logger );
+ } else {
+ $this->setLogger( new NullLogger() );
}
$this->items = XMPInfo::getItems();
@@ -137,16 +151,31 @@ class XMPReader {
$this->resetXMLParser();
}
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * free the XML parser.
+ *
+ * @note It is unclear to me if we really need to do this ourselves
+ * or if php garbage collection will automatically free the xmlParser
+ * when it is no longer needed.
+ */
+ private function destroyXMLParser() {
+ if ( $this->xmlParser ) {
+ xml_parser_free( $this->xmlParser );
+ $this->xmlParser = null;
+ }
+ }
+
/**
* 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 ) {
- //is this needed?
- xml_parser_free( $this->xmlParser );
- }
+ $this->destroyXMLParser();
$this->xmlParser = xml_parser_create_ns( 'UTF-8', ' ' );
xml_parser_set_option( $this->xmlParser, XML_OPTION_CASE_FOLDING, 0 );
@@ -162,15 +191,6 @@ class XMPReader {
$this->xmlParsableBuffer = '';
}
- /** Destroy the xml parser
- *
- * Not sure if this is actually needed.
- */
- function __destruct() {
- // not sure if this is needed.
- xml_parser_free( $this->xmlParser );
- }
-
/**
* Check if this instance supports using this class
*/
@@ -195,8 +215,6 @@ class XMPReader {
$data = $this->results;
- Hooks::run( 'XMPGetResults', array( &$data ) );
-
if ( isset( $data['xmp-special']['AuthorsPosition'] )
&& is_string( $data['xmp-special']['AuthorsPosition'] )
&& isset( $data['xmp-general']['Artist'][0] )
@@ -278,12 +296,11 @@ class XMPReader {
*
* @param string $content XMP data
* @param bool $allOfIt If this is all the data (true) or if its split up (false). Default true
- * @param bool $reset Does xml parser need to be reset. Default false
- * @throws MWException
+ * @throws RuntimeException
* @return bool Success.
*/
- public function parse( $content, $allOfIt = true, $reset = false ) {
- if ( $reset ) {
+ public function parse( $content, $allOfIt = true ) {
+ if ( !$this->xmlParser ) {
$this->resetXMLParser();
}
try {
@@ -313,7 +330,7 @@ class XMPReader {
break;
default:
//this should be impossible to get to
- throw new MWException( "Invalid BOM" );
+ throw new RuntimeException( "Invalid BOM" );
}
} else {
// standard specifically says, if no bom assume utf-8
@@ -322,16 +339,16 @@ class XMPReader {
}
if ( $this->charset !== 'UTF-8' ) {
//don't convert if already utf-8
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$content = iconv( $this->charset, 'UTF-8//IGNORE', $content );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
}
// Ensure the XMP block does not have an xml doctype declaration, which
// could declare entities unsafe to parse with xml_parse (T85848/T71210).
if ( $this->parsable !== self::PARSABLE_OK ) {
if ( $this->parsable === self::PARSABLE_NO ) {
- throw new Exception( 'Unsafe doctype declaration in XML.' );
+ throw new RuntimeException( 'Unsafe doctype declaration in XML.' );
}
$content = $this->xmlParsableBuffer . $content;
@@ -344,27 +361,49 @@ class XMPReader {
$msg = ( $this->parsable === self::PARSABLE_NO ) ?
'Unsafe doctype declaration in XML.' :
'No root element found in XML.';
- throw new Exception( $msg );
+ throw new RuntimeException( $msg );
}
}
$ok = xml_parse( $this->xmlParser, $content, $allOfIt );
if ( !$ok ) {
- $error = xml_error_string( xml_get_error_code( $this->xmlParser ) );
- $where = 'line: ' . xml_get_current_line_number( $this->xmlParser )
- . ' column: ' . xml_get_current_column_number( $this->xmlParser )
- . ' byte offset: ' . xml_get_current_byte_index( $this->xmlParser );
-
- wfDebugLog( 'XMP', "XMPReader::parse : Error reading XMP content: $error ($where)" );
+ $code = xml_get_error_code( $this->xmlParser );
+ $error = xml_error_string( $code );
+ $line = xml_get_current_line_number( $this->xmlParser );
+ $col = xml_get_current_column_number( $this->xmlParser );
+ $offset = xml_get_current_byte_index( $this->xmlParser );
+
+ $this->logger->warning(
+ '{method} : Error reading XMP content: {error} ' .
+ '(line: {line} column: {column} byte offset: {offset})',
+ array(
+ 'method' => __METHOD__,
+ 'error_code' => $code,
+ 'error' => $error,
+ 'line' => $line,
+ 'column' => $col,
+ 'offset' => $offset,
+ 'content' => $content,
+ ) );
$this->results = array(); // blank if error.
+ $this->destroyXMLParser();
return false;
}
} catch ( Exception $e ) {
- wfDebugLog( 'XMP', 'XMP parse error: ' . $e );
+ $this->logger->warning(
+ '{method} Exception caught while parsing: ' . $e->getMessage(),
+ array(
+ 'method' => __METHOD__,
+ 'exception' => $e,
+ 'content' => $content,
+ )
+ );
$this->results = array();
-
return false;
}
+ if ( $allOfIt ) {
+ $this->destroyXMLParser();
+ }
return true;
}
@@ -383,7 +422,7 @@ class XMPReader {
if ( !isset( $this->results['xmp-special']['HasExtendedXMP'] )
|| $this->results['xmp-special']['HasExtendedXMP'] !== $guid
) {
- wfDebugLog( 'XMP', __METHOD__ .
+ $this->logger->info( __METHOD__ .
" Ignoring XMPExtended block due to wrong guid (guid= '$guid')" );
return false;
@@ -391,7 +430,7 @@ class XMPReader {
$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.' );
+ $this->logger->info( __METHOD__ . 'Error reading extended XMP block, invalid length or offset.' );
return false;
}
@@ -408,7 +447,7 @@ class XMPReader {
// > 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 '
+ $this->logger->info( __METHOD__ . 'Ignoring XMPExtended block due to wrong order. (Offset was '
. $len['offset'] . ' but expected ' . $this->extendedXMPOffset . ')' );
return false;
@@ -430,7 +469,7 @@ class XMPReader {
$atEnd = false;
}
- wfDebugLog( 'XMP', __METHOD__ . 'Parsing a XMPExtended block' );
+ $this->logger->debug( __METHOD__ . 'Parsing a XMPExtended block' );
return $this->parse( $actualContent, $atEnd );
}
@@ -449,7 +488,7 @@ class XMPReader {
*
* @param XMLParser $parser XMLParser reference to the xml parser
* @param string $data Character data
- * @throws MWException On invalid data
+ * @throws RuntimeException On invalid data
*/
function char( $parser, $data ) {
@@ -459,7 +498,7 @@ class XMPReader {
}
if ( !isset( $this->mode[0] ) ) {
- throw new MWException( 'Unexpected character data before first rdf:Description element' );
+ throw new RuntimeException( 'Unexpected character data before first rdf:Description element' );
}
if ( $this->mode[0] === self::MODE_IGNORE ) {
@@ -469,7 +508,7 @@ class XMPReader {
if ( $this->mode[0] !== self::MODE_SIMPLE
&& $this->mode[0] !== self::MODE_QDESC
) {
- throw new MWException( 'character data where not expected. (mode ' . $this->mode[0] . ')' );
+ throw new RuntimeException( 'character data where not expected. (mode ' . $this->mode[0] . ')' );
}
// to check, how does this handle w.s.
@@ -501,6 +540,7 @@ class XMPReader {
);
$oldDisable = libxml_disable_entity_loader( true );
+ /** @noinspection PhpUnusedLocalVariableInspection */
$reset = new ScopedCallback(
'libxml_disable_entity_loader',
array( $oldDisable )
@@ -509,7 +549,7 @@ class XMPReader {
// Even with LIBXML_NOWARNING set, XMLReader::read gives a warning
// when parsing truncated XML, which causes unit tests to fail.
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
while ( $reader->read() ) {
if ( $reader->nodeType === XMLReader::ELEMENT ) {
// Reached the first element without hitting a doctype declaration
@@ -523,7 +563,7 @@ class XMPReader {
break;
}
}
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !is_null( $result ) ) {
return $result;
@@ -597,7 +637,7 @@ class XMPReader {
* This method is called when we hit the "</exif:ISOSpeedRatings>" tag.
*
* @param string $elm Namespace . space . tag name.
- * @throws MWException
+ * @throws RuntimeException
*/
private function endElementNested( $elm ) {
@@ -607,35 +647,38 @@ class XMPReader {
&& !( $elm === self::NS_RDF . ' Description'
&& $this->mode[0] === self::MODE_STRUCT )
) {
- throw new MWException( "nesting mismatch. got a </$elm> but expected a </" .
+ throw new RuntimeException( "nesting mismatch. got a </$elm> but expected a </" .
$this->curItem[0] . '>' );
}
// Validate structures.
list( $ns, $tag ) = explode( ' ', $elm, 2 );
if ( isset( $this->items[$ns][$tag]['validate'] ) ) {
-
$info =& $this->items[$ns][$tag];
$finalName = isset( $info['map_name'] )
? $info['map_name'] : $tag;
- $validate = is_array( $info['validate'] ) ? $info['validate']
- : array( 'XMPValidate', $info['validate'] );
+ if ( is_array( $info['validate'] ) ) {
+ $validate = $info['validate'];
+ } else {
+ $validator = new XMPValidate( $this->logger );
+ $validate = array( $validator, $info['validate'] );
+ }
if ( !isset( $this->results['xmp-' . $info['map_group']][$finalName] ) ) {
// This can happen if all the members of the struct failed validation.
- wfDebugLog( 'XMP', __METHOD__ . " <$ns:$tag> has no valid members." );
+ $this->logger->debug( __METHOD__ . " <$ns:$tag> has no valid members." );
} elseif ( is_callable( $validate ) ) {
$val =& $this->results['xmp-' . $info['map_group']][$finalName];
call_user_func_array( $validate, array( $info, &$val, false ) );
if ( is_null( $val ) ) {
// the idea being the validation function will unset the variable if
// its invalid.
- wfDebugLog( 'XMP', __METHOD__ . " <$ns:$tag> failed validation." );
+ $this->logger->info( __METHOD__ . " <$ns:$tag> failed validation." );
unset( $this->results['xmp-' . $info['map_group']][$finalName] );
}
} else {
- wfDebugLog( 'XMP', __METHOD__ . " Validation function for $finalName ("
+ $this->logger->warning( __METHOD__ . " Validation function for $finalName ("
. $validate[0] . '::' . $validate[1] . '()) is not callable.' );
}
}
@@ -664,7 +707,7 @@ class XMPReader {
* hit the "</rdf:li>")
*
* @param string $elm Namespace . ' ' . element name
- * @throws MWException
+ * @throws RuntimeException
*/
private function endElementModeLi( $elm ) {
@@ -676,7 +719,7 @@ class XMPReader {
array_shift( $this->mode );
if ( !isset( $this->results['xmp-' . $info['map_group']][$finalName] ) ) {
- wfDebugLog( 'XMP', __METHOD__ . " Empty compund element $finalName." );
+ $this->logger->debug( __METHOD__ . " Empty compund element $finalName." );
return;
}
@@ -691,7 +734,7 @@ class XMPReader {
$this->results['xmp-' . $info['map_group']][$finalName]['_type'] = 'lang';
}
} else {
- throw new MWException( __METHOD__ . " expected </rdf:seq> or </rdf:bag> but instead got $elm." );
+ throw new RuntimeException( __METHOD__ . " expected </rdf:seq> or </rdf:bag> but instead got $elm." );
}
}
@@ -729,7 +772,7 @@ class XMPReader {
*
* @param XMLParser $parser
* @param string $elm Namespace . ' ' . element name
- * @throws MWException
+ * @throws RuntimeException
*/
function endElement( $parser, $elm ) {
if ( $elm === ( self::NS_RDF . ' RDF' )
@@ -743,7 +786,7 @@ class XMPReader {
if ( $elm === self::NS_RDF . ' type' ) {
// these aren't really supported properly yet.
// However, it appears they almost never used.
- wfDebugLog( 'XMP', __METHOD__ . ' encountered <rdf:type>' );
+ $this->logger->info( __METHOD__ . ' encountered <rdf:type>' );
}
if ( strpos( $elm, ' ' ) === false ) {
@@ -751,7 +794,7 @@ class XMPReader {
// However, there is a bug in an adobe product
// that forgets the namespace on some things.
// (Luckily they are unimportant things).
- wfDebugLog( 'XMP', __METHOD__ . " Encountered </$elm> which has no namespace. Skipping." );
+ $this->logger->info( __METHOD__ . " Encountered </$elm> which has no namespace. Skipping." );
return;
}
@@ -759,13 +802,13 @@ class XMPReader {
if ( count( $this->mode[0] ) === 0 ) {
// This should never ever happen and means
// there is a pretty major bug in this class.
- throw new MWException( 'Encountered end element with no mode' );
+ throw new RuntimeException( 'Encountered end element with no mode' );
}
if ( count( $this->curItem ) == 0 && $this->mode[0] !== self::MODE_INITIAL ) {
// just to be paranoid. Should always have a curItem, except for initially
// (aka during MODE_INITAL).
- throw new MWException( "Hit end element </$elm> but no curItem" );
+ throw new RuntimeException( "Hit end element </$elm> but no curItem" );
}
switch ( $this->mode[0] ) {
@@ -786,7 +829,7 @@ class XMPReader {
if ( $elm === self::NS_RDF . ' Description' ) {
array_shift( $this->mode );
} else {
- throw new MWException( 'Element ended unexpectedly while in MODE_INITIAL' );
+ throw new RuntimeException( 'Element ended unexpectedly while in MODE_INITIAL' );
}
break;
case self::MODE_LI:
@@ -797,7 +840,7 @@ class XMPReader {
$this->endElementModeQDesc( $elm );
break;
default:
- wfDebugLog( 'XMP', __METHOD__ . " no mode (elm = $elm)" );
+ $this->logger->warning( __METHOD__ . " no mode (elm = $elm)" );
break;
}
}
@@ -825,13 +868,13 @@ class XMPReader {
* this should always be <rdf:Bag>
*
* @param string $elm Namespace . ' ' . tag
- * @throws MWException If we have an element that's not <rdf:Bag>
+ * @throws RuntimeException 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 );
} else {
- throw new MWException( "Expected <rdf:Bag> but got $elm." );
+ throw new RuntimeException( "Expected <rdf:Bag> but got $elm." );
}
}
@@ -840,18 +883,18 @@ class XMPReader {
* this should always be <rdf:Seq>
*
* @param string $elm Namespace . ' ' . tag
- * @throws MWException If we have an element that's not <rdf:Seq>
+ * @throws RuntimeException 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 );
} elseif ( $elm === self::NS_RDF . ' Bag' ) {
# bug 27105
- wfDebugLog( 'XMP', __METHOD__ . ' Expected an rdf:Seq, but got an rdf:Bag. Pretending'
+ $this->logger->info( __METHOD__ . ' Expected an rdf:Seq, but got an rdf:Bag. Pretending'
. ' it is a Seq, since some buggy software is known to screw this up.' );
array_unshift( $this->mode, self::MODE_LI );
} else {
- throw new MWException( "Expected <rdf:Seq> but got $elm." );
+ throw new RuntimeException( "Expected <rdf:Seq> but got $elm." );
}
}
@@ -867,13 +910,13 @@ class XMPReader {
* we don't care about.
*
* @param string $elm Namespace . ' ' . tag
- * @throws MWException If we have an element that's not <rdf:Alt>
+ * @throws RuntimeException 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 );
} else {
- throw new MWException( "Expected <rdf:Seq> but got $elm." );
+ throw new RuntimeException( "Expected <rdf:Seq> but got $elm." );
}
}
@@ -893,7 +936,7 @@ class XMPReader {
*
* @param string $elm Namespace and tag names separated by space.
* @param array $attribs Attributes of the element.
- * @throws MWException
+ * @throws RuntimeException
*/
private function startElementModeSimple( $elm, $attribs ) {
if ( $elm === self::NS_RDF . ' Description' ) {
@@ -907,10 +950,10 @@ class XMPReader {
}
} elseif ( $elm === self::NS_RDF . ' value' ) {
// This should not be here.
- throw new MWException( __METHOD__ . ' Encountered <rdf:value> where it was unexpected.' );
+ throw new RuntimeException( __METHOD__ . ' Encountered <rdf:value> where it was unexpected.' );
} else {
// something else we don't recognize, like a qualifier maybe.
- wfDebugLog( 'XMP', __METHOD__ .
+ $this->logger->info( __METHOD__ .
" Encountered element <$elm> where only expecting character data as value of " .
$this->curItem[0] );
array_unshift( $this->mode, self::MODE_IGNORE );
@@ -952,7 +995,7 @@ class XMPReader {
* @param string $ns Namespace
* @param string $tag Tag name (without namespace prefix)
* @param array $attribs Array of attributes
- * @throws MWException
+ * @throws RuntimeException
*/
private function startElementModeInitial( $ns, $tag, $attribs ) {
if ( $ns !== self::NS_RDF ) {
@@ -964,7 +1007,7 @@ class XMPReader {
// a child of a struct), then something weird is
// happening, so ignore this element and its children.
- wfDebugLog( 'XMP', "Encountered <$ns:$tag> outside"
+ $this->logger->warning( "Encountered <$ns:$tag> outside"
. " of its expected parent. Ignoring." );
array_unshift( $this->mode, self::MODE_IGNORE );
@@ -982,11 +1025,11 @@ class XMPReader {
if ( $this->charContent !== false ) {
// Something weird.
// Should not happen in valid XMP.
- throw new MWException( 'tag nested in non-whitespace characters.' );
+ throw new RuntimeException( 'tag nested in non-whitespace characters.' );
}
} else {
// This element is not on our list of allowed elements so ignore.
- wfDebugLog( 'XMP', __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." );
+ $this->logger->debug( __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." );
array_unshift( $this->mode, self::MODE_IGNORE );
array_unshift( $this->curItem, $ns . ' ' . $tag );
@@ -1014,7 +1057,7 @@ class XMPReader {
* @param string $ns Namespace
* @param string $tag Tag name (no ns)
* @param array $attribs Array of attribs w/ values.
- * @throws MWException
+ * @throws RuntimeException
*/
private function startElementModeStruct( $ns, $tag, $attribs ) {
if ( $ns !== self::NS_RDF ) {
@@ -1025,7 +1068,7 @@ class XMPReader {
) {
// This assumes that we don't have inter-namespace nesting
// which we don't in all the properties we're interested in.
- throw new MWException( " <$tag> appeared nested in <" . $this->ancestorStruct
+ throw new RuntimeException( " <$tag> appeared nested in <" . $this->ancestorStruct
. "> where it is not allowed." );
}
array_unshift( $this->mode, $this->items[$ns][$tag]['mode'] );
@@ -1033,7 +1076,7 @@ class XMPReader {
if ( $this->charContent !== false ) {
// Something weird.
// Should not happen in valid XMP.
- throw new MWException( "tag <$tag> nested in non-whitespace characters (" .
+ throw new RuntimeException( "tag <$tag> nested in non-whitespace characters (" .
$this->charContent . ")." );
}
} else {
@@ -1062,17 +1105,17 @@ class XMPReader {
*
* @param string $elm Namespace . ' ' . tagname
* @param array $attribs Attributes. (needed for BAGSTRUCTS)
- * @throws MWException If gets a tag other than <rdf:li>
+ * @throws RuntimeException 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." );
+ throw new RuntimeException( "<rdf:li> expected but got $elm." );
}
if ( !isset( $this->mode[1] ) ) {
// This should never ever ever happen. Checking for it
// to be paranoid.
- throw new MWException( 'In mode Li, but no 2xPrevious mode!' );
+ throw new RuntimeException( 'In mode Li, but no 2xPrevious mode!' );
}
if ( $this->mode[1] === self::MODE_BAGSTRUCT ) {
@@ -1083,7 +1126,7 @@ class XMPReader {
if ( !isset( $this->curItem[1] ) ) {
// be paranoid.
- throw new MWException( 'Can not find parent of BAGSTRUCT.' );
+ throw new RuntimeException( 'Can not find parent of BAGSTRUCT.' );
}
list( $curNS, $curTag ) = explode( ' ', $this->curItem[1] );
$this->ancestorStruct = isset( $this->items[$curNS][$curTag]['map_name'] )
@@ -1112,16 +1155,16 @@ class XMPReader {
*
* @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
+ * @throws RuntimeException 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." );
+ throw new RuntimeException( __METHOD__ . " <rdf:li> expected but got $elm." );
}
if ( !isset( $attribs[self::NS_XML . ' lang'] )
|| !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $attribs[self::NS_XML . ' lang'] )
) {
- throw new MWException( __METHOD__
+ throw new RuntimeException( __METHOD__
. " <rdf:li> did not contain, or has invalid xml:lang attribute in lang alternative" );
}
@@ -1143,7 +1186,7 @@ class XMPReader {
* @param XMLParser $parser
* @param string $elm Namespace "<space>" element
* @param array $attribs Attribute name => value
- * @throws MWException
+ * @throws RuntimeException
*/
function startElement( $parser, $elm, $attribs ) {
@@ -1166,12 +1209,12 @@ class XMPReader {
//
// also it seems as if exiv2 and exiftool do not support
// this either (That or I misunderstand the standard)
- wfDebugLog( 'XMP', __METHOD__ . ' Encountered <rdf:type> which isn\'t currently supported' );
+ $this->logger->info( __METHOD__ . ' Encountered <rdf:type> which isn\'t currently supported' );
}
if ( strpos( $elm, ' ' ) === false ) {
// This probably shouldn't happen.
- wfDebugLog( 'XMP', __METHOD__ . " Encountered <$elm> which has no namespace. Skipping." );
+ $this->logger->info( __METHOD__ . " Encountered <$elm> which has no namespace. Skipping." );
return;
}
@@ -1180,7 +1223,7 @@ class XMPReader {
if ( count( $this->mode ) === 0 ) {
// This should not happen.
- throw new MWException( 'Error extracting XMP, '
+ throw new RuntimeException( 'Error extracting XMP, '
. "encountered <$elm> with no mode" );
}
@@ -1217,7 +1260,7 @@ class XMPReader {
$this->startElementModeQDesc( $elm );
break;
default:
- throw new MWException( 'StartElement in unknown mode: ' . $this->mode[0] );
+ throw new RuntimeException( 'StartElement in unknown mode: ' . $this->mode[0] );
}
}
@@ -1236,7 +1279,7 @@ class XMPReader {
* @codingStandardsIgnoreEnd
*
* @param array $attribs Array attribute=>value
- * @throws MWException
+ * @throws RuntimeException
*/
private function doAttribs( $attribs ) {
// first check for rdf:parseType attribute, as that can change
@@ -1253,7 +1296,7 @@ class XMPReader {
if ( strpos( $name, ' ' ) === false ) {
// This shouldn't happen, but so far some old software forgets namespace
// on rdf:about.
- wfDebugLog( 'XMP', __METHOD__ . ' Encountered non-namespaced attribute: '
+ $this->logger->info( __METHOD__ . ' Encountered non-namespaced attribute: '
. " $name=\"$val\". Skipping. " );
continue;
}
@@ -1266,12 +1309,12 @@ class XMPReader {
}
} elseif ( isset( $this->items[$ns][$tag] ) ) {
if ( $this->mode[0] === self::MODE_SIMPLE ) {
- throw new MWException( __METHOD__
+ throw new RuntimeException( __METHOD__
. " $ns:$tag found as attribute where not allowed" );
}
$this->saveValue( $ns, $tag, $val );
} else {
- wfDebugLog( 'XMP', __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." );
+ $this->logger->debug( __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." );
}
}
}
@@ -1293,20 +1336,24 @@ class XMPReader {
$finalName = isset( $info['map_name'] )
? $info['map_name'] : $tag;
if ( isset( $info['validate'] ) ) {
- $validate = is_array( $info['validate'] ) ? $info['validate']
- : array( 'XMPValidate', $info['validate'] );
+ if ( is_array( $info['validate'] ) ) {
+ $validate = $info['validate'];
+ } else {
+ $validator = new XMPValidate( $this->logger );
+ $validate = array( $validator, $info['validate'] );
+ }
if ( is_callable( $validate ) ) {
call_user_func_array( $validate, array( $info, &$val, true ) );
// the reasoning behind using &$val instead of using the return value
// is to be consistent between here and validating structures.
if ( is_null( $val ) ) {
- wfDebugLog( 'XMP', __METHOD__ . " <$ns:$tag> failed validation." );
+ $this->logger->info( __METHOD__ . " <$ns:$tag> failed validation." );
return;
}
} else {
- wfDebugLog( 'XMP', __METHOD__ . " Validation function for $finalName ("
+ $this->logger->warning( __METHOD__ . " Validation function for $finalName ("
. $validate[0] . '::' . $validate[1] . '()) is not callable.' );
}
}
diff --git a/includes/media/XMPInfo.php b/includes/media/XMPInfo.php
index e0a491cb..1d8d7771 100644
--- a/includes/media/XMPInfo.php
+++ b/includes/media/XMPInfo.php
@@ -31,18 +31,9 @@ class XMPInfo {
* @return array XMP item configuration array.
*/
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.
- Hooks::run( 'XMPGetInfo', array( &self::$items ) );
- self::$ranHooks = true; // Only want to do this once.
- }
-
return self::$items;
}
- static private $ranHooks = false;
-
/**
* XMPInfo::$items keeps a list of all the items
* we are interested to extract, as well as
@@ -57,7 +48,7 @@ class XMPInfo {
* * 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
+ * input. A string value is assumed to be a method of
* XMPValidate. Can also take a array( 'className', 'methodName' ).
* * choices - Array of potential values (format of 'value' => true ).
* Only used with validateClosed.
diff --git a/includes/media/XMPValidate.php b/includes/media/XMPValidate.php
index 0fa60117..55e8ce79 100644
--- a/includes/media/XMPValidate.php
+++ b/includes/media/XMPValidate.php
@@ -21,6 +21,9 @@
* @ingroup Media
*/
+use Psr\Log\LoggerInterface;
+use Psr\Log\LoggerAwareInterface;
+
/**
* This contains some static methods for
* validating XMP properties. See XMPInfo and XMPReader classes.
@@ -40,7 +43,20 @@
* @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 {
+class XMPValidate implements LoggerAwareInterface {
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ public function __construct( LoggerInterface $logger ) {
+ $this->setLogger( $logger );
+ }
+
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
/**
* Function to validate boolean properties ( True or False )
*
@@ -48,13 +64,13 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateBoolean( $info, &$val, $standalone ) {
+ public function validateBoolean( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
}
if ( $val !== 'True' && $val !== 'False' ) {
- wfDebugLog( 'XMP', __METHOD__ . " Expected True or False but got $val" );
+ $this->debug->info( __METHOD__ . " Expected True or False but got $val" );
$val = null;
}
}
@@ -66,13 +82,13 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateRational( $info, &$val, $standalone ) {
+ public function validateRational( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
}
if ( !preg_match( '/^(?:-?\d+)\/(?:\d+[1-9]|[1-9]\d*)$/D', $val ) ) {
- wfDebugLog( 'XMP', __METHOD__ . " Expected rational but got $val" );
+ $this->logger->info( __METHOD__ . " Expected rational but got $val" );
$val = null;
}
}
@@ -87,7 +103,7 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateRating( $info, &$val, $standalone ) {
+ public function validateRating( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
@@ -95,7 +111,7 @@ class XMPValidate {
if ( !preg_match( '/^[-+]?\d*(?:\.?\d*)$/D', $val )
|| !is_numeric( $val )
) {
- wfDebugLog( 'XMP', __METHOD__ . " Expected rating but got $val" );
+ $this->logger->info( __METHOD__ . " Expected rating but got $val" );
$val = null;
return;
@@ -105,13 +121,13 @@ 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)" );
+ $this->logger->info( __METHOD__ . " Rating too low, setting to -1 (Rejected)" );
$val = '-1';
return;
}
if ( $nVal > 5 ) {
- wfDebugLog( 'XMP', __METHOD__ . " Rating too high, setting to 5" );
+ $this->logger->info( __METHOD__ . " Rating too high, setting to 5" );
$val = '5';
return;
@@ -126,13 +142,13 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateInteger( $info, &$val, $standalone ) {
+ public function validateInteger( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
}
if ( !preg_match( '/^[-+]?\d+$/D', $val ) ) {
- wfDebugLog( 'XMP', __METHOD__ . " Expected integer but got $val" );
+ $this->logger->info( __METHOD__ . " Expected integer but got $val" );
$val = null;
}
}
@@ -145,7 +161,7 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateClosed( $info, &$val, $standalone ) {
+ public function validateClosed( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
@@ -163,7 +179,7 @@ class XMPValidate {
}
if ( !isset( $info['choices'][$val] ) && !$inRange ) {
- wfDebugLog( 'XMP', __METHOD__ . " Expected closed choice, but got $val" );
+ $this->logger->info( __METHOD__ . " Expected closed choice, but got $val" );
$val = null;
}
}
@@ -175,7 +191,7 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateFlash( $info, &$val, $standalone ) {
+ public function validateFlash( $info, &$val, $standalone ) {
if ( $standalone ) {
// this only validates flash structs, not individual properties
return;
@@ -186,7 +202,7 @@ class XMPValidate {
&& isset( $val['RedEyeMode'] )
&& isset( $val['Return'] )
) ) {
- wfDebugLog( 'XMP', __METHOD__ . " Flash structure did not have all the required components" );
+ $this->logger->info( __METHOD__ . " Flash structure did not have all the required components" );
$val = null;
} else {
$val = ( "\0" | ( $val['Fired'] === 'True' )
@@ -209,14 +225,14 @@ class XMPValidate {
* @param mixed &$val Current value to validate
* @param bool $standalone If this is a simple property or array
*/
- public static function validateLangCode( $info, &$val, $standalone ) {
+ public function validateLangCode( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
}
if ( !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $val ) ) {
//this is a rather naive check.
- wfDebugLog( 'XMP', __METHOD__ . " Expected Lang code but got $val" );
+ $this->logger->info( __METHOD__ . " Expected Lang code but got $val" );
$val = null;
}
}
@@ -238,7 +254,7 @@ class XMPValidate {
* 2011:04.
* @param bool $standalone If this is a simple property or array
*/
- public static function validateDate( $info, &$val, $standalone ) {
+ public function validateDate( $info, &$val, $standalone ) {
if ( !$standalone ) {
// this only validates standalone properties, not arrays, etc
return;
@@ -252,7 +268,7 @@ class XMPValidate {
) {
// @codingStandardsIgnoreEnd
- wfDebugLog( 'XMP', __METHOD__ . " Expected date but got $val" );
+ $this->logger->info( __METHOD__ . " Expected date but got $val" );
$val = null;
} else {
/*
@@ -270,7 +286,7 @@ class XMPValidate {
* some programs convert between metadata formats.
*/
if ( $res[1] === '0000' ) {
- wfDebugLog( 'XMP', __METHOD__ . " Invalid date (year 0): $val" );
+ $this->logger->info( __METHOD__ . " Invalid date (year 0): $val" );
$val = null;
return;
@@ -339,7 +355,7 @@ class XMPValidate {
* or DDD,MM.mmk form
* @param bool $standalone If its a simple prop (should always be true)
*/
- public static function validateGPS( $info, &$val, $standalone ) {
+ public function validateGPS( $info, &$val, $standalone ) {
if ( !$standalone ) {
return;
}
@@ -371,7 +387,7 @@ class XMPValidate {
return;
} else {
- wfDebugLog( 'XMP', __METHOD__
+ $this->logger->info( __METHOD__
. " Expected GPSCoordinate, but got $val." );
$val = null;
diff --git a/includes/media/tinyrgb.icc b/includes/media/tinyrgb.icc
new file mode 100644
index 00000000..eab973f5
--- /dev/null
+++ b/includes/media/tinyrgb.icc
Binary files differ