summaryrefslogtreecommitdiff
path: root/includes/media/Jpeg.php
blob: fbdbdfe31e8f9c232bc71f7760250787542d29e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
<?php
/**
 * Handler for JPEG images.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @file
 * @ingroup Media
 */

/**
 * JPEG specific handler.
 * Inherits most stuff from BitmapHandler, just here to do the metadata handler differently.
 *
 * Metadata stuff common to Jpeg and built-in Tiff (not PagedTiffHandler) is
 * in ExifBitmapHandler.
 *
 * @ingroup Media
 */
class JpegHandler extends ExifBitmapHandler {

	function normaliseParams( $image, &$params ) {
		if ( !parent::normaliseParams( $image, $params ) ) {
			return false;
		}
		if ( isset( $params['quality'] ) && !self::validateQuality( $params['quality'] ) ) {
			return false;
		}
		return true;
	}

	function validateParam( $name, $value ) {
		if ( $name === 'quality' ) {
			return self::validateQuality( $value );
		} else {
			return parent::validateParam( $name, $value );
		}
	}

	/** Validate and normalize quality value to be between 1 and 100 (inclusive).
	 * @param int $value Quality value, will be converted to integer or 0 if invalid
	 * @return bool True if the value is valid
	 */
	private static function validateQuality( $value ) {
		return $value === 'low';
	}

	function makeParamString( $params ) {
		// Prepend quality as "qValue-". This has to match parseParamString() below
		$res = parent::makeParamString( $params );
		if ( $res && isset( $params['quality'] ) ) {
			$res = "q{$params['quality']}-$res";
		}
		return $res;
	}

	function parseParamString( $str ) {
		// $str contains "qlow-200px" or "200px" strings because thumb.php would strip the filename
		// first - check if the string begins with "qlow-", and if so, treat it as quality.
		// Pass the first portion, or the whole string if "qlow-" not found, to the parent
		// The parsing must match the makeParamString() above
		$res = false;
		$m = false;
		if ( preg_match( '/q([^-]+)-(.*)$/', $str, $m ) ) {
			$v = $m[1];
			if ( self::validateQuality( $v ) ) {
				$res = parent::parseParamString( $m[2] );
				if ( $res ) {
					$res['quality'] = $v;
				}
			}
		} else {
			$res = parent::parseParamString( $str );
		}
		return $res;
	}

	function getScriptParams( $params ) {
		$res = parent::getScriptParams( $params );
		if ( isset( $params['quality'] ) ) {
			$res['quality'] = $params['quality'];
		}
		return $res;
	}

	function getMetadata( $image, $filename ) {
		try {
			$meta = BitmapMetadataHandler::Jpeg( $filename );
			if ( !is_array( $meta ) ) {
				// This should never happen, but doesn't hurt to be paranoid.
				throw new MWException( 'Metadata array is not an array' );
			}
			$meta['MEDIAWIKI_EXIF_VERSION'] = Exif::version();

			return serialize( $meta );
		} catch ( MWException $e ) {
			// BitmapMetadataHandler throws an exception in certain exceptional
			// cases like if file does not exist.
			wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" );

			/* This used to use 0 (ExifBitmapHandler::OLD_BROKEN_FILE) for the cases
			 * 	* No metadata in the file
			 * 	* Something is broken in the file.
			 * However, if the metadata support gets expanded then you can't tell if the 0 is from
			 * a broken file, or just no props found. A broken file is likely to stay broken, but
			 * a file which had no props could have props once the metadata support is improved.
			 * Thus switch to using -1 to denote only a broken file, and use an array with only
			 * MEDIAWIKI_EXIF_VERSION to denote no props.
			 */

			return ExifBitmapHandler::BROKEN_FILE;
		}
	}

	/**
	 * @param File $file
	 * @param array $params Rotate parameters.
	 *    'rotation' clockwise rotation in degrees, allowed are multiples of 90
	 * @since 1.21
	 * @return bool
	 */
	public function rotate( $file, $params ) {
		global $wgJpegTran;

		$rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;

		if ( $wgJpegTran && is_file( $wgJpegTran ) ) {
			$cmd = wfEscapeShellArg( $wgJpegTran ) .
				" -rotate " . wfEscapeShellArg( $rotation ) .
				" -outfile " . wfEscapeShellArg( $params['dstPath'] ) .
				" " . wfEscapeShellArg( $params['srcPath'] );
			wfDebug( __METHOD__ . ": running jpgtran: $cmd\n" );
			wfProfileIn( 'jpegtran' );
			$retval = 0;
			$err = wfShellExecWithStderr( $cmd, $retval );
			wfProfileOut( 'jpegtran' );
			if ( $retval !== 0 ) {
				$this->logErrorForExternalProcess( $retval, $err, $cmd );

				return new MediaTransformError( 'thumbnail_error', 0, 0, $err );
			}

			return false;
		} else {
			return parent::rotate( $file, $params );
		}
	}

	public function supportsBucketing() {
		return true;
	}

	public function sanitizeParamsForBucketing( $params ) {
		$params = parent::sanitizeParamsForBucketing( $params );

		// Quality needs to be cleared for bucketing. Buckets need to be default quality
		if ( isset( $params['quality'] ) ) {
			unset( $params['quality'] );
		}

		return $params;
	}
}