MediaWiki REL1_32
JpegHandler.php
Go to the documentation of this file.
1<?php
34 const SRGB_EXIF_COLOR_SPACE = 'sRGB';
35 const SRGB_ICC_PROFILE_DESCRIPTION = 'sRGB IEC61966-2.1';
36
38 if ( !parent::normaliseParams( $image, $params ) ) {
39 return false;
40 }
41 if ( isset( $params['quality'] ) && !self::validateQuality( $params['quality'] ) ) {
42 return false;
43 }
44 return true;
45 }
46
47 public function validateParam( $name, $value ) {
48 if ( $name === 'quality' ) {
50 } else {
51 return parent::validateParam( $name, $value );
52 }
53 }
54
59 private static function validateQuality( $value ) {
60 return $value === 'low';
61 }
62
63 public function makeParamString( $params ) {
64 // Prepend quality as "qValue-". This has to match parseParamString() below
65 $res = parent::makeParamString( $params );
66 if ( $res && isset( $params['quality'] ) ) {
67 $res = "q{$params['quality']}-$res";
68 }
69 return $res;
70 }
71
72 public function parseParamString( $str ) {
73 // $str contains "qlow-200px" or "200px" strings because thumb.php would strip the filename
74 // first - check if the string begins with "qlow-", and if so, treat it as quality.
75 // Pass the first portion, or the whole string if "qlow-" not found, to the parent
76 // The parsing must match the makeParamString() above
77 $res = false;
78 $m = false;
79 if ( preg_match( '/q([^-]+)-(.*)$/', $str, $m ) ) {
80 $v = $m[1];
81 if ( self::validateQuality( $v ) ) {
82 $res = parent::parseParamString( $m[2] );
83 if ( $res ) {
84 $res['quality'] = $v;
85 }
86 }
87 } else {
88 $res = parent::parseParamString( $str );
89 }
90 return $res;
91 }
92
94 $res = parent::getScriptParams( $params );
95 if ( isset( $params['quality'] ) ) {
96 $res['quality'] = $params['quality'];
97 }
98 return $res;
99 }
100
101 function getMetadata( $image, $filename ) {
102 try {
103 $meta = BitmapMetadataHandler::Jpeg( $filename );
104 if ( !is_array( $meta ) ) {
105 // This should never happen, but doesn't hurt to be paranoid.
106 throw new MWException( 'Metadata array is not an array' );
107 }
108 $meta['MEDIAWIKI_EXIF_VERSION'] = Exif::version();
109
110 return serialize( $meta );
111 } catch ( Exception $e ) {
112 // BitmapMetadataHandler throws an exception in certain exceptional
113 // cases like if file does not exist.
114 wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" );
115
116 /* This used to use 0 (ExifBitmapHandler::OLD_BROKEN_FILE) for the cases
117 * * No metadata in the file
118 * * Something is broken in the file.
119 * However, if the metadata support gets expanded then you can't tell if the 0 is from
120 * a broken file, or just no props found. A broken file is likely to stay broken, but
121 * a file which had no props could have props once the metadata support is improved.
122 * Thus switch to using -1 to denote only a broken file, and use an array with only
123 * MEDIAWIKI_EXIF_VERSION to denote no props.
124 */
125
127 }
128 }
129
137 public function rotate( $file, $params ) {
138 global $wgJpegTran;
139
140 $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;
141
142 if ( $wgJpegTran && is_executable( $wgJpegTran ) ) {
143 $cmd = wfEscapeShellArg( $wgJpegTran ) .
144 " -rotate " . wfEscapeShellArg( $rotation ) .
145 " -outfile " . wfEscapeShellArg( $params['dstPath'] ) .
146 " " . wfEscapeShellArg( $params['srcPath'] );
147 wfDebug( __METHOD__ . ": running jpgtran: $cmd\n" );
148 $retval = 0;
149 $err = wfShellExecWithStderr( $cmd, $retval );
150 if ( $retval !== 0 ) {
151 $this->logErrorForExternalProcess( $retval, $err, $cmd );
152
153 return new MediaTransformError( 'thumbnail_error', 0, 0, $err );
154 }
155
156 return false;
157 } else {
158 return parent::rotate( $file, $params );
159 }
160 }
161
162 public function supportsBucketing() {
163 return true;
164 }
165
167 $params = parent::sanitizeParamsForBucketing( $params );
168
169 // Quality needs to be cleared for bucketing. Buckets need to be default quality
170 if ( isset( $params['quality'] ) ) {
171 unset( $params['quality'] );
172 }
173
174 return $params;
175 }
176
180 protected function transformImageMagick( $image, $params ) {
182
183 $ret = parent::transformImageMagick( $image, $params );
184
185 if ( $ret ) {
186 return $ret;
187 }
188
190 // T100976 If the profile embedded in the JPG is sRGB, swap it for the smaller
191 // (and free) TinyRGB
192
202 $colorSpaces = [ self::SRGB_EXIF_COLOR_SPACE, '-' ];
204
205 // we'll also add TinyRGB profile to images lacking a profile, but
206 // only if they're not low quality (which are meant to save bandwith
207 // and we don't want to increase the filesize by adding a profile)
208 if ( isset( $params['quality'] ) && $params['quality'] > 30 ) {
209 $profiles[] = '-';
210 }
211
212 $this->swapICCProfile(
213 $params['dstPath'],
214 $colorSpaces,
215 $profiles,
216 realpath( __DIR__ ) . '/tinyrgb.icc'
217 );
218 }
219
220 return false;
221 }
222
234 public function swapICCProfile( $filepath, array $colorSpaces,
235 array $oldProfileStrings, $profileFilepath
236 ) {
237 global $wgExiftool;
238
239 if ( !$wgExiftool || !is_executable( $wgExiftool ) ) {
240 return false;
241 }
242
244 '-EXIF:ColorSpace',
245 '-ICC_Profile:ProfileDescription',
246 '-S',
247 '-T',
248 $filepath
249 );
250
252
253 // Explode EXIF data into an array with [0 => Color Space, 1 => Device Model Desc]
254 $data = explode( "\t", trim( $output ) );
255
256 if ( $retval !== 0 ) {
257 return false;
258 }
259
260 // Make a regex out of the source data to match it to an array of color
261 // spaces in a case-insensitive way
262 $colorSpaceRegex = '/' . preg_quote( $data[0], '/' ) . '/i';
263 if ( empty( preg_grep( $colorSpaceRegex, $colorSpaces ) ) ) {
264 // We can't establish that this file matches the color space, don't process it
265 return false;
266 }
267
268 $profileRegex = '/' . preg_quote( $data[1], '/' ) . '/i';
269 if ( empty( preg_grep( $profileRegex, $oldProfileStrings ) ) ) {
270 // We can't establish that this file has the expected ICC profile, don't process it
271 return false;
272 }
273
275 '-overwrite_original',
276 '-icc_profile<=' . $profileFilepath,
277 $filepath
278 );
279
281
282 if ( $retval !== 0 ) {
284
285 return false;
286 }
287
288 return true;
289 }
290}
serialize()
$wgUseTinyRGBForJPGThumbnails
When this variable is true and JPGs use the sRGB ICC profile, swaps it for the more lightweight (and ...
$wgJpegTran
used for lossless jpeg rotation
$wgExiftool
Path to exiftool binary.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfEscapeShellArg(... $args)
Version of escapeshellarg() that works better on Windows.
wfShellExecWithStderr( $cmd, &$retval=null, $environ=[], $limits=[])
Execute a shell command, returning both stdout and stderr.
static Jpeg( $filename)
Main entry point for jpeg's.
Stuff specific to JPEG and (built-in) TIFF handler.
getRotation( $file)
On supporting image formats, try to read out the low-level orientation of the file and return the ang...
static version()
#-
Definition Exif.php:582
JPEG specific handler.
makeParamString( $params)
Merge a parameter array into a string appropriate for inclusion in filenames.
validateParam( $name, $value)
Validate a thumbnail parameter at parse time.
const SRGB_ICC_PROFILE_DESCRIPTION
getScriptParams( $params)
getMetadata( $image, $filename)
Get handler-specific metadata which will be saved in the img_metadata field.
swapICCProfile( $filepath, array $colorSpaces, array $oldProfileStrings, $profileFilepath)
Swaps an embedded ICC profile for another, if found.
static validateQuality( $value)
Validate and normalize quality value to be between 1 and 100 (inclusive).
supportsBucketing()
Returns whether or not this handler supports the chained generation of thumbnails according to bucket...
rotate( $file, $params)
const SRGB_EXIF_COLOR_SPACE
parseParamString( $str)
Parse a param string made with makeParamString back into an array.
sanitizeParamsForBucketing( $params)
Returns a normalised params array for which parameters have been cleaned up for bucketing purposes.
normaliseParams( $image, &$params)
transformImageMagick( $image, $params)
@inheritDoc
MediaWiki exception.
logErrorForExternalProcess( $retval, $err, $cmd)
Log an error that occurred in an external process.
Basic media transform error class.
$res
Definition database.txt:21
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account incomplete not yet checked for validity & $retval
Definition hooks.txt:266
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check $image
Definition hooks.txt:925
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition hooks.txt:2054
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place $output
Definition hooks.txt:2317
returning false will NOT prevent logging $e
Definition hooks.txt:2226
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
$params