27use Wikimedia\AtEase\AtEase;
28use Wikimedia\RequestTimeout\TimeoutException;
29use Wikimedia\ScopedCallback;
39 private const SVG_DEFAULT_RENDER_LANG =
'en';
45 private static $metaConversion = [
46 'originalwidth' =>
'ImageWidth',
47 'originalheight' =>
'ImageLength',
48 'description' =>
'ImageDescription',
49 'title' =>
'ObjectName',
53 $svgConverters = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::SVGConverters );
54 $svgConverter = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::SVGConverter );
55 if ( !isset( $svgConverters[$svgConverter] ) ) {
56 wfDebug(
"\$wgSVGConverter is invalid, disabling SVG rendering." );
77 # @todo Detect animated SVGs
79 if ( isset( $metadata[
'animated'] ) ) {
80 return $metadata[
'animated'];
101 if ( isset( $metadata[
'translations'] ) ) {
102 foreach ( $metadata[
'translations'] as
$lang => $langType ) {
103 if ( $langType === SVGReader::LANG_FULL_MATCH ) {
104 $langList[] = strtolower(
$lang );
108 return array_unique( $langList );
128 if ( $userPreferredLanguage ===
'und' ) {
131 foreach ( $svgLanguages as $svgLang ) {
132 if ( strcasecmp( $svgLang, $userPreferredLanguage ) === 0 ) {
135 $trimmedSvgLang = $svgLang;
136 while ( strpos( $trimmedSvgLang,
'-' ) !==
false ) {
137 $trimmedSvgLang = substr( $trimmedSvgLang, 0, strrpos( $trimmedSvgLang,
'-' ) );
138 if ( strcasecmp( $trimmedSvgLang, $userPreferredLanguage ) === 0 ) {
154 return $params[
'lang'] ?? $params[
'targetlang'] ?? self::SVG_DEFAULT_RENDER_LANG;
164 return self::SVG_DEFAULT_RENDER_LANG;
182 if ( parent::normaliseParams( $image, $params ) ) {
200 $svgMaxSize = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::SVGMaxSize );
202 # Don't make an image bigger than wgMaxSVGSize on the smaller side
203 if ( $params[
'physicalWidth'] <= $params[
'physicalHeight'] ) {
204 if ( $params[
'physicalWidth'] > $svgMaxSize ) {
205 $srcWidth = $image->getWidth( $params[
'page'] );
206 $srcHeight = $image->getHeight( $params[
'page'] );
207 $params[
'physicalWidth'] = $svgMaxSize;
208 $params[
'physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight, $svgMaxSize );
210 } elseif ( $params[
'physicalHeight'] > $svgMaxSize ) {
211 $srcWidth = $image->getWidth( $params[
'page'] );
212 $srcHeight = $image->getHeight( $params[
'page'] );
213 $params[
'physicalWidth'] = File::scaleHeight( $srcHeight, $srcWidth, $svgMaxSize );
214 $params[
'physicalHeight'] = $svgMaxSize;
218 if ( isset( $params[
'targetlang'] ) && !$image->getMatchedLanguage( $params[
'targetlang'] ) ) {
219 unset( $params[
'targetlang'] );
233 public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
237 $clientWidth = $params[
'width'];
238 $clientHeight = $params[
'height'];
239 $physicalWidth = $params[
'physicalWidth'];
240 $physicalHeight = $params[
'physicalHeight'];
243 if ( $flags & self::TRANSFORM_LATER ) {
248 if ( isset( $metadata[
'error'] ) ) {
249 $err =
wfMessage(
'svg-long-error', $metadata[
'error'][
'message'] );
256 wfMessage(
'thumbnail_dest_directory' ) );
259 $srcPath = $image->getLocalRefPath();
260 if ( $srcPath ===
false ) {
262 sprintf(
'Thumbnail failed on %s: could not get local copy of "%s"',
266 $params[
'width'], $params[
'height'],
275 $lnPath =
"$tmpDir/" . basename( $srcPath );
276 $ok = mkdir( $tmpDir, 0771 );
279 sprintf(
'Thumbnail failed on %s: could not create temporary directory %s',
282 $params[
'width'], $params[
'height'],
283 wfMessage(
'thumbnail-temp-create' )->text()
286 $ok = symlink( $srcPath, $lnPath );
288 $cleaner =
new ScopedCallback(
static function () use ( $tmpDir, $lnPath ) {
289 AtEase::suppressWarnings();
292 AtEase::restoreWarnings();
296 $ok = copy( $srcPath, $lnPath );
300 sprintf(
'Thumbnail failed on %s: could not link %s to %s',
303 $params[
'width'], $params[
'height'],
308 $status = $this->
rasterize( $lnPath, $dstPath, $physicalWidth, $physicalHeight,
$lang );
309 if ( $status ===
true ) {
327 public function rasterize( $srcPath, $dstPath, $width, $height,
$lang =
false ) {
328 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
329 $svgConverters = $mainConfig->get( MainConfigNames::SVGConverters );
330 $svgConverter = $mainConfig->get( MainConfigNames::SVGConverter );
331 $svgConverterPath = $mainConfig->get( MainConfigNames::SVGConverterPath );
334 if ( isset( $svgConverters[$svgConverter] ) ) {
335 if ( is_array( $svgConverters[$svgConverter] ) ) {
337 $func = $svgConverters[$svgConverter][0];
338 if ( !is_callable( $func ) ) {
341 $err = $func( $srcPath,
346 ...array_slice( $svgConverters[$svgConverter], 1 )
348 $retval = (bool)$err;
351 $path = $svgConverterPath ? Shell::escape(
"{$svgConverterPath}/" ) :
'';
352 $cmd = preg_replace_callback(
'/\$(path\/|width|height|input|output)/',
353 static function ( $m ) use (
$path, $width, $height, $srcPath, $dstPath ) {
356 '$width' => intval( $width ),
357 '$height' => intval( $height ),
358 '$input' => Shell::escape( $srcPath ),
359 '$output' => Shell::escape( $dstPath ),
362 $svgConverters[$svgConverter]
366 if (
$lang !==
false ) {
367 $env[
'LANG'] =
$lang;
370 wfDebug( __METHOD__ .
": $cmd" );
375 if ( $retval != 0 || $removed ) {
386 $im =
new Imagick( $srcPath );
387 $im->setBackgroundColor(
'transparent' );
388 $im->readImage( $srcPath );
389 $im->setImageFormat(
'png' );
390 $im->setImageDepth( 8 );
392 if ( !$im->thumbnailImage( (
int)$width, (
int)$height,
false ) ) {
393 return 'Could not resize image';
395 if ( !$im->writeImage( $dstPath ) ) {
396 return "Could not write to $dstPath";
401 return [
'png',
'image/png' ];
415 if ( isset( $metadata[
'error'] ) ) {
416 return wfMessage(
'svg-long-error', $metadata[
'error'][
'message'] )->text();
420 $msg =
wfMessage(
'svg-long-desc-animated' );
425 return $msg->numParams(
$file->getWidth(),
$file->getHeight() )->sizeParams(
$file->getSize() )->parse();
434 $metadata = [
'version' => self::SVG_METADATA_VERSION ];
438 $metadata += $svgReader->getMetadata();
439 }
catch ( TimeoutException $e ) {
441 }
catch ( Exception $e ) {
443 $metadata[
'error'] = [
444 'message' => $e->getMessage(),
445 'code' => $e->getCode()
447 wfDebug( __METHOD__ .
': ' . $e->getMessage() );
451 'width' => $metadata[
'width'] ?? 0,
452 'height' => $metadata[
'height'] ?? 0,
453 'metadata' => $metadata
458 if ( isset( $unser[
'version'] ) && $unser[
'version'] === self::SVG_METADATA_VERSION ) {
472 return self::METADATA_BAD;
474 if ( !isset( $meta[
'originalWidth'] ) ) {
476 return self::METADATA_COMPATIBLE;
479 return self::METADATA_GOOD;
483 return [
'objectname',
'imagedescription' ];
497 if ( !$metadata || isset( $metadata[
'error'] ) ) {
510 foreach ( $metadata as $name => $value ) {
511 $tag = strtolower( $name );
512 if ( isset( self::$metaConversion[$tag] ) ) {
513 $tag = strtolower( self::$metaConversion[$tag] );
519 self::addMeta( $result,
520 in_array( $tag, $visibleFields ) ?
'visible' :
'collapsed',
527 return $showMeta ? $result :
false;
536 if ( in_array( $name, [
'width',
'height' ] ) ) {
538 return ( $value > 0 );
540 if ( $name ===
'lang' ) {
543 || !LanguageCode::isWellFormedLanguageTag( $value )
562 if ( $code !== self::SVG_DEFAULT_RENDER_LANG ) {
563 $lang =
'lang' . strtolower( $code ) .
'-';
565 if ( !isset( $params[
'width'] ) ) {
569 return "$lang{$params['width']}px";
575 if ( preg_match(
'/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) {
576 if ( LanguageCode::isWellFormedLanguageTag( $m[1] ) ) {
577 return [
'width' => array_pop( $m ),
'lang' => $m[1] ];
579 return [
'width' => array_pop( $m ),
'lang' => self::SVG_DEFAULT_RENDER_LANG ];
581 if ( preg_match(
'/^(\d+)px$/', $str, $m ) ) {
582 return [
'width' => $m[1],
'lang' => self::SVG_DEFAULT_RENDER_LANG ];
588 return [
'img_lang' =>
'lang',
'img_width' =>
'width' ];
596 $scriptParams = [
'width' => $params[
'width'] ];
597 if ( isset( $params[
'lang'] ) ) {
598 $scriptParams[
'lang'] = $params[
'lang'];
601 return $scriptParams;
606 if ( !$metadata || isset( $metadata[
'error'] ) ) {
610 foreach ( $metadata as $name => $value ) {
611 $tag = strtolower( $name );
612 if ( $tag ===
'originalwidth' || $tag ===
'originalheight' ) {
617 if ( isset( self::$metaConversion[$tag] ) ) {
618 $tag = self::$metaConversion[$tag];
619 $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.
Implements some public methods and some protected utility functions which are required by multiple ch...
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
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.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!is_readable( $file)) $ext
if(!isset( $args[0])) $lang