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