MediaWiki
REL1_19
|
00001 <?php 00014 class SvgHandler extends ImageHandler { 00015 const SVG_METADATA_VERSION = 2; 00016 00017 function isEnabled() { 00018 global $wgSVGConverters, $wgSVGConverter; 00019 if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) { 00020 wfDebug( "\$wgSVGConverter is invalid, disabling SVG rendering.\n" ); 00021 return false; 00022 } else { 00023 return true; 00024 } 00025 } 00026 00027 function mustRender( $file ) { 00028 return true; 00029 } 00030 00031 function isVectorized( $file ) { 00032 return true; 00033 } 00034 00039 function isAnimatedImage( $file ) { 00040 # TODO: detect animated SVGs 00041 $metadata = $file->getMetadata(); 00042 if ( $metadata ) { 00043 $metadata = $this->unpackMetadata( $metadata ); 00044 if( isset( $metadata['animated'] ) ) { 00045 return $metadata['animated']; 00046 } 00047 } 00048 return false; 00049 } 00050 00056 function normaliseParams( $image, &$params ) { 00057 global $wgSVGMaxSize; 00058 if ( !parent::normaliseParams( $image, $params ) ) { 00059 return false; 00060 } 00061 # Don't make an image bigger than wgMaxSVGSize on the smaller side 00062 if ( $params['physicalWidth'] <= $params['physicalHeight'] ) { 00063 if ( $params['physicalWidth'] > $wgSVGMaxSize ) { 00064 $srcWidth = $image->getWidth( $params['page'] ); 00065 $srcHeight = $image->getHeight( $params['page'] ); 00066 $params['physicalWidth'] = $wgSVGMaxSize; 00067 $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight, $wgSVGMaxSize ); 00068 } 00069 } else { 00070 if ( $params['physicalHeight'] > $wgSVGMaxSize ) { 00071 $srcWidth = $image->getWidth( $params['page'] ); 00072 $srcHeight = $image->getHeight( $params['page'] ); 00073 $params['physicalWidth'] = File::scaleHeight( $srcHeight, $srcWidth, $wgSVGMaxSize ); 00074 $params['physicalHeight'] = $wgSVGMaxSize; 00075 } 00076 } 00077 return true; 00078 } 00079 00088 function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) { 00089 if ( !$this->normaliseParams( $image, $params ) ) { 00090 return new TransformParameterError( $params ); 00091 } 00092 $clientWidth = $params['width']; 00093 $clientHeight = $params['height']; 00094 $physicalWidth = $params['physicalWidth']; 00095 $physicalHeight = $params['physicalHeight']; 00096 $srcPath = $image->getLocalRefPath(); 00097 00098 if ( $flags & self::TRANSFORM_LATER ) { 00099 return new ThumbnailImage( $image, $dstUrl, $clientWidth, $clientHeight, $dstPath ); 00100 } 00101 00102 if ( !wfMkdirParents( dirname( $dstPath ), null, __METHOD__ ) ) { 00103 return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, 00104 wfMsg( 'thumbnail_dest_directory' ) ); 00105 } 00106 00107 $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight ); 00108 if( $status === true ) { 00109 return new ThumbnailImage( $image, $dstUrl, $clientWidth, $clientHeight, $dstPath ); 00110 } else { 00111 return $status; // MediaTransformError 00112 } 00113 } 00114 00124 public function rasterize( $srcPath, $dstPath, $width, $height ) { 00125 global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath; 00126 $err = false; 00127 $retval = ''; 00128 if ( isset( $wgSVGConverters[$wgSVGConverter] ) ) { 00129 if ( is_array( $wgSVGConverters[$wgSVGConverter] ) ) { 00130 // This is a PHP callable 00131 $func = $wgSVGConverters[$wgSVGConverter][0]; 00132 $args = array_merge( array( $srcPath, $dstPath, $width, $height ), 00133 array_slice( $wgSVGConverters[$wgSVGConverter], 1 ) ); 00134 if ( !is_callable( $func ) ) { 00135 throw new MWException( "$func is not callable" ); 00136 } 00137 $err = call_user_func_array( $func, $args ); 00138 $retval = (bool)$err; 00139 } else { 00140 // External command 00141 $cmd = str_replace( 00142 array( '$path/', '$width', '$height', '$input', '$output' ), 00143 array( $wgSVGConverterPath ? wfEscapeShellArg( "$wgSVGConverterPath/" ) : "", 00144 intval( $width ), 00145 intval( $height ), 00146 wfEscapeShellArg( $srcPath ), 00147 wfEscapeShellArg( $dstPath ) ), 00148 $wgSVGConverters[$wgSVGConverter] 00149 ) . " 2>&1"; 00150 wfProfileIn( 'rsvg' ); 00151 wfDebug( __METHOD__.": $cmd\n" ); 00152 $err = wfShellExec( $cmd, $retval ); 00153 wfProfileOut( 'rsvg' ); 00154 } 00155 } 00156 $removed = $this->removeBadFile( $dstPath, $retval ); 00157 if ( $retval != 0 || $removed ) { 00158 wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', 00159 wfHostname(), $retval, trim($err), $cmd ) ); 00160 return new MediaTransformError( 'thumbnail_error', $width, $height, $err ); 00161 } 00162 return true; 00163 } 00164 00165 public static function rasterizeImagickExt( $srcPath, $dstPath, $width, $height ) { 00166 $im = new Imagick( $srcPath ); 00167 $im->setImageFormat( 'png' ); 00168 $im->setBackgroundColor( 'transparent' ); 00169 $im->setImageDepth( 8 ); 00170 00171 if ( !$im->thumbnailImage( intval( $width ), intval( $height ), /* fit */ false ) ) { 00172 return 'Could not resize image'; 00173 } 00174 if ( !$im->writeImage( $dstPath ) ) { 00175 return "Could not write to $dstPath"; 00176 } 00177 } 00178 00185 function getImageSize( $file, $path, $metadata = false ) { 00186 if ( $metadata === false ) { 00187 $metadata = $file->getMetaData(); 00188 } 00189 $metadata = $this->unpackMetaData( $metadata ); 00190 00191 if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) { 00192 return array( $metadata['width'], $metadata['height'], 'SVG', 00193 "width=\"{$metadata['width']}\" height=\"{$metadata['height']}\"" ); 00194 } 00195 } 00196 00197 function getThumbType( $ext, $mime, $params = null ) { 00198 return array( 'png', 'image/png' ); 00199 } 00200 00205 function getLongDesc( $file ) { 00206 global $wgLang; 00207 return wfMsgExt( 'svg-long-desc', 'parseinline', 00208 $wgLang->formatNum( $file->getWidth() ), 00209 $wgLang->formatNum( $file->getHeight() ), 00210 $wgLang->formatSize( $file->getSize() ) ); 00211 } 00212 00213 function getMetadata( $file, $filename ) { 00214 try { 00215 $metadata = SVGMetadataExtractor::getMetadata( $filename ); 00216 } catch( Exception $e ) { 00217 // Broken file? 00218 wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); 00219 return '0'; 00220 } 00221 $metadata['version'] = self::SVG_METADATA_VERSION; 00222 return serialize( $metadata ); 00223 } 00224 00225 function unpackMetadata( $metadata ) { 00226 wfSuppressWarnings(); 00227 $unser = unserialize( $metadata ); 00228 wfRestoreWarnings(); 00229 if ( isset( $unser['version'] ) && $unser['version'] == self::SVG_METADATA_VERSION ) { 00230 return $unser; 00231 } else { 00232 return false; 00233 } 00234 } 00235 00236 function getMetadataType( $image ) { 00237 return 'parsed-svg'; 00238 } 00239 00240 function isMetadataValid( $image, $metadata ) { 00241 return $this->unpackMetadata( $metadata ) !== false; 00242 } 00243 00244 function visibleMetadataFields() { 00245 $fields = array( 'title', 'description', 'animated' ); 00246 return $fields; 00247 } 00248 00253 function formatMetadata( $file ) { 00254 $result = array( 00255 'visible' => array(), 00256 'collapsed' => array() 00257 ); 00258 $metadata = $file->getMetadata(); 00259 if ( !$metadata ) { 00260 return false; 00261 } 00262 $metadata = $this->unpackMetadata( $metadata ); 00263 if ( !$metadata ) { 00264 return false; 00265 } 00266 unset( $metadata['version'] ); 00267 unset( $metadata['metadata'] ); /* non-formatted XML */ 00268 00269 /* TODO: add a formatter 00270 $format = new FormatSVG( $metadata ); 00271 $formatted = $format->getFormattedData(); 00272 */ 00273 00274 // Sort fields into visible and collapsed 00275 $visibleFields = $this->visibleMetadataFields(); 00276 00277 // Rename fields to be compatible with exif, so that 00278 // the labels for these fields work. 00279 $conversion = array( 'width' => 'imagewidth', 00280 'height' => 'imagelength', 00281 'description' => 'imagedescription', 00282 'title' => 'objectname', 00283 ); 00284 foreach ( $metadata as $name => $value ) { 00285 $tag = strtolower( $name ); 00286 if ( isset( $conversion[$tag] ) ) { 00287 $tag = $conversion[$tag]; 00288 } 00289 self::addMeta( $result, 00290 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed', 00291 'exif', 00292 $tag, 00293 $value 00294 ); 00295 } 00296 return $result; 00297 } 00298 }