MediaWiki  REL1_21
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                 $metadata = $this->unpackMetadata( $image->getMetadata() );
00124                 if ( isset( $metadata['error'] ) ) { // sanity check
00125                         $err = wfMessage( 'svg-long-error', $metadata['error']['message'] )->text();
00126                         return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
00127                 }
00128 
00129                 if ( !wfMkdirParents( dirname( $dstPath ), null, __METHOD__ ) ) {
00130                         return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight,
00131                                 wfMessage( 'thumbnail_dest_directory' )->text() );
00132                 }
00133 
00134                 $srcPath = $image->getLocalRefPath();
00135                 $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight );
00136                 if ( $status === true ) {
00137                         return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
00138                 } else {
00139                         return $status; // MediaTransformError
00140                 }
00141         }
00142 
00153         public function rasterize( $srcPath, $dstPath, $width, $height ) {
00154                 global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
00155                 $err = false;
00156                 $retval = '';
00157                 if ( isset( $wgSVGConverters[$wgSVGConverter] ) ) {
00158                         if ( is_array( $wgSVGConverters[$wgSVGConverter] ) ) {
00159                                 // This is a PHP callable
00160                                 $func = $wgSVGConverters[$wgSVGConverter][0];
00161                                 $args = array_merge( array( $srcPath, $dstPath, $width, $height ),
00162                                         array_slice( $wgSVGConverters[$wgSVGConverter], 1 ) );
00163                                 if ( !is_callable( $func ) ) {
00164                                         throw new MWException( "$func is not callable" );
00165                                 }
00166                                 $err = call_user_func_array( $func, $args );
00167                                 $retval = (bool)$err;
00168                         } else {
00169                                 // External command
00170                                 $cmd = str_replace(
00171                                         array( '$path/', '$width', '$height', '$input', '$output' ),
00172                                         array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "",
00173                                                 intval( $width ),
00174                                                 intval( $height ),
00175                                                 wfEscapeShellArg( $srcPath ),
00176                                                 wfEscapeShellArg( $dstPath ) ),
00177                                         $wgSVGConverters[$wgSVGConverter]
00178                                 ) . " 2>&1";
00179                                 wfProfileIn( 'rsvg' );
00180                                 wfDebug( __METHOD__ . ": $cmd\n" );
00181                                 $err = wfShellExec( $cmd, $retval );
00182                                 wfProfileOut( 'rsvg' );
00183                         }
00184                 }
00185                 $removed = $this->removeBadFile( $dstPath, $retval );
00186                 if ( $retval != 0 || $removed ) {
00187                         wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
00188                                         wfHostname(), $retval, trim( $err ), $cmd ) );
00189                         return new MediaTransformError( 'thumbnail_error', $width, $height, $err );
00190                 }
00191                 return true;
00192         }
00193 
00194         public static function rasterizeImagickExt( $srcPath, $dstPath, $width, $height ) {
00195                 $im = new Imagick( $srcPath );
00196                 $im->setImageFormat( 'png' );
00197                 $im->setBackgroundColor( 'transparent' );
00198                 $im->setImageDepth( 8 );
00199 
00200                 if ( !$im->thumbnailImage( intval( $width ), intval( $height ), /* fit */ false ) ) {
00201                         return 'Could not resize image';
00202                 }
00203                 if ( !$im->writeImage( $dstPath ) ) {
00204                         return "Could not write to $dstPath";
00205                 }
00206         }
00207 
00214         function getImageSize( $file, $path, $metadata = false ) {
00215                 if ( $metadata === false ) {
00216                         $metadata = $file->getMetaData();
00217                 }
00218                 $metadata = $this->unpackMetaData( $metadata );
00219 
00220                 if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) {
00221                         return array( $metadata['width'], $metadata['height'], 'SVG',
00222                                         "width=\"{$metadata['width']}\" height=\"{$metadata['height']}\"" );
00223                 } else { // error
00224                         return array( 0, 0, 'SVG', "width=\"0\" height=\"0\"" );
00225                 }
00226         }
00227 
00228         function getThumbType( $ext, $mime, $params = null ) {
00229                 return array( 'png', 'image/png' );
00230         }
00231 
00241         function getLongDesc( $file ) {
00242                 global $wgLang;
00243 
00244                 $metadata = $this->unpackMetadata( $file->getMetadata() );
00245                 if ( isset( $metadata['error'] ) ) {
00246                         return wfMessage( 'svg-long-error', $metadata['error']['message'] )->text();
00247                 }
00248 
00249                 $size = $wgLang->formatSize( $file->getSize() );
00250 
00251                 if ( $this->isAnimatedImage( $file ) ) {
00252                         $msg = wfMessage( 'svg-long-desc-animated' );
00253                 } else {
00254                         $msg = wfMessage( 'svg-long-desc' );
00255                 }
00256 
00257                 $msg->numParams( $file->getWidth(), $file->getHeight() )->params( $size );
00258 
00259                 return $msg->parse();
00260         }
00261 
00262         function getMetadata( $file, $filename ) {
00263                 $metadata = array( 'version' => self::SVG_METADATA_VERSION );
00264                 try {
00265                         $metadata += SVGMetadataExtractor::getMetadata( $filename );
00266                 } catch( MWException $e ) { // @TODO: SVG specific exceptions
00267                         // File not found, broken, etc.
00268                         $metadata['error'] = array(
00269                                 'message' => $e->getMessage(),
00270                                 'code'    => $e->getCode()
00271                         );
00272                         wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" );
00273                 }
00274                 return serialize( $metadata );
00275         }
00276 
00277         function unpackMetadata( $metadata ) {
00278                 wfSuppressWarnings();
00279                 $unser = unserialize( $metadata );
00280                 wfRestoreWarnings();
00281                 if ( isset( $unser['version'] ) && $unser['version'] == self::SVG_METADATA_VERSION ) {
00282                         return $unser;
00283                 } else {
00284                         return false;
00285                 }
00286         }
00287 
00288         function getMetadataType( $image ) {
00289                 return 'parsed-svg';
00290         }
00291 
00292         function isMetadataValid( $image, $metadata ) {
00293                 $meta = $this->unpackMetadata( $metadata );
00294                 if ( $meta === false ) {
00295                         return self::METADATA_BAD;
00296                 }
00297                 if ( !isset( $meta['originalWidth'] ) ) {
00298                         // Old but compatible
00299                         return self::METADATA_COMPATIBLE;
00300                 }
00301                 return self::METADATA_GOOD;
00302         }
00303 
00304         function visibleMetadataFields() {
00305                 $fields = array( 'objectname', 'imagedescription' );
00306                 return $fields;
00307         }
00308 
00313         function formatMetadata( $file ) {
00314                 $result = array(
00315                         'visible' => array(),
00316                         'collapsed' => array()
00317                 );
00318                 $metadata = $file->getMetadata();
00319                 if ( !$metadata ) {
00320                         return false;
00321                 }
00322                 $metadata = $this->unpackMetadata( $metadata );
00323                 if ( !$metadata || isset( $metadata['error'] ) ) {
00324                         return false;
00325                 }
00326 
00327                 /* TODO: add a formatter
00328                 $format = new FormatSVG( $metadata );
00329                 $formatted = $format->getFormattedData();
00330                 */
00331 
00332                 // Sort fields into visible and collapsed
00333                 $visibleFields = $this->visibleMetadataFields();
00334 
00335                 // Rename fields to be compatible with exif, so that
00336                 // the labels for these fields work and reuse existing messages.
00337                 $conversion = array(
00338                         'originalwidth' => 'imagewidth',
00339                         'originalheight' => 'imagelength',
00340                         'description' => 'imagedescription',
00341                         'title' => 'objectname',
00342                 );
00343                 foreach ( $metadata as $name => $value ) {
00344                         $tag = strtolower( $name );
00345                         if ( isset( $conversion[$tag] ) ) {
00346                                 $tag = $conversion[$tag];
00347                         } else {
00348                                 // Do not output other metadata not in list
00349                                 continue;
00350                         }
00351                         self::addMeta( $result,
00352                                 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
00353                                 'exif',
00354                                 $tag,
00355                                 $value
00356                         );
00357                 }
00358                 return $result;
00359         }
00360 }