diff options
Diffstat (limited to 'includes/media')
-rw-r--r-- | includes/media/BMP.php | 31 | ||||
-rw-r--r-- | includes/media/Bitmap.php | 236 | ||||
-rw-r--r-- | includes/media/DjVu.php | 206 | ||||
-rw-r--r-- | includes/media/Generic.php | 298 | ||||
-rw-r--r-- | includes/media/SVG.php | 97 |
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' ); + } +} +?> |