52 if ( !parent::normaliseParams( $image,
$params ) ) {
56 # Obtain the source, pre-rotation dimensions
57 $srcWidth = $image->getWidth(
$params[
'page'] );
58 $srcHeight = $image->getHeight(
$params[
'page'] );
60 # Don't make an image bigger than the source
61 if (
$params[
'physicalWidth'] >= $srcWidth ) {
62 $params[
'physicalWidth'] = $srcWidth;
63 $params[
'physicalHeight'] = $srcHeight;
65 # Skip scaling limit checks if no scaling is required
66 # due to requested size being bigger than source.
67 if ( !$image->mustRender() ) {
88 if ( $rotation === 90 || $rotation === 270 ) {
90 $width =
$params[
'physicalHeight'];
91 $height =
$params[
'physicalWidth'];
93 $width =
$params[
'physicalWidth'];
94 $height =
$params[
'physicalHeight'];
97 return [ $width, $height ];
122 'physicalWidth' =>
$params[
'physicalWidth'],
123 'physicalHeight' =>
$params[
'physicalHeight'],
124 'physicalDimensions' =>
"{$params['physicalWidth']}x{$params['physicalHeight']}",
126 'clientWidth' =>
$params[
'width'],
127 'clientHeight' =>
$params[
'height'],
129 'comment' => isset(
$params[
'descriptionUrl'] )
130 ?
"File source: {$params['descriptionUrl']}"
133 'srcWidth' => $image->getWidth(),
134 'srcHeight' => $image->getHeight(),
135 'mimeType' => $image->getMimeType(),
136 'dstPath' => $dstPath,
138 'interlace' =>
$params[
'interlace'] ??
false,
139 'isFilePageThumb' =>
$params[
'isFilePageThumb'] ??
false,
142 if ( isset(
$params[
'quality'] ) &&
$params[
'quality'] ===
'low' ) {
143 $scalerParams[
'quality'] = 30;
147 if ( $image->isMultipage() && isset(
$params[
'page'] ) ) {
148 $scalerParams[
'page'] = (int)
$params[
'page'];
151 # Determine scaler type
154 if ( is_array( $scaler ) ) {
155 $scalerName = get_class( $scaler[0] );
157 $scalerName = $scaler;
160 wfDebug( __METHOD__ .
": creating {$scalerParams['physicalDimensions']} " .
161 "thumbnail of {$image->getPath()} at $dstPath using scaler $scalerName" );
163 if ( !$image->mustRender() &&
164 $scalerParams[
'physicalWidth'] == $scalerParams[
'srcWidth']
165 && $scalerParams[
'physicalHeight'] == $scalerParams[
'srcHeight']
166 && !isset( $scalerParams[
'quality'] )
168 # normaliseParams (or the user) wants us to return the unscaled image
169 wfDebug( __METHOD__ .
": returning unscaled image" );
174 if ( $scaler ===
'client' ) {
175 # Client-side image scaling, use the source URL
176 # Using the destination URL in a TRANSFORM_LATER request would be incorrect
180 if ( $image->isTransformedLocally() && !$this->isImageAreaOkForThumbnaling( $image,
$params ) ) {
181 $maxImageArea = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::MaxImageArea );
185 if ( $flags & self::TRANSFORM_LATER ) {
186 wfDebug( __METHOD__ .
": Transforming later per flags." );
188 'width' => $scalerParams[
'clientWidth'],
189 'height' => $scalerParams[
'clientHeight']
191 if ( isset(
$params[
'quality'] ) ) {
192 $newParams[
'quality'] =
$params[
'quality'];
195 $newParams[
'page'] =
$params[
'page'];
200 # Try to make a target path for the thumbnail
202 wfDebug( __METHOD__ .
": Unable to create thumbnail destination " .
203 "directory, falling back to client scaling" );
208 # Transform functions and binaries need a FS source file
212 if ( $scalerParams[
'srcWidth'] != $thumbnailSource[
'width']
213 || $scalerParams[
'srcHeight'] != $thumbnailSource[
'height'] ) {
214 $scalerParams[
'disableRotation'] =
true;
217 $scalerParams[
'srcPath'] = $thumbnailSource[
'path'];
218 $scalerParams[
'srcWidth'] = $thumbnailSource[
'width'];
219 $scalerParams[
'srcHeight'] = $thumbnailSource[
'height'];
221 if ( $scalerParams[
'srcPath'] ===
false ) {
223 sprintf(
'Thumbnail failed on %s: could not get local copy of "%s"',
227 $scalerParams[
'clientWidth'], $scalerParams[
'clientHeight'],
235 (
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
236 ->onBitmapHandlerTransform( $this, $image, $scalerParams, $mto );
237 if ( $mto !==
null ) {
238 wfDebug( __METHOD__ .
": Hook to BitmapHandlerTransform created an mto" );
239 $scaler =
'hookaborted';
245 if ( is_array( $scaler ) && is_callable( $scaler ) ) {
247 $err = call_user_func( $scaler, $image, $scalerParams );
251 # Handled by the hook above
252 $err = $mto->isError() ? $mto :
false;
265 $err = $this->
transformGd( $image, $scalerParams );
274 # transform returned MediaTransforError
281 $scalerParams[
'clientWidth'], $scalerParams[
'clientHeight'],
292 'width' => $scalerParams[
'clientWidth'],
293 'height' => $scalerParams[
'clientHeight']
295 if ( isset(
$params[
'quality'] ) ) {
296 $newParams[
'quality'] =
$params[
'quality'];
299 $newParams[
'page'] =
$params[
'page'];
301 return new ThumbnailImage( $image, $dstUrl, $dstPath, $newParams );
312 return $file->getThumbnailSource(
$params );
336 abstract protected function getScalerType( $dstPath, $checkDstPath =
true );
351 'width' => $scalerParams[
'clientWidth'],
352 'height' => $scalerParams[
'clientHeight']
355 $url = $image->getUrl();
356 if ( isset( $scalerParams[
'isFilePageThumb'] ) && $scalerParams[
'isFilePageThumb'] ) {
358 $url = $image->getFilePageThumbUrl(
$url );
418 $params[
'clientHeight'], $errMsg );
443 $s = str_replace(
'\\',
'\\\\', $s );
445 $s = str_replace(
'%',
'%%', $s );
447 if ( strlen( $s ) > 0 && ( $s[0] ===
'-' || $s[0] ===
'@' ) ) {
471 # Die on initial metacharacters (caller should prepend path)
472 $firstChar = substr(
$path, 0, 1 );
473 if ( $firstChar ===
'~' || $firstChar ===
'@' ) {
474 throw new InvalidArgumentException( __METHOD__ .
': cannot escape this path name' );
478 $path = preg_replace(
'/[*?\[\]{}]/',
'\\\\\0',
$path );
505 # Die on format specifiers (other than drive letters). The regex is
506 # meant to match all the formats you get from "convert -list format"
507 if ( preg_match(
'/^([a-zA-Z0-9-]+):/',
$path, $m ) ) {
512 throw new InvalidArgumentException( __METHOD__ .
': unexpected colon character in path name' );
516 # If there are square brackets, add a do-nothing scene specification
517 # to force a literal interpretation
518 if ( $scene ===
false ) {
519 if ( strpos(
$path,
'[' ) !==
false ) {
536 $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
537 $method = __METHOD__;
538 return $cache->getWithSetCallback(
539 $cache->makeGlobalKey(
'imagemagick-version' ),
541 static function () use ( $method ) {
542 $imageMagickConvertCommand = MediaWikiServices::getInstance()
543 ->getMainConfig()->get( MainConfigNames::ImageMagickConvertCommand );
545 $cmd = Shell::escape( $imageMagickConvertCommand ) .
' -version';
546 wfDebug( $method .
": Running convert -version" );
550 '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', $return,
$matches
553 wfDebug( $method .
": ImageMagick version check failed" );
599 static::class .
' rotation not implemented' );
626 $maxImageArea = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::MaxImageArea );
628 # For historical reasons, hook starts with BitmapHandler
629 $checkImageAreaHookResult =
null;
630 (
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onBitmapHandlerCheckImageArea(
631 $file,
$params, $checkImageAreaHookResult );
633 if ( $checkImageAreaHookResult !==
null ) {
635 return (
bool)$checkImageAreaHookResult;
638 if ( $maxImageArea ===
false ) {
644 $srcWidth = $file->getWidth(
$params[
'page'] );
646 $srcHeight = $file->getHeight(
$params[
'page'] );
648 if ( $srcWidth * $srcHeight > $maxImageArea
649 && !( $file->getMimeType() ===
'image/jpeg'
650 && $this->getScalerType(
null,
false ) ===
'im' )
652 # Only ImageMagick can efficiently downsize jpg images without loading
653 # the entire file in memory
wfIsWindows()
Check if the operating system is Windows.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
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.
Media handler abstract base class for images.
A class containing constants representing the names of configuration variables.
Media transform output for images.