MediaWiki master
ImageHandler.php
Go to the documentation of this file.
1<?php
10namespace MediaWiki\Media;
11
15
23abstract class ImageHandler extends MediaHandler {
30 public function canRender( $file ) {
31 return ( $file->getWidth() && $file->getHeight() );
32 }
33
39 public function getParamMap() {
40 return [ 'img_width' => 'width' ];
41 }
42
47 public function validateParam( $name, $value ) {
48 return in_array( $name, [ 'width', 'height' ] ) && (int)$value > 0;
49 }
50
56 public function makeParamString( $params ) {
57 if ( isset( $params['physicalWidth'] ) ) {
58 $width = $params['physicalWidth'];
59 } elseif ( isset( $params['width'] ) ) {
60 $width = $params['width'];
61 } else {
62 throw new MediaTransformInvalidParametersException( 'No width specified to ' . __METHOD__ );
63 }
64
65 # Removed for ProofreadPage
66 # $width = intval( $width );
67 return "{$width}px";
68 }
69
74 public function parseParamString( $str ) {
75 $m = false;
76 if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
77 return [ 'width' => $m[1] ];
78 }
79 return false;
80 }
81
87 protected function getScriptParams( $params ) {
88 return [ 'width' => $params['width'] ];
89 }
90
99 public function normaliseParams( $image, &$params ) {
100 if ( !isset( $params['width'] ) ) {
101 return false;
102 }
103
104 if ( !isset( $params['page'] ) ) {
105 $params['page'] = 1;
106 } else {
107 $params['page'] = (int)$params['page'];
108 if ( $params['page'] > $image->pageCount() ) {
109 $params['page'] = $image->pageCount();
110 }
111
112 if ( $params['page'] < 1 ) {
113 $params['page'] = 1;
114 }
115 }
116
117 $srcWidth = $image->getWidth( $params['page'] );
118 $srcHeight = $image->getHeight( $params['page'] );
119
120 if ( isset( $params['height'] ) && $params['height'] !== -1 ) {
121 # Height & width were both set
122 if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
123 # Height is the relative smaller dimension, so scale width accordingly
124 $params['width'] = self::fitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
125
126 if ( $params['width'] == 0 ) {
127 # Very small image, so we need to rely on client side scaling :(
128 $params['width'] = 1;
129 }
130
131 $params['physicalWidth'] = $params['width'];
132 } else {
133 # Height was crap, unset it so that it will be calculated later
134 unset( $params['height'] );
135 }
136 }
137
138 if ( !isset( $params['physicalWidth'] ) ) {
139 # Passed all validations, so set the physicalWidth
140 $params['physicalWidth'] = $params['width'];
141 }
142
143 # Because thumbs are only referred to by width, the height always needs
144 # to be scaled by the width to keep the thumbnail sizes consistent,
145 # even if it was set inside the if block above
146 $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight,
147 $params['physicalWidth'] );
148
149 # Set the height if it was not validated in the if block higher up
150 if ( !isset( $params['height'] ) || $params['height'] === -1 ) {
151 $params['height'] = $params['physicalHeight'];
152 }
153
154 if ( !$this->validateThumbParams( $params['physicalWidth'],
155 $params['physicalHeight'], $srcWidth, $srcHeight )
156 ) {
157 return false;
158 }
159
160 return true;
161 }
162
172 protected function getSteppedThumbWidth(
173 File $image, int $requestWidth, int $srcWidth, int $srcHeight
174 ): int {
175 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
176 $thumbnailSteps = $mainConfig->get( MainConfigNames::ThumbnailSteps );
177
178 if ( !$thumbnailSteps ) {
179 return $requestWidth;
180 }
181
182 $prevStep = $thumbnailSteps[0];
183 foreach ( $thumbnailSteps as $widthStep ) {
184 if ( ( $widthStep > $srcWidth ) && !$image->isVectorized() ) {
185 if ( $this->mustRender( $image ) ) {
186 // Not web-safe: Round down to previous step
187 //
188 // While unlikely, this tries to upscale an original that is smaller than the smallest step
189 // (e.g. smaller than 20px if that's the smallest step configured), which is disallowed
190 // by default in MediaWiki core with HTTP 400 (ThumbnailEntryPoint::generateThumbnail).
191 return $prevStep;
192 } else {
193 // Web-safe: Round up or down and use the original
194 //
195 // There was no step between the requested width and the original width
196 // (so either a thumb between penultimate step and original,
197 // or a thumb beyond original jpg but under penultimate step).
198 //
199 // NOTE: This thumb-at-original-width is replaced with the original
200 // in a higher-level code path after this method runs.
201 return $srcWidth;
202 }
203 }
204 if ( $widthStep == $requestWidth ) {
205 return $requestWidth;
206 }
207 if ( $widthStep > $requestWidth ) {
208 return $widthStep;
209 }
210 $prevStep = $widthStep;
211 }
212
213 // Respond with the largest step if it's too big.
214 return $prevStep;
215 }
216
226 private function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight ) {
227 $width = (int)$width;
228
229 if ( $width <= 0 ) {
230 wfDebug( __METHOD__ . ": Invalid destination width: $width" );
231
232 return false;
233 }
234 if ( $srcWidth <= 0 ) {
235 wfDebug( __METHOD__ . ": Invalid source width: $srcWidth" );
236
237 return false;
238 }
239
240 $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
241 if ( $height == 0 ) {
242 # Force height to be at least 1 pixel
243 $height = 1;
244 }
245
246 return true;
247 }
248
257 public function getScriptedTransform( $image, $script, $params ) {
258 if ( !$this->normaliseParams( $image, $params ) ) {
259 return false;
260 }
261 $url = wfAppendQuery( $script, $this->getScriptParams( $params ) );
262
263 if ( $image->mustRender() || $params['width'] < $image->getWidth() ) {
264 return new ThumbnailImage( $image, $url, false, $params );
265 }
266 }
267
269 public function getImageSize( $image, $path ) {
270 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
271 return @getimagesize( $path );
272 }
273
275 public function getSizeAndMetadata( $state, $path ) {
276 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
277 $gis = @getimagesize( $path );
278 if ( $gis ) {
279 $info = [
280 'width' => $gis[0],
281 'height' => $gis[1],
282 ];
283 if ( isset( $gis['bits'] ) ) {
284 $info['bits'] = $gis['bits'];
285 }
286 } else {
287 $info = [];
288 }
289 return $info;
290 }
291
302 public function getImageArea( $image ) {
303 return $image->getWidth() * $image->getHeight();
304 }
305
312 public function getShortDesc( $file ) {
313 $lang = $this->getLanguage();
314 $nbytes = htmlspecialchars( $lang->formatSize( $file->getSize() ), ENT_QUOTES );
315 $widthheight = wfMessage( 'widthheight' )
316 ->numParams( $file->getWidth(), $file->getHeight() )
317 ->inLanguage( $lang )
318 ->escaped();
319
320 return "$widthheight ($nbytes)";
321 }
322
329 public function getLongDesc( $file ) {
330 $pages = $file->pageCount();
331 if ( $pages === false || $pages <= 1 ) {
332 $msg = wfMessage( 'file-info-size' )
333 ->numParams( $file->getWidth(), $file->getHeight() )
334 ->sizeParams( $file->getSize() )
335 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )
336 ->inLanguage( $this->getLanguage() )
337 ->parse();
338 } else {
339 $msg = wfMessage( 'file-info-size-pages' )
340 ->numParams( $file->getWidth(), $file->getHeight() )
341 ->sizeParams( $file->getSize() )
342 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->numParams( $pages )
343 ->inLanguage( $this->getLanguage() )
344 ->parse();
345 }
346
347 return $msg;
348 }
349
356 public function getDimensionsString( $file ) {
357 $pages = $file->pageCount();
358 $lang = $this->getLanguage();
359 if ( $pages > 1 ) {
360 return wfMessage( 'widthheightpage' )
361 ->numParams( $file->getWidth(), $file->getHeight(), $pages )
362 ->inLanguage( $lang )->text();
363 }
364 return wfMessage( 'widthheight' )
365 ->numParams( $file->getWidth(), $file->getHeight() )->inLanguage( $lang )->text();
366 }
367
372 public function sanitizeParamsForBucketing( $params ) {
373 $params = parent::sanitizeParamsForBucketing( $params );
374
375 // We unset the height parameters in order to let normaliseParams recalculate them
376 // Otherwise there might be a height discrepancy
377 unset( $params['height'] );
378 unset( $params['physicalHeight'] );
379
380 return $params;
381 }
382}
383
385class_alias( ImageHandler::class, 'ImageHandler' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
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...
Definition File.php:80
pageCount()
Returns the number of pages of a multipage document, or false for documents which aren't multipage do...
Definition File.php:2244
isVectorized()
Return true if the file is vectorized.
Definition File.php:710
getHeight( $page=1)
Return the height of the image.
Definition File.php:601
getWidth( $page=1)
Return the width of the image.
Definition File.php:586
mustRender()
Return true if the file is of a type that can't be directly rendered by typical browsers and needs to...
Definition File.php:968
A class containing constants representing the names of configuration variables.
const ThumbnailSteps
Name constant for the ThumbnailSteps setting, for use with Config::get()
Service locator for MediaWiki core services.
Media handler abstract base class for images.
getSizeAndMetadata( $state, $path)
Get image size information and metadata array.If this returns null, the caller will fall back to getI...
makeParamString( $params)
Merge a parameter array into a string appropriate for inclusion in filenames.string|false
canRender( $file)
True if the handled types can be transformed.to overridebool
sanitizeParamsForBucketing( $params)
Returns a normalised params array for which parameters have been cleaned up for bucketing purposes....
getImageArea( $image)
Function that returns the number of pixels to be thumbnailed.
getShortDesc( $file)
Short description.Shown on Special:Search results.Until MediaWiki 1.45, the return value was poorly d...
parseParamString( $str)
Parse a param string made with makeParamString back into an array.array|false Array of parameters or ...
getImageSize( $image, $path)
Get an image size array like that returned by getimagesize(), or false if it can't be determined....
getLongDesc( $file)
Long description.Shown under image on image description page surrounded by ().Until MediaWiki 1....
getSteppedThumbWidth(File $image, int $requestWidth, int $srcWidth, int $srcHeight)
Adjust the thumbnail size to fit the width steps defined in config via $wgThumbnailSteps.
validateParam( $name, $value)
Validate a thumbnail parameter at parse time.Return true to accept the parameter, and false to reject...
getScriptedTransform( $image, $script, $params)
Get a MediaTransformOutput object representing an alternate of the transformed output which will call...
getParamMap()
Get an associative array mapping magic word IDs to parameter names.Will be used by the parser to iden...
getDimensionsString( $file)
Shown in file history box on image description page.to overridestring Dimensions (plain text)
normaliseParams( $image, &$params)
Changes the parameter array as necessary, ready for transformation.Should be idempotent....
Base media handler class.
mustRender( $file)
True if handled types cannot be displayed directly in a browser but can be rendered.
static fitBoxWidth( $boxWidth, $boxHeight, $maxHeight)
Calculate the largest thumbnail width for a given original file size such that the thumbnail's height...
MediaWiki exception thrown by some methods when the transform parameter array is invalid.
Media transform output for images.