118 # TIFF Rev. 6.0 Attribute Information (p22)
120 # Tags relating to image structure
123 'BitsPerSample' => [
self::SHORT, 3 ], # Number of bits per component
124 # "When a primary image is JPEG compressed, this designation is not"
125 # "necessary and is omitted." (p23)
126 'Compression' =>
self::SHORT, # Compression scheme #p23
127 'PhotometricInterpretation' =>
self::SHORT, # Pixel composition #p23
128 'Orientation' =>
self::SHORT, # Orientation of image #p24
129 'SamplesPerPixel' =>
self::SHORT, # Number of components
130 'PlanarConfiguration' =>
self::SHORT, # Image data arrangement #p24
131 'YCbCrSubSampling' => [
self::SHORT, 2 ], # Subsampling ratio of Y to C #p24
132 'YCbCrPositioning' =>
self::SHORT, # Y and C positioning #p24-25
133 'XResolution' =>
self::RATIONAL, # Image resolution in width direction
134 'YResolution' =>
self::RATIONAL, # Image resolution in height direction
135 'ResolutionUnit' =>
self::SHORT, # Unit of X and Y resolution #(p26)
137 # Tags relating to recording offset
138 'StripOffsets' => self::SHORT_OR_LONG, # Image data location
139 'RowsPerStrip' => self::SHORT_OR_LONG, # Number of rows per strip
140 'StripByteCounts' => self::SHORT_OR_LONG, # Bytes per compressed strip
141 'JPEGInterchangeFormat' => self::SHORT_OR_LONG, # Offset to JPEG SOI
142 'JPEGInterchangeFormatLength' => self::SHORT_OR_LONG, # Bytes of JPEG data
144 # Tags relating to image data characteristics
145 'TransferFunction' => self::IGNORE, # Transfer
function
146 'WhitePoint' => [ self::RATIONAL, 2 ], # White point chromaticity
147 'PrimaryChromaticities' => [ self::RATIONAL, 6 ], # Chromaticities of primarities
148 # Color space transformation matrix coefficients #p27
149 'YCbCrCoefficients' => [ self::RATIONAL, 3 ],
150 'ReferenceBlackWhite' => [ self::RATIONAL, 6 ], # Pair of black and white reference values
153 'DateTime' => self::ASCII, #
File change date and time
154 'ImageDescription' => self::ASCII, # Image title
155 'Make' => self::ASCII, # Image input equipment manufacturer
156 'Model' => self::ASCII, # Image input equipment model
157 'Software' => self::ASCII, # Software used
158 'Artist' => self::ASCII, # Person who created the image
159 'Copyright' => self::ASCII, # Copyright holder
162 #
Exif IFD Attribute Information (p30-31)
164 # @todo NOTE: Nonexistence of this field is taken to mean nonconformance
165 # to the Exif 2.1 AND 2.2 standards
169 # Tags relating to Image Data Characteristics
170 'ColorSpace' =>
self::SHORT, # Color space information #p32
172 # Tags relating to image configuration
173 'ComponentsConfiguration' =>
self::UNDEFINED, # Meaning of each component #p33
174 'CompressedBitsPerPixel' =>
self::RATIONAL, # Image compression mode
178 # Tags relating to related user information
182 # Tags relating to related file information
183 'RelatedSoundFile' =>
self::ASCII, # Related audio file
185 # Tags relating to date and time
186 'DateTimeOriginal' =>
self::ASCII, # Date and time of original data generation #p36
187 'DateTimeDigitized' =>
self::ASCII, # Date and time of original data generation
189 'SubSecTimeOriginal' =>
self::ASCII, # DateTimeOriginal subseconds
190 'SubSecTimeDigitized' =>
self::ASCII, # DateTimeDigitized subseconds
192 # Tags relating to picture-taking conditions (p31)
195 'ExposureProgram' =>
self::SHORT, # Exposure Program #p38
196 'SpectralSensitivity' =>
self::ASCII, # Spectral sensitivity
197 'ISOSpeedRatings' =>
self::SHORT, # ISO speed rating
199 # Optoelectronic conversion factor. Note: We don't have support for this atm.
206 'MeteringMode' =>
self::SHORT, # Metering mode #p40
207 'LightSource' =>
self::SHORT, # Light source #p40-41
210 'SubjectArea' => [
self::SHORT, 4 ], # Subject area
212 'SpatialFrequencyResponse' =>
self::IGNORE, # Spatial frequency response. Not supported atm.
213 'FocalPlaneXResolution' =>
self::RATIONAL, # Focal plane X resolution
214 'FocalPlaneYResolution' =>
self::RATIONAL, # Focal plane Y resolution
215 'FocalPlaneResolutionUnit' =>
self::SHORT, # Focal plane resolution unit #p46
216 'SubjectLocation' => [
self::SHORT, 2 ], # Subject location
218 'SensingMethod' =>
self::SHORT, # Sensing method #p46
221 'CFAPattern' =>
self::IGNORE, # CFA pattern. not supported atm.
222 'CustomRendered' =>
self::SHORT, # Custom image processing #p48
223 'ExposureMode' =>
self::SHORT, # Exposure mode #p48
224 'WhiteBalance' =>
self::SHORT, # White Balance #p49
226 'FocalLengthIn35mmFilm' =>
self::SHORT, # Focal length in 35 mm film
227 'SceneCaptureType' =>
self::SHORT, # Scene capture type #p49
228 'GainControl' =>
self::SHORT, # Scene control #p49-50
233 # Device settings description. This could maybe be supported. Need to find an
234 # example file that uses this to see if it has stuff of interest in it.
235 'SubjectDistanceRange' =>
self::SHORT, # Subject distance range #p51
240 # GPS Attribute Information (p52)
243 # Should be an array of 4 Exif::BYTE's. However php treats it as an undefined
244 # Note exif standard calls this GPSVersionID, but php doesn't like the id suffix
245 'GPSLatitudeRef' =>
self::ASCII, # North or South Latitude #p52-53
247 'GPSLongitudeRef' =>
self::ASCII, # East or West Longitude #p53
250 # Altitude reference. Note, the exif standard says this should be an EXIF::Byte,
251 # but php seems to disagree.
253 'GPSTimeStamp' => [
self::RATIONAL, 3 ], # GPS time (atomic clock)
254 'GPSSatellites' =>
self::ASCII, # Satellites used
for measurement
256 'GPSMeasureMode' =>
self::ASCII, # Measurement mode #p54-55
260 'GPSTrackRef' =>
self::ASCII, # Reference
for direction of movement #p55
262 'GPSImgDirectionRef' =>
self::ASCII, # Reference
for direction of image #p56
264 'GPSMapDatum' =>
self::ASCII, # Geodetic survey data used
265 'GPSDestLatitudeRef' =>
self::ASCII, # Reference
for latitude of destination #p56
266 'GPSDestLatitude' => [
self::RATIONAL, 3 ], # Latitude destination
267 'GPSDestLongitudeRef' =>
self::ASCII, # Reference
for longitude of destination #p57
268 'GPSDestLongitude' => [
self::RATIONAL, 3 ], # Longitude of destination
269 'GPSDestBearingRef' =>
self::ASCII, # Reference
for bearing of destination #p57
271 'GPSDestDistanceRef' =>
self::ASCII, # Reference
for distance to destination #p57-58
273 'GPSProcessingMethod' =>
self::UNDEFINED, # Name of GPS processing method
276 'GPSDifferential' =>
self::SHORT, # GPS differential correction
288 wfWarn(
'Exif class did not have byte order specified. ' .
289 'Some properties may be decoded incorrectly.' );
290 $this->byteOrder =
'BE';
294 if ( function_exists(
'exif_read_data' ) ) {
295 Wikimedia\suppressWarnings();
296 $data = exif_read_data( $this->file, 0,
true );
297 Wikimedia\restoreWarnings();
299 throw new MWException(
"Internal error: exif_read_data not present. " .
300 "\$wgShowEXIF may be incorrectly set or not checked by an extension." );
307 $this->mRawExifData = $data ?: [];
366 if ( isset( $this->mFilteredExifData[
'GPSAltitude'] ) ) {
370 list( $num, $denom ) = explode(
'/', $this->mFilteredExifData[
'GPSAltitude'] );
371 $this->mFilteredExifData[
'GPSAltitude'] = $num / $denom;
373 if ( isset( $this->mFilteredExifData[
'GPSAltitudeRef'] ) ) {
374 switch ( $this->mFilteredExifData[
'GPSAltitudeRef'] ) {
380 $this->mFilteredExifData[
'GPSAltitude'] *= -1;
384 unset( $this->mFilteredExifData[
'GPSAltitude'] );
389 unset( $this->mFilteredExifData[
'GPSAltitudeRef'] );
401 if ( isset( $this->mFilteredExifData[
'ComponentsConfiguration'] ) ) {
402 $val = $this->mFilteredExifData[
'ComponentsConfiguration'];
405 $strLen = strlen( $val );
406 for ( $i = 0; $i < $strLen; $i++ ) {
407 $ccVals[$i] = ord( substr( $val, $i, 1 ) );
409 $ccVals[
'_type'] =
'ol';
410 $this->mFilteredExifData[
'ComponentsConfiguration'] = $ccVals;
420 if ( isset( $this->mFilteredExifData[
'GPSVersion'] ) ) {
421 $val = $this->mFilteredExifData[
'GPSVersion'];
424 $strLen = strlen( $val );
425 for ( $i = 0; $i < $strLen; $i++ ) {
429 $newVal .= ord( substr( $val, $i, 1 ) );
432 if ( $this->byteOrder ===
'LE' ) {
435 for ( $i = strlen( $newVal ) - 1; $i >= 0; $i-- ) {
436 $newVal2 .= substr( $newVal, $i, 1 );
438 $this->mFilteredExifData[
'GPSVersionID'] = $newVal2;
440 $this->mFilteredExifData[
'GPSVersionID'] = $newVal;
442 unset( $this->mFilteredExifData[
'GPSVersion'] );
453 if ( isset( $this->mFilteredExifData[$prop] ) ) {
454 if ( strlen( $this->mFilteredExifData[$prop] ) <= 8 ) {
457 $this->
debug( $this->mFilteredExifData[$prop], __FUNCTION__,
false );
458 unset( $this->mFilteredExifData[$prop] );
462 $charCode = substr( $this->mFilteredExifData[$prop], 0, 8 );
463 $val = substr( $this->mFilteredExifData[$prop], 8 );
465 switch ( $charCode ) {
466 case "JIS\x00\x00\x00\x00\x00":
467 $charset =
"Shift-JIS";
477 Wikimedia\suppressWarnings();
478 $val = iconv( $charset,
'UTF-8//IGNORE', $val );
479 Wikimedia\restoreWarnings();
483 UtfNormal\Validator::quickIsNFCVerify( $valCopy );
484 if ( $valCopy !== $val ) {
485 Wikimedia\suppressWarnings();
486 $val = iconv(
'Windows-1252',
'UTF-8//IGNORE', $val );
487 Wikimedia\restoreWarnings();
493 if ( strlen( $val ) === 0 ) {
495 $this->
debug( $this->mFilteredExifData[$prop], __FUNCTION__,
"$prop: Is only whitespace" );
496 unset( $this->mFilteredExifData[$prop] );
502 $this->mFilteredExifData[$prop] = $val;