summaryrefslogtreecommitdiff
path: root/includes/media
diff options
context:
space:
mode:
Diffstat (limited to 'includes/media')
-rw-r--r--includes/media/BMP.php31
-rw-r--r--includes/media/Bitmap.php236
-rw-r--r--includes/media/DjVu.php206
-rw-r--r--includes/media/Generic.php298
-rw-r--r--includes/media/SVG.php97
5 files changed, 868 insertions, 0 deletions
diff --git a/includes/media/BMP.php b/includes/media/BMP.php
new file mode 100644
index 00000000..9917856a
--- /dev/null
+++ b/includes/media/BMP.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Handler for Microsoft bitmap format (bmp). It inherits most of the methods
+ * from ImageHandler, some of them had to be overriden cause gd does not
+ * support this format.
+ *
+ * @addtogroup Media
+ */
+class BmpHandler extends BitmapHandler {
+
+ /*
+ * Get width and height from the bmp header.
+ */
+ function getImageSize( $image, $filename ) {
+ $f = fopen( $filename, 'r' );
+ if(!$f) return false;
+ $header = fread( $f, 54 );
+ fclose($f);
+
+ // Extract binary form of width and height from the header
+ $w = substr( $header, 18, 4);
+ $h = substr( $header, 22, 4);
+
+ // Convert the unsigned long 32 bits (little endian):
+ $w = unpack( 'V' , $w );
+ $h = unpack( 'V' , $h );
+ return array( $w[1], $h[1] );
+ }
+}
+
+?>
diff --git a/includes/media/Bitmap.php b/includes/media/Bitmap.php
new file mode 100644
index 00000000..3f3aabbf
--- /dev/null
+++ b/includes/media/Bitmap.php
@@ -0,0 +1,236 @@
+<?php
+
+/**
+ * @addtogroup Media
+ */
+class BitmapHandler extends ImageHandler {
+ function normaliseParams( $image, &$params ) {
+ global $wgMaxImageArea;
+ if ( !parent::normaliseParams( $image, $params ) ) {
+ return false;
+ }
+
+ $mimeType = $image->getMimeType();
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+
+ # Don't thumbnail an image so big that it will fill hard drives and send servers into swap
+ # JPEG has the handy property of allowing thumbnailing without full decompression, so we make
+ # an exception for it.
+ if ( $mimeType !== 'image/jpeg' &&
+ $srcWidth * $srcHeight > $wgMaxImageArea )
+ {
+ return false;
+ }
+
+ # Don't make an image bigger than the source
+ $params['physicalWidth'] = $params['width'];
+ $params['physicalHeight'] = $params['height'];
+
+ if ( $params['physicalWidth'] >= $srcWidth ) {
+ $params['physicalWidth'] = $srcWidth;
+ $params['physicalHeight'] = $srcHeight;
+ return true;
+ }
+
+ return true;
+ }
+
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ global $wgUseImageMagick, $wgImageMagickConvertCommand;
+ global $wgCustomConvertCommand;
+ global $wgSharpenParameter, $wgSharpenReductionThreshold;
+
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return new TransformParameterError( $params );
+ }
+ $physicalWidth = $params['physicalWidth'];
+ $physicalHeight = $params['physicalHeight'];
+ $clientWidth = $params['width'];
+ $clientHeight = $params['height'];
+ $srcWidth = $image->getWidth();
+ $srcHeight = $image->getHeight();
+ $mimeType = $image->getMimeType();
+ $srcPath = $image->getImagePath();
+ $retval = 0;
+ wfDebug( __METHOD__.": creating {$physicalWidth}x{$physicalHeight} thumbnail at $dstPath\n" );
+
+ if ( $physicalWidth == $srcWidth && $physicalHeight == $srcHeight ) {
+ # normaliseParams (or the user) wants us to return the unscaled image
+ wfDebug( __METHOD__.": returning unscaled image\n" );
+ return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
+ }
+
+ if ( $wgUseImageMagick ) {
+ $scaler = 'im';
+ } elseif ( $wgCustomConvertCommand ) {
+ $scaler = 'custom';
+ } elseif ( function_exists( 'imagecreatetruecolor' ) ) {
+ $scaler = 'gd';
+ } else {
+ $scaler = 'client';
+ }
+
+ if ( $scaler == 'client' ) {
+ # Client-side image scaling, use the source URL
+ # Using the destination URL in a TRANSFORM_LATER request would be incorrect
+ return new ThumbnailImage( $image->getURL(), $clientWidth, $clientHeight, $srcPath );
+ }
+
+ if ( $flags & self::TRANSFORM_LATER ) {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+
+ if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
+ wfMsg( 'thumbnail_dest_directory' ) );
+ }
+
+ if ( $scaler == 'im' ) {
+ # use ImageMagick
+
+ $sharpen = '';
+ if ( $mimeType == 'image/jpeg' ) {
+ $quality = "-quality 80"; // 80%
+ # Sharpening, see bug 6193
+ if ( ( $physicalWidth + $physicalHeight ) / ( $srcWidth + $srcHeight ) < $wgSharpenReductionThreshold ) {
+ $sharpen = "-sharpen " . wfEscapeShellArg( $wgSharpenParameter );
+ }
+ } elseif ( $mimeType == 'image/png' ) {
+ $quality = "-quality 95"; // zlib 9, adaptive filtering
+ } else {
+ $quality = ''; // default
+ }
+
+ # Specify white background color, will be used for transparent images
+ # in Internet Explorer/Windows instead of default black.
+
+ # Note, we specify "-size {$physicalWidth}" and NOT "-size {$physicalWidth}x{$physicalHeight}".
+ # It seems that ImageMagick has a bug wherein it produces thumbnails of
+ # the wrong size in the second case.
+
+ $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) .
+ " {$quality} -background white -size {$physicalWidth} ".
+ wfEscapeShellArg($srcPath) .
+ // Coalesce is needed to scale animated GIFs properly (bug 1017).
+ ' -coalesce ' .
+ // For the -resize option a "!" is needed to force exact size,
+ // or ImageMagick may decide your ratio is wrong and slice off
+ // a pixel.
+ " -thumbnail " . wfEscapeShellArg( "{$physicalWidth}x{$physicalHeight}!" ) .
+ " -depth 8 $sharpen " .
+ wfEscapeShellArg($dstPath) . " 2>&1";
+ wfDebug( __METHOD__.": running ImageMagick: $cmd\n");
+ wfProfileIn( 'convert' );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'convert' );
+ } elseif( $scaler == 'custom' ) {
+ # Use a custom convert command
+ # Variables: %s %d %w %h
+ $src = wfEscapeShellArg( $srcPath );
+ $dst = wfEscapeShellArg( $dstPath );
+ $cmd = $wgCustomConvertCommand;
+ $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
+ $cmd = str_replace( '%h', $physicalHeight, str_replace( '%w', $physicalWidth, $cmd ) ); # Size
+ wfDebug( __METHOD__.": Running custom convert command $cmd\n" );
+ wfProfileIn( 'convert' );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'convert' );
+ } else /* $scaler == 'gd' */ {
+ # Use PHP's builtin GD library functions.
+ #
+ # First find out what kind of file this is, and select the correct
+ # input routine for this.
+
+ $typemap = array(
+ 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
+ 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( __CLASS__, 'imageJpegWrapper' ) ),
+ 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
+ 'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
+ 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
+ );
+ if( !isset( $typemap[$mimeType] ) ) {
+ $err = 'Image type not supported';
+ wfDebug( "$err\n" );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ }
+ list( $loader, $colorStyle, $saveType ) = $typemap[$mimeType];
+
+ if( !function_exists( $loader ) ) {
+ $err = "Incomplete GD library configuration: missing function $loader";
+ wfDebug( "$err\n" );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ }
+
+ $src_image = call_user_func( $loader, $srcPath );
+ $dst_image = imagecreatetruecolor( $physicalWidth, $physicalHeight );
+ imagecopyresampled( $dst_image, $src_image,
+ 0,0,0,0,
+ $physicalWidth, $physicalHeight, imagesx( $src_image ), imagesy( $src_image ) );
+ call_user_func( $saveType, $dst_image, $dstPath );
+ imagedestroy( $dst_image );
+ imagedestroy( $src_image );
+ $retval = 0;
+ }
+
+ $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 ) );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ } else {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+ }
+
+ static function imageJpegWrapper( $dst_image, $thumbPath ) {
+ imageinterlace( $dst_image );
+ imagejpeg( $dst_image, $thumbPath, 95 );
+ }
+
+
+ function getMetadata( $image, $filename ) {
+ global $wgShowEXIF;
+ if( $wgShowEXIF && file_exists( $filename ) ) {
+ $exif = new Exif( $filename );
+ $data = $exif->getFilteredData();
+ if ( $data ) {
+ $data['MEDIAWIKI_EXIF_VERSION'] = Exif::version();
+ return serialize( $data );
+ } else {
+ return '0';
+ }
+ } else {
+ return '';
+ }
+ }
+
+ function getMetadataType( $image ) {
+ return 'exif';
+ }
+
+ function isMetadataValid( $image, $metadata ) {
+ global $wgShowEXIF;
+ if ( !$wgShowEXIF ) {
+ # Metadata disabled and so an empty field is expected
+ return true;
+ }
+ if ( $metadata === '0' ) {
+ # Special value indicating that there is no EXIF data in the file
+ return true;
+ }
+ $exif = @unserialize( $metadata );
+ if ( !isset( $exif['MEDIAWIKI_EXIF_VERSION'] ) ||
+ $exif['MEDIAWIKI_EXIF_VERSION'] != Exif::version() )
+ {
+ # Wrong version
+ wfDebug( __METHOD__.": wrong version\n" );
+ return false;
+ }
+ return true;
+ }
+
+}
+
+?>
diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php
new file mode 100644
index 00000000..3c053a0c
--- /dev/null
+++ b/includes/media/DjVu.php
@@ -0,0 +1,206 @@
+<?php
+
+/**
+ * @addtogroup Media
+ */
+class DjVuHandler extends ImageHandler {
+ function isEnabled() {
+ global $wgDjvuRenderer, $wgDjvuDump, $wgDjvuToXML;
+ if ( !$wgDjvuRenderer || ( !$wgDjvuDump && !$wgDjvuToXML ) ) {
+ wfDebug( "DjVu is disabled, please set \$wgDjvuRenderer and \$wgDjvuDump\n" );
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ function mustRender() { return true; }
+ function isMultiPage() { return true; }
+
+ function validateParam( $name, $value ) {
+ if ( in_array( $name, array( 'width', 'height', 'page' ) ) ) {
+ if ( $value <= 0 ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ function makeParamString( $params ) {
+ $page = isset( $params['page'] ) ? $params['page'] : 1;
+ if ( !isset( $params['width'] ) ) {
+ return false;
+ }
+ return "page{$page}-{$params['width']}px";
+ }
+
+ function parseParamString( $str ) {
+ $m = false;
+ if ( preg_match( '/^page(\d+)-(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => $m[2], 'page' => $m[1] );
+ } else {
+ return false;
+ }
+ }
+
+ function getScriptParams( $params ) {
+ return array(
+ 'width' => $params['width'],
+ 'page' => $params['page'],
+ );
+ }
+
+ 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 ) {
+ return new MediaTransformError( 'thumbnail_error', @$params['width'], @$params['height'],
+ wfMsg( 'djvu_no_xml' ) );
+ }
+
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return new TransformParameterError( $params );
+ }
+ $width = $params['width'];
+ $height = $params['height'];
+ $srcPath = $image->getImagePath();
+ $page = $params['page'];
+ $pageCount = $this->pageCount( $image );
+ if ( $page > $this->pageCount( $image ) ) {
+ return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'djvu_page_error' ) );
+ }
+
+ if ( $flags & self::TRANSFORM_LATER ) {
+ return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
+ }
+
+ if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+ return new MediaTransformError( 'thumbnail_error', $width, $height, wfMsg( 'thumbnail_dest_directory' ) );
+ }
+
+ # Use a subshell (brackets) to aggregate stderr from both pipeline commands
+ # before redirecting it to the overall stdout. This works in both Linux and Windows XP.
+ $cmd = '(' . wfEscapeShellArg( $wgDjvuRenderer ) . " -format=ppm -page={$page} -size={$width}x{$height} " .
+ wfEscapeShellArg( $srcPath );
+ if ( $wgDjvuPostProcessor ) {
+ $cmd .= " | {$wgDjvuPostProcessor}";
+ }
+ $cmd .= ' > ' . wfEscapeShellArg($dstPath) . ') 2>&1';
+ wfProfileIn( 'ddjvu' );
+ wfDebug( __METHOD__.": $cmd\n" );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'ddjvu' );
+
+ $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 ) );
+ return new MediaTransformError( 'thumbnail_error', $width, $height, $err );
+ } else {
+ return new ThumbnailImage( $dstUrl, $width, $height, $dstPath );
+ }
+ }
+
+ /**
+ * Cache an instance of DjVuImage in an Image object, return that instance
+ */
+ function getDjVuImage( $image, $path ) {
+ if ( !$image ) {
+ $deja = new DjVuImage( $path );
+ } elseif ( !isset( $image->dejaImage ) ) {
+ $deja = $image->dejaImage = new DjVuImage( $path );
+ } else {
+ $deja = $image->dejaImage;
+ }
+ return $deja;
+ }
+
+ /**
+ * Cache a document tree for the DjVu XML metadata
+ */
+ function getMetaTree( $image ) {
+ if ( isset( $image->dejaMetaTree ) ) {
+ return $image->dejaMetaTree;
+ }
+
+ $metadata = $image->getMetadata();
+ if ( !$this->isMetadataValid( $image, $metadata ) ) {
+ wfDebug( "DjVu XML metadata is invalid or missing, should have been fixed in upgradeRow\n" );
+ return false;
+ }
+ wfProfileIn( __METHOD__ );
+
+ wfSuppressWarnings();
+ try {
+ $image->dejaMetaTree = new SimpleXMLElement( $metadata );
+ } catch( Exception $e ) {
+ wfDebug( "Bogus multipage XML metadata on '$image->name'\n" );
+ // Set to false rather than null to avoid further attempts
+ $image->dejaMetaTree = false;
+ }
+ wfRestoreWarnings();
+ wfProfileOut( __METHOD__ );
+ return $image->dejaMetaTree;
+ }
+
+ function getImageSize( $image, $path ) {
+ return $this->getDjVuImage( $image, $path )->getImageSize();
+ }
+
+ function getThumbType( $ext, $mime ) {
+ global $wgDjvuOutputExtension;
+ static $mime;
+ if ( !isset( $mime ) ) {
+ $magic = MimeMagic::singleton();
+ $mime = $magic->guessTypesForExtension( $wgDjvuOutputExtension );
+ }
+ return array( $wgDjvuOutputExtension, $mime );
+ }
+
+ function getMetadata( $image, $path ) {
+ wfDebug( "Getting DjVu metadata for $path\n" );
+ return $this->getDjVuImage( $image, $path )->retrieveMetaData();
+ }
+
+ function getMetadataType( $image ) {
+ return 'djvuxml';
+ }
+
+ function isMetadataValid( $image, $metadata ) {
+ return !empty( $metadata ) && $metadata != serialize(array());
+ }
+
+ function pageCount( $image ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+ return count( $tree->xpath( '//OBJECT' ) );
+ }
+
+ function getPageDimensions( $image, $page ) {
+ $tree = $this->getMetaTree( $image );
+ if ( !$tree ) {
+ return false;
+ }
+
+ $o = $tree->BODY[0]->OBJECT[$page-1];
+ if ( $o ) {
+ return array(
+ 'width' => intval( $o['width'] ),
+ 'height' => intval( $o['height'] )
+ );
+ } else {
+ return false;
+ }
+ }
+}
+
+?>
diff --git a/includes/media/Generic.php b/includes/media/Generic.php
new file mode 100644
index 00000000..5254e0ea
--- /dev/null
+++ b/includes/media/Generic.php
@@ -0,0 +1,298 @@
+<?php
+
+/**
+ * Media-handling base classes and generic functionality
+ */
+
+/**
+ * Base media handler class
+ *
+ * @addtogroup Media
+ */
+abstract class MediaHandler {
+ const TRANSFORM_LATER = 1;
+
+ /**
+ * Instance cache
+ */
+ static $handlers = array();
+
+ /**
+ * Get a MediaHandler for a given MIME type from the instance cache
+ */
+ static function getHandler( $type ) {
+ global $wgMediaHandlers;
+ if ( !isset( $wgMediaHandlers[$type] ) ) {
+ wfDebug( __METHOD__ . ": no handler found for $type.\n");
+ return false;
+ }
+ $class = $wgMediaHandlers[$type];
+ if ( !isset( self::$handlers[$class] ) ) {
+ self::$handlers[$class] = new $class;
+ if ( !self::$handlers[$class]->isEnabled() ) {
+ self::$handlers[$class] = false;
+ }
+ }
+ return self::$handlers[$class];
+ }
+
+ /*
+ * Validate a thumbnail parameter at parse time.
+ * Return true to accept the parameter, and false to reject it.
+ * If you return false, the parser will do something quiet and forgiving.
+ */
+ abstract function validateParam( $name, $value );
+
+ /**
+ * Merge a parameter array into a string appropriate for inclusion in filenames
+ */
+ abstract function makeParamString( $params );
+
+ /**
+ * Parse a param string made with makeParamString back into an array
+ */
+ abstract function parseParamString( $str );
+
+ /**
+ * Changes the parameter array as necessary, ready for transformation.
+ * Should be idempotent.
+ * Returns false if the parameters are unacceptable and the transform should fail
+ */
+ abstract function normaliseParams( $image, &$params );
+
+ /**
+ * Get an image size array like that returned by getimagesize(), or false if it
+ * can't be determined.
+ *
+ * @param Image $image The image object, or false if there isn't one
+ * @param string $fileName The filename
+ * @return array
+ */
+ abstract function getImageSize( $image, $path );
+
+ /**
+ * Get handler-specific metadata which will be saved in the img_metadata field.
+ *
+ * @param Image $image The image object, or false if there isn't one
+ * @param string $fileName The filename
+ * @return string
+ */
+ function getMetadata( $image, $path ) { return ''; }
+
+ /**
+ * Get a string describing the type of metadata, for display purposes.
+ */
+ function getMetadataType( $image ) { return false; }
+
+ /**
+ * Check if the metadata string is valid for this handler.
+ * If it returns false, Image will reload the metadata from the file and update the database
+ */
+ function isMetadataValid( $image, $metadata ) { return true; }
+
+ /**
+ * Get a MediaTransformOutput object representing the transformed output. Does not
+ * actually do the transform.
+ *
+ * @param Image $image The image object
+ * @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()
+ */
+ function getTransform( $image, $dstPath, $dstUrl, $params ) {
+ return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
+ }
+
+ /**
+ * Get a MediaTransformOutput object representing the transformed output. Does the
+ * transform unless $flags contains self::TRANSFORM_LATER.
+ *
+ * @param Image $image The image object
+ * @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 integer $flags A bitfield, may contain self::TRANSFORM_LATER
+ */
+ abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
+
+ /**
+ * Get the thumbnail extension and MIME type for a given source MIME type
+ * @return array thumbnail extension and MIME type
+ */
+ function getThumbType( $ext, $mime ) {
+ return array( $ext, $mime );
+ }
+
+ /**
+ * True if the handled types can be transformed
+ */
+ function canRender() { return true; }
+ /**
+ * True if handled types cannot be displayed directly in a browser
+ * but can be rendered
+ */
+ function mustRender() { return false; }
+ /**
+ * True if the type has multi-page capabilities
+ */
+ function isMultiPage() { return false; }
+ /**
+ * Page count for a multi-page document, false if unsupported or unknown
+ */
+ function pageCount() { return false; }
+ /**
+ * False if the handler is disabled for all files
+ */
+ function isEnabled() { return true; }
+
+ /**
+ * Get an associative array of page dimensions
+ * Currently "width" and "height" are understood, but this might be
+ * expanded in the future.
+ * Returns false if unknown or if the document is not multi-page.
+ */
+ function getPageDimensions( $image, $page ) {
+ $gis = $this->getImageSize( $image, $image->getImagePath() );
+ return array(
+ 'width' => $gis[0],
+ 'height' => $gis[1]
+ );
+ }
+}
+
+/**
+ * Media handler abstract base class for images
+ *
+ * @addtogroup Media
+ */
+abstract class ImageHandler extends MediaHandler {
+ function validateParam( $name, $value ) {
+ if ( in_array( $name, array( 'width', 'height' ) ) ) {
+ if ( $value <= 0 ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ function makeParamString( $params ) {
+ if ( isset( $params['physicalWidth'] ) ) {
+ $width = $params['physicalWidth'];
+ } else {
+ $width = $params['width'];
+ }
+ # Removed for ProofreadPage
+ #$width = intval( $width );
+ return "{$width}px";
+ }
+
+ function parseParamString( $str ) {
+ $m = false;
+ if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => $m[1] );
+ } else {
+ return false;
+ }
+ }
+
+ function getScriptParams( $params ) {
+ return array( 'width' => $params['width'] );
+ }
+
+ function normaliseParams( $image, &$params ) {
+ $mimeType = $image->getMimeType();
+
+ if ( !isset( $params['width'] ) ) {
+ return false;
+ }
+ if ( !isset( $params['page'] ) ) {
+ $params['page'] = 1;
+ }
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+ if ( isset( $params['height'] ) && $params['height'] != -1 ) {
+ if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
+ $params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
+ }
+ }
+ $params['height'] = Image::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
+ if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get a transform output object without actually doing the transform
+ */
+ function getTransform( $image, $dstPath, $dstUrl, $params ) {
+ return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
+ }
+
+ /**
+ * Validate thumbnail parameters and fill in the correct height
+ *
+ * @param integer &$width Specified width (input/output)
+ * @param integer &$height Height (output only)
+ * @return false to indicate that an error should be returned to the user.
+ */
+ function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
+ $width = intval( $width );
+
+ # Sanity check $width
+ if( $width <= 0) {
+ wfDebug( __METHOD__.": Invalid destination width: $width\n" );
+ return false;
+ }
+ if ( $srcWidth <= 0 ) {
+ wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
+ return false;
+ }
+
+ $height = Image::scaleHeight( $srcWidth, $srcHeight, $width );
+ return true;
+ }
+
+ function getScriptedTransform( $image, $script, $params ) {
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return false;
+ }
+ $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
+ return new ThumbnailImage( $url, $params['width'], $params['height'] );
+ }
+
+ /**
+ * Check for zero-sized thumbnails. These can be generated when
+ * no disk space is available or some other error occurs
+ *
+ * @param $dstPath The location of the suspect file
+ * @param $retval Return value of some shell process, file will be deleted if this is non-zero
+ * @return true if removed, false otherwise
+ */
+ function removeBadFile( $dstPath, $retval = 0 ) {
+ $removed = false;
+ if( file_exists( $dstPath ) ) {
+ $thumbstat = stat( $dstPath );
+ if( $thumbstat['size'] == 0 || $retval != 0 ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'Removing bad %d-byte thumbnail "%s"',
+ $thumbstat['size'], $dstPath ) );
+ unlink( $dstPath );
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function getImageSize( $image, $path ) {
+ wfSuppressWarnings();
+ $gis = getimagesize( $path );
+ wfRestoreWarnings();
+ return $gis;
+ }
+}
+
+?>
diff --git a/includes/media/SVG.php b/includes/media/SVG.php
new file mode 100644
index 00000000..5307e269
--- /dev/null
+++ b/includes/media/SVG.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @addtogroup Media
+ */
+class SvgHandler extends ImageHandler {
+ function isEnabled() {
+ global $wgSVGConverters, $wgSVGConverter;
+ if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) {
+ wfDebug( "\$wgSVGConverter is invalid, disabling SVG rendering.\n" );
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ function mustRender() {
+ return true;
+ }
+
+ function normaliseParams( $image, &$params ) {
+ global $wgSVGMaxSize;
+ if ( !parent::normaliseParams( $image, $params ) ) {
+ return false;
+ }
+
+ # Don't make an image bigger than wgMaxSVGSize
+ $params['physicalWidth'] = $params['width'];
+ $params['physicalHeight'] = $params['height'];
+ if ( $params['physicalWidth'] > $wgSVGMaxSize ) {
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+ $params['physicalWidth'] = $wgSVGMaxSize;
+ $params['physicalHeight'] = Image::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize );
+ }
+ return true;
+ }
+
+ function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+ global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
+
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return new TransformParameterError( $params );
+ }
+ $clientWidth = $params['width'];
+ $clientHeight = $params['height'];
+ $physicalWidth = $params['physicalWidth'];
+ $physicalHeight = $params['physicalHeight'];
+ $srcWidth = $image->getWidth();
+ $srcHeight = $image->getHeight();
+ $srcPath = $image->getImagePath();
+
+ if ( $flags & self::TRANSFORM_LATER ) {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+
+ if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
+ wfMsg( 'thumbnail_dest_directory' ) );
+ }
+
+ $err = false;
+ if( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
+ $cmd = str_replace(
+ array( '$path/', '$width', '$height', '$input', '$output' ),
+ array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "",
+ intval( $physicalWidth ),
+ intval( $physicalHeight ),
+ wfEscapeShellArg( $srcPath ),
+ wfEscapeShellArg( $dstPath ) ),
+ $wgSVGConverters[$wgSVGConverter] ) . " 2>&1";
+ wfProfileIn( 'rsvg' );
+ wfDebug( __METHOD__.": $cmd\n" );
+ $err = wfShellExec( $cmd, $retval );
+ wfProfileOut( 'rsvg' );
+ }
+
+ $removed = $this->removeBadFile( $dstPath, $retval );
+ if ( $retval != 0 || $removed ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
+ wfHostname(), $retval, trim($err), $cmd ) );
+ return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
+ } else {
+ return new ThumbnailImage( $dstUrl, $clientWidth, $clientHeight, $dstPath );
+ }
+ }
+
+ function getImageSize( $image, $path ) {
+ return wfGetSVGsize( $path );
+ }
+
+ function getThumbType( $ext, $mime ) {
+ return array( 'png', 'image/png' );
+ }
+}
+?>