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;
352 [
'$path/',
'$width',
'$height',
'$input',
'$output' ],
353 [ $svgConverterPath ? Shell::escape(
"{$svgConverterPath}/" ) :
"",
356 Shell::escape( $srcPath ),
357 Shell::escape( $dstPath ) ],
358 $svgConverters[$svgConverter]
362 if (
$lang !==
false ) {
363 $env[
'LANG'] =
$lang;
366 wfDebug( __METHOD__ .
": $cmd" );
371 if ( $retval != 0 || $removed ) {
382 $im =
new Imagick( $srcPath );
383 $im->setBackgroundColor(
'transparent' );
384 $im->readImage( $srcPath );
385 $im->setImageFormat(
'png' );
386 $im->setImageDepth( 8 );
388 if ( !$im->thumbnailImage( intval( $width ), intval( $height ),
false ) ) {
389 return 'Could not resize image';
391 if ( !$im->writeImage( $dstPath ) ) {
392 return "Could not write to $dstPath";
397 return [
'png',
'image/png' ];
411 if ( isset( $metadata[
'error'] ) ) {
412 return wfMessage(
'svg-long-error', $metadata[
'error'][
'message'] )->text();
416 $msg =
wfMessage(
'svg-long-desc-animated' );
421 return $msg->numParams(
$file->getWidth(),
$file->getHeight() )->sizeParams(
$file->getSize() )->parse();
430 $metadata = [
'version' => self::SVG_METADATA_VERSION ];
434 $metadata += $svgReader->getMetadata();
435 }
catch ( TimeoutException $e ) {
437 }
catch ( Exception $e ) {
439 $metadata[
'error'] = [
440 'message' => $e->getMessage(),
441 'code' => $e->getCode()
443 wfDebug( __METHOD__ .
': ' . $e->getMessage() );
447 'width' => $metadata[
'width'] ?? 0,
448 'height' => $metadata[
'height'] ?? 0,
449 'metadata' => $metadata
454 if ( isset( $unser[
'version'] ) && $unser[
'version'] == self::SVG_METADATA_VERSION ) {
468 return self::METADATA_BAD;
470 if ( !isset( $meta[
'originalWidth'] ) ) {
472 return self::METADATA_COMPATIBLE;
475 return self::METADATA_GOOD;
479 $fields = [
'objectname',
'imagedescription' ];
495 if ( !$metadata || isset( $metadata[
'error'] ) ) {
508 foreach ( $metadata as $name => $value ) {
509 $tag = strtolower( $name );
510 if ( isset( self::$metaConversion[$tag] ) ) {
511 $tag = strtolower( self::$metaConversion[$tag] );
517 self::addMeta( $result,
518 in_array( $tag, $visibleFields ) ?
'visible' :
'collapsed',
525 return $showMeta ? $result :
false;
534 if ( in_array( $name, [
'width',
'height' ] ) ) {
536 return ( $value > 0 );
538 if ( $name ==
'lang' ) {
541 || !LanguageCode::isWellFormedLanguageTag( $value )
560 if ( $code !== self::SVG_DEFAULT_RENDER_LANG ) {
561 $lang =
'lang' . strtolower( $code ) .
'-';
563 if ( !isset( $params[
'width'] ) ) {
567 return "$lang{$params['width']}px";
573 if ( preg_match(
'/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) {
574 if ( LanguageCode::isWellFormedLanguageTag( $m[1] ) ) {
575 return [
'width' => array_pop( $m ),
'lang' => $m[1] ];
577 return [
'width' => array_pop( $m ),
'lang' => self::SVG_DEFAULT_RENDER_LANG ];
579 if ( preg_match(
'/^(\d+)px$/', $str, $m ) ) {
580 return [
'width' => $m[1],
'lang' => self::SVG_DEFAULT_RENDER_LANG ];
586 return [
'img_lang' =>
'lang',
'img_width' =>
'width' ];
594 $scriptParams = [
'width' => $params[
'width'] ];
595 if ( isset( $params[
'lang'] ) ) {
596 $scriptParams[
'lang'] = $params[
'lang'];
599 return $scriptParams;
604 if ( !$metadata || isset( $metadata[
'error'] ) ) {
608 foreach ( $metadata as $name => $value ) {
609 $tag = strtolower( $name );
610 if ( $tag ===
'originalwidth' || $tag ===
'originalheight' ) {
615 if ( isset( self::$metaConversion[$tag] ) ) {
616 $tag = self::$metaConversion[$tag];
617 $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