MediaWiki
REL1_20
|
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 }