29use Wikimedia\AtEase\AtEase;
30use Wikimedia\ScopedCallback;
40 private const SVG_DEFAULT_RENDER_LANG =
'en';
46 private static $metaConversion = [
47 'originalwidth' =>
'ImageWidth',
48 'originalheight' =>
'ImageLength',
49 'description' =>
'ImageDescription',
50 'title' =>
'ObjectName',
54 $config = MediaWikiServices::getInstance()->getMainConfig();
55 $svgConverters = $config->get( MainConfigNames::SVGConverters );
56 $svgConverter = $config->get( MainConfigNames::SVGConverter );
57 if ( $config->get( MainConfigNames::SVGNativeRendering ) ===
true ) {
60 if ( !isset( $svgConverters[$svgConverter] ) ) {
61 wfDebug(
"\$wgSVGConverter is invalid, disabling SVG rendering." );
70 $svgNativeRendering = MediaWikiServices::getInstance()
71 ->getMainConfig()->get( MainConfigNames::SVGNativeRendering );
72 if ( $svgNativeRendering ===
true ) {
76 if ( $svgNativeRendering !==
'partial' ) {
80 $maxSVGFilesize = MediaWikiServices::getInstance()
81 ->getMainConfig()->get( MainConfigNames::SVGNativeRenderingSizeLimit );
85 && $file->getSize() <= $maxSVGFilesize;
101 # @todo Detect animated SVGs
103 if ( isset( $metadata[
'animated'] ) ) {
104 return $metadata[
'animated'];
125 if ( isset( $metadata[
'translations'] ) ) {
126 foreach ( $metadata[
'translations'] as $lang => $langType ) {
127 if ( $langType === SVGReader::LANG_FULL_MATCH ) {
128 $langList[] = strtolower( $lang );
132 return array_unique( $langList );
152 if ( $userPreferredLanguage ===
'und' ) {
155 foreach ( $svgLanguages as $svgLang ) {
156 if ( strcasecmp( $svgLang, $userPreferredLanguage ) === 0 ) {
159 $trimmedSvgLang = $svgLang;
160 while ( strpos( $trimmedSvgLang,
'-' ) !==
false ) {
161 $trimmedSvgLang = substr( $trimmedSvgLang, 0, strrpos( $trimmedSvgLang,
'-' ) );
162 if ( strcasecmp( $trimmedSvgLang, $userPreferredLanguage ) === 0 ) {
178 return $params[
'lang'] ??
$params[
'targetlang'] ?? self::SVG_DEFAULT_RENDER_LANG;
188 return self::SVG_DEFAULT_RENDER_LANG;
206 if ( parent::normaliseParams( $image,
$params ) ) {
224 $svgMaxSize = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::SVGMaxSize );
226 # Don't make an image bigger than wgMaxSVGSize on the smaller side
228 if (
$params[
'physicalWidth'] > $svgMaxSize ) {
229 $srcWidth = $image->getWidth(
$params[
'page'] );
230 $srcHeight = $image->getHeight(
$params[
'page'] );
231 $params[
'physicalWidth'] = $svgMaxSize;
232 $params[
'physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight, $svgMaxSize );
234 } elseif (
$params[
'physicalHeight'] > $svgMaxSize ) {
235 $srcWidth = $image->getWidth(
$params[
'page'] );
236 $srcHeight = $image->getHeight(
$params[
'page'] );
237 $params[
'physicalWidth'] = File::scaleHeight( $srcHeight, $srcWidth, $svgMaxSize );
238 $params[
'physicalHeight'] = $svgMaxSize;
242 if ( isset(
$params[
'targetlang'] ) && !$image->getMatchedLanguage(
$params[
'targetlang'] ) ) {
243 unset(
$params[
'targetlang'] );
261 $clientWidth =
$params[
'width'];
262 $clientHeight =
$params[
'height'];
263 $physicalWidth =
$params[
'physicalWidth'];
264 $physicalHeight =
$params[
'physicalHeight'];
272 if ( $flags & self::TRANSFORM_LATER ) {
277 if ( isset( $metadata[
'error'] ) ) {
278 $err =
wfMessage(
'svg-long-error', $metadata[
'error'][
'message'] );
285 wfMessage(
'thumbnail_dest_directory' ) );
288 $srcPath = $image->getLocalRefPath();
289 if ( $srcPath ===
false ) {
291 sprintf(
'Thumbnail failed on %s: could not get local copy of "%s"',
304 $lnPath =
"$tmpDir/" . basename( $srcPath );
305 $ok = mkdir( $tmpDir, 0771 );
308 sprintf(
'Thumbnail failed on %s: could not create temporary directory %s',
312 wfMessage(
'thumbnail-temp-create' )->text()
316 $ok = @symlink( $srcPath, $lnPath );
318 $cleaner =
new ScopedCallback(
static function () use ( $tmpDir, $lnPath ) {
319 AtEase::suppressWarnings();
322 AtEase::restoreWarnings();
326 $ok = copy( $srcPath, $lnPath );
330 sprintf(
'Thumbnail failed on %s: could not link %s to %s',
338 $status = $this->
rasterize( $lnPath, $dstPath, $physicalWidth, $physicalHeight, $lang );
339 if ( $status ===
true ) {
356 public function rasterize( $srcPath, $dstPath, $width, $height, $lang =
false ) {
357 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
358 $svgConverters = $mainConfig->get( MainConfigNames::SVGConverters );
359 $svgConverter = $mainConfig->get( MainConfigNames::SVGConverter );
360 $svgConverterPath = $mainConfig->get( MainConfigNames::SVGConverterPath );
363 if ( isset( $svgConverters[$svgConverter] ) ) {
364 if ( is_array( $svgConverters[$svgConverter] ) ) {
366 $func = $svgConverters[$svgConverter][0];
367 if ( !is_callable( $func ) ) {
368 throw new UnexpectedValueException(
"$func is not callable" );
370 $err = $func( $srcPath,
375 ...array_slice( $svgConverters[$svgConverter], 1 )
377 $retval = (bool)$err;
380 $cmd = strtr( $svgConverters[$svgConverter], [
381 '$path/' => $svgConverterPath ? Shell::escape(
"$svgConverterPath/" ) :
'',
382 '$width' => (int)$width,
383 '$height' => (
int)$height,
384 '$input' => Shell::escape( $srcPath ),
385 '$output' => Shell::escape( $dstPath ),
389 if ( $lang !==
false ) {
390 $env[
'LANG'] = $lang;
393 wfDebug( __METHOD__ .
": $cmd" );
398 if ( $retval != 0 || $removed ) {
409 $im =
new Imagick( $srcPath );
410 $im->setBackgroundColor(
'transparent' );
411 $im->readImage( $srcPath );
412 $im->setImageFormat(
'png' );
413 $im->setImageDepth( 8 );
415 if ( !$im->thumbnailImage( (
int)$width, (
int)$height,
false ) ) {
416 return 'Could not resize image';
418 if ( !$im->writeImage( $dstPath ) ) {
419 return "Could not write to $dstPath";
424 return [
'png',
'image/png' ];
438 if ( isset( $metadata[
'error'] ) ) {
439 return wfMessage(
'svg-long-error', $metadata[
'error'][
'message'] )->text();
443 $msg =
wfMessage(
'svg-long-desc-animated' );
448 return $msg->numParams( $file->getWidth(), $file->getHeight() )->sizeParams( $file->getSize() )->parse();
457 $metadata = [
'version' => self::SVG_METADATA_VERSION ];
461 $metadata += $svgReader->getMetadata();
464 $metadata[
'error'] = [
465 'message' => $e->getMessage(),
466 'code' => $e->getCode()
468 wfDebug( __METHOD__ .
': ' . $e->getMessage() );
472 'width' => $metadata[
'width'] ?? 0,
473 'height' => $metadata[
'height'] ?? 0,
474 'metadata' => $metadata
479 if ( isset( $unser[
'version'] ) && $unser[
'version'] === self::SVG_METADATA_VERSION ) {
493 return self::METADATA_BAD;
495 if ( !isset( $meta[
'originalWidth'] ) ) {
497 return self::METADATA_COMPATIBLE;
500 return self::METADATA_GOOD;
504 return [
'objectname',
'imagedescription' ];
518 if ( !$metadata || isset( $metadata[
'error'] ) ) {
531 foreach ( $metadata as $name => $value ) {
532 $tag = strtolower( $name );
533 if ( isset( self::$metaConversion[$tag] ) ) {
534 $tag = strtolower( self::$metaConversion[$tag] );
540 self::addMeta( $result,
541 in_array( $tag, $visibleFields ) ?
'visible' :
'collapsed',
548 return $showMeta ? $result :
false;
557 if ( in_array( $name, [
'width',
'height' ] ) ) {
559 return ( $value > 0 );
561 if ( $name ===
'lang' ) {
564 || !LanguageCode::isWellFormedLanguageTag( $value )
583 if ( $code !== self::SVG_DEFAULT_RENDER_LANG ) {
584 $lang =
'lang' . strtolower( $code ) .
'-';
586 if ( !isset(
$params[
'width'] ) ) {
590 return "$lang{$params['width']}px";
596 if ( preg_match(
'/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) {
597 if ( LanguageCode::isWellFormedLanguageTag( $m[1] ) ) {
598 return [
'width' => array_pop( $m ),
'lang' => $m[1] ];
600 return [
'width' => array_pop( $m ),
'lang' => self::SVG_DEFAULT_RENDER_LANG ];
602 if ( preg_match(
'/^(\d+)px$/', $str, $m ) ) {
603 return [
'width' => $m[1],
'lang' => self::SVG_DEFAULT_RENDER_LANG ];
609 return [
'img_lang' =>
'lang',
'img_width' =>
'width' ];
617 $scriptParams = [
'width' =>
$params[
'width'] ];
618 if ( isset(
$params[
'lang'] ) ) {
619 $scriptParams[
'lang'] =
$params[
'lang'];
622 return $scriptParams;
627 if ( !$metadata || isset( $metadata[
'error'] ) ) {
631 foreach ( $metadata as $name => $value ) {
632 $tag = strtolower( $name );
633 if ( $tag ===
'originalwidth' || $tag ===
'originalheight' ) {
638 if ( isset( self::$metaConversion[$tag] ) ) {
639 $tag = self::$metaConversion[$tag];
640 $stdMetadata[$tag] = $value;
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTempDir()
Tries to get the system directory for temporary files.
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
wfHostname()
Get host name of the current machine, for use in error reporting.
wfShellExecWithStderr( $cmd, &$retval=null, $environ=[], $limits=[])
Execute a shell command, returning both stdout and stderr.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
array $params
The job parameters.
Implements some public methods and some protected utility functions which are required by multiple ch...
getMetadataArray()
Get the unserialized handler-specific metadata STUB.
Media handler abstract base class for images.
A class containing constants representing the names of configuration variables.
validateParam( $name, $value)
isVectorized( $file)
The material is vectorized and thus scaling is lossless.
normaliseParams( $image, &$params)
formatMetadata( $file, $context=false)
parseParamString( $str)
Parse a param string made with makeParamString back into an array.array|false Array of parameters or ...
mustRender( $file)
True if handled types cannot be displayed directly in a browser but can be rendered.
getScriptParams( $params)
makeParamString( $params)
getCommonMetaArray(File $file)
Get an array of standard (FormatMetadata type) metadata values.
doTransform( $image, $dstPath, $dstUrl, $params, $flags=0)
validateMetadata( $unser)
getLanguageFromParams(array $params)
Determines render language from image parameters This is a lowercase IETF language.
getAvailableLanguages(File $file)
Which languages (systemLanguage attribute) is supported.
getLongDesc( $file)
Subtitle for the image.
normaliseParamsInternal( $image, $params)
Code taken out of normaliseParams() for testability.
getMetadataType( $image)
Get a string describing the type of metadata, for display purposes.
getThumbType( $ext, $mime, $params=null)
Get the thumbnail extension and MIME type for a given source MIME type.
getDefaultRenderLanguage(File $file)
What language to render file in if none selected.
rasterize( $srcPath, $dstPath, $width, $height, $lang=false)
Transform an SVG file to PNG This function can be called outside of thumbnail contexts.
getSizeAndMetadata( $state, $filename)
isEnabled()
False if the handler is disabled for all files.
canAnimateThumbnail( $file)
We do not support making animated svg thumbnails.
visibleMetadataFields()
Get a list of metadata items which should be displayed when the metadata table is collapsed.
static rasterizeImagickExt( $srcPath, $dstPath, $width, $height)
isFileMetadataValid( $image)
Check if the metadata is valid for this handler.
getMatchedLanguage( $userPreferredLanguage, array $svgLanguages)
SVG's systemLanguage matching rules state: 'The systemLanguage attribute ... [e]valuates to "true" if...
const SVG_METADATA_VERSION
allowRenderingByUserAgent( $file)
getParamMap()
Get an associative array mapping magic word IDs to parameter names.Will be used by the parser to iden...
Media transform output for images.
Interface for objects which can provide a MediaWiki context on request.