MediaWiki  REL1_20
SVG.php
Go to the documentation of this file.
00001 <?php
00029 class SvgHandler extends ImageHandler {
00030         const SVG_METADATA_VERSION = 2;
00031 
00032         function isEnabled() {
00033                 global $wgSVGConverters, $wgSVGConverter;
00034                 if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) {
00035                         wfDebug( "\$wgSVGConverter is invalid, disabling SVG rendering.\n" );
00036                         return false;
00037                 } else {
00038                         return true;
00039                 }
00040         }
00041 
00042         function mustRender( $file ) {
00043                 return true;
00044         }
00045 
00046         function isVectorized( $file ) {
00047                 return true;
00048         }
00049 
00054         function isAnimatedImage( $file ) {
00055                 # TODO: detect animated SVGs
00056                 $metadata = $file->getMetadata();
00057                 if ( $metadata ) {
00058                         $metadata = $this->unpackMetadata( $metadata );
00059                         if( isset( $metadata['animated'] ) ) {
00060                                 return $metadata['animated'];
00061                         }
00062                 }
00063                 return false;
00064         }
00065 
00069         function canAnimateThumb( $file ) {
00070                 return false;
00071         }
00072 
00078         function normaliseParams( $image, &$params ) {
00079                 global $wgSVGMaxSize;
00080                 if ( !parent::normaliseParams( $image, $params ) ) {
00081                         return false;
00082                 }
00083                 # Don't make an image bigger than wgMaxSVGSize on the smaller side
00084                 if ( $params['physicalWidth'] <= $params['physicalHeight'] ) {
00085                         if ( $params['physicalWidth'] > $wgSVGMaxSize ) {
00086                                 $srcWidth = $image->getWidth( $params['page'] );
00087                                 $srcHeight = $image->getHeight( $params['page'] );
00088                                 $params['physicalWidth'] = $wgSVGMaxSize;
00089                                 $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize );
00090                         }
00091                 } else {
00092                         if ( $params['physicalHeight'] > $wgSVGMaxSize ) {
00093                                 $srcWidth = $image->getWidth( $params['page'] );
00094                                 $srcHeight = $image->getHeight( $params['page'] );
00095                                 $params['physicalWidth'] = File::scaleHeight( $srcHeight, $srcWidth, $wgSVGMaxSize );
00096                                 $params['physicalHeight'] = $wgSVGMaxSize;
00097                         }
00098                 }
00099                 return true;
00100         }
00101 
00110         function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
00111                 if ( !$this->normaliseParams( $image, $params ) ) {
00112                         return new TransformParameterError( $params );
00113                 }
00114                 $clientWidth = $params['width'];
00115                 $clientHeight = $params['height'];
00116                 $physicalWidth = $params['physicalWidth'];
00117                 $physicalHeight = $params['physicalHeight'];
00118 
00119                 if ( $flags & self::TRANSFORM_LATER ) {
00120                         return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
00121                 }
00122 
00123                 if ( !wfMkdirParents( dirname( $dstPath ), null, __METHOD__ ) ) {
00124                         return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
00125                                 wfMessage( 'thumbnail_dest_directory' )->text() );
00126                 }
00127 
00128                 $srcPath = $image->getLocalRefPath();
00129                 $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight );
00130                 if( $status === true ) {
00131                         return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
00132                 } else {
00133                         return $status; // MediaTransformError
00134                 }
00135         }
00136 
00146         public function rasterize( $srcPath, $dstPath, $width, $height ) {
00147                 global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
00148                 $err = false;
00149                 $retval = '';
00150                 if ( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
00151                         if ( is_array( $wgSVGConverters[$wgSVGConverter] ) ) {
00152                                 // This is a PHP callable
00153                                 $func = $wgSVGConverters[$wgSVGConverter][0];
00154                                 $args = array_merge( array( $srcPath, $dstPath, $width, $height ),
00155                                         array_slice( $wgSVGConverters[$wgSVGConverter], 1 ) );
00156                                 if ( !is_callable( $func ) ) {
00157                                         throw new MWException( "$func is not callable" );
00158                                 }
00159                                 $err = call_user_func_array( $func, $args );
00160                                 $retval = (bool)$err;
00161                         } else {
00162                                 // External command
00163                                 $cmd = str_replace(
00164                                         array( '$path/', '$width', '$height', '$input', '$output' ),
00165                                         array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "",
00166                                                    intval( $width ),
00167                                                    intval( $height ),
00168                                                    wfEscapeShellArg( $srcPath ),
00169                                                    wfEscapeShellArg( $dstPath ) ),
00170                                         $wgSVGConverters[$wgSVGConverter]
00171                                 ) . " 2>&1";
00172                                 wfProfileIn( 'rsvg' );
00173                                 wfDebug( __METHOD__.": $cmd\n" );
00174                                 $err = wfShellExec( $cmd, $retval );
00175                                 wfProfileOut( 'rsvg' );
00176                         }
00177                 }
00178                 $removed = $this->removeBadFile( $dstPath, $retval );
00179                 if ( $retval != 0 || $removed ) {
00180                         wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
00181                                         wfHostname(), $retval, trim($err), $cmd ) );
00182                         return new MediaTransformError( 'thumbnail_error', $width, $height, $err );
00183                 }
00184                 return true;
00185         }
00186 
00187         public static function rasterizeImagickExt( $srcPath, $dstPath, $width, $height ) {
00188                 $im = new Imagick( $srcPath );
00189                 $im->setImageFormat( 'png' );
00190                 $im->setBackgroundColor( 'transparent' );
00191                 $im->setImageDepth( 8 );
00192 
00193                 if ( !$im->thumbnailImage( intval( $width ), intval( $height ), /* fit */ false ) ) {
00194                         return 'Could not resize image';
00195                 }
00196                 if ( !$im->writeImage( $dstPath ) ) {
00197                         return "Could not write to $dstPath";
00198                 }
00199         }
00200 
00207         function getImageSize( $file, $path, $metadata = false ) {
00208                 if ( $metadata === false ) {
00209                         $metadata = $file->getMetaData();
00210                 }
00211                 $metadata = $this->unpackMetaData( $metadata );
00212 
00213                 if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) {
00214                         return array( $metadata['width'], $metadata['height'], 'SVG',
00215                                         "width=\"{$metadata['width']}\" height=\"{$metadata['height']}\"" );
00216                 }
00217         }
00218 
00219         function getThumbType( $ext, $mime, $params = null ) {
00220                 return array( 'png', 'image/png' );
00221         }
00222 
00232         function getLongDesc( $file ) {
00233                 global $wgLang;
00234                 $size = $wgLang->formatSize( $file->getSize() );
00235 
00236                 if ( $this->isAnimatedImage( $file ) ) {
00237                         $msg = wfMessage( 'svg-long-desc-animated' );
00238                 } else {
00239                         $msg = wfMessage( 'svg-long-desc' );
00240                 }
00241 
00242                 $msg->numParams(
00243                         $file->getWidth(),
00244                         $file->getHeight()
00245                 );
00246                 $msg->Params( $size );
00247                 return $msg->parse();
00248         }
00249 
00250         function getMetadata( $file, $filename ) {
00251                 try {
00252                         $metadata = SVGMetadataExtractor::getMetadata( $filename );
00253                 } catch( Exception $e ) {
00254                         // Broken file?
00255                         wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" );
00256                         return '0';
00257                 }
00258                 $metadata['version'] = self::SVG_METADATA_VERSION;
00259                 return serialize( $metadata );
00260         }
00261 
00262         function unpackMetadata( $metadata ) {
00263                 wfSuppressWarnings();
00264                 $unser = unserialize( $metadata );
00265                 wfRestoreWarnings();
00266                 if ( isset( $unser['version'] ) && $unser['version'] == self::SVG_METADATA_VERSION ) {
00267                         return $unser;
00268                 } else {
00269                         return false;
00270                 }
00271         }
00272 
00273         function getMetadataType( $image ) {
00274                 return 'parsed-svg';
00275         }
00276 
00277         function isMetadataValid( $image, $metadata ) {
00278                 $meta = $this->unpackMetadata( $metadata );
00279                 if ( $meta === false ) {
00280                         return self::METADATA_BAD;
00281                 }
00282                 if ( !isset( $meta['originalWidth'] ) ) {
00283                         // Old but compatible
00284                         return self::METADATA_COMPATIBLE;
00285                 }
00286                 return self::METADATA_GOOD;
00287         }
00288 
00289         function visibleMetadataFields() {
00290                 $fields = array( 'objectname', 'imagedescription' );
00291                 return $fields;
00292         }
00293 
00298         function formatMetadata( $file ) {
00299                 $result = array(
00300                         'visible' => array(),
00301                         'collapsed' => array()
00302                 );
00303                 $metadata = $file->getMetadata();
00304                 if ( !$metadata ) {
00305                         return false;
00306                 }
00307                 $metadata = $this->unpackMetadata( $metadata );
00308                 if ( !$metadata ) {
00309                         return false;
00310                 }
00311 
00312                 /* TODO: add a formatter
00313                 $format = new FormatSVG( $metadata );
00314                 $formatted = $format->getFormattedData();
00315                 */
00316 
00317                 // Sort fields into visible and collapsed
00318                 $visibleFields = $this->visibleMetadataFields();
00319 
00320                 // Rename fields to be compatible with exif, so that
00321                 // the labels for these fields work and reuse existing messages.
00322                 $conversion = array(
00323                         'originalwidth' => 'imagewidth',
00324                         'originalheight' => 'imagelength',
00325                         'description' => 'imagedescription',
00326                         'title' => 'objectname',
00327                 );
00328                 foreach ( $metadata as $name => $value ) {
00329                         $tag = strtolower( $name );
00330                         if ( isset( $conversion[$tag] ) ) {
00331                                 $tag = $conversion[$tag];
00332                         } else {
00333                                 // Do not output other metadata not in list
00334                                 continue;
00335                         }
00336                         self::addMeta( $result,
00337                                 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
00338                                 'exif',
00339                                 $tag,
00340                                 $value
00341                         );
00342                 }
00343                 return $result;
00344         }
00345 }