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
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)
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.
223 'ExposureMode' =>
self::SHORT, # Exposure mode #p48
224 'WhiteBalance' =>
self::SHORT, # White Balance #p49
226 'FocalLengthIn35mmFilm' =>
self::SHORT, # Focal length in 35 mm film
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
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';
293 $this->
debugFile( $this->basename, __FUNCTION__,
true );
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 ?: [];
368 if ( isset( $this->mFilteredExifData[
'GPSAltitude'] )
369 && isset( $this->mFilteredExifData[
'GPSAltitudeRef'] )
374 list( $num, $denom ) = explode(
'/', $this->mFilteredExifData[
'GPSAltitude'] );
375 $this->mFilteredExifData[
'GPSAltitude'] = $num / $denom;
377 if ( $this->mFilteredExifData[
'GPSAltitudeRef'] ===
"\1" ) {
378 $this->mFilteredExifData[
'GPSAltitude'] *= -1;
380 unset( $this->mFilteredExifData[
'GPSAltitudeRef'] );
393 if ( isset( $this->mFilteredExifData[
'ComponentsConfiguration'] ) ) {
394 $val = $this->mFilteredExifData[
'ComponentsConfiguration'];
397 $strLen = strlen( $val );
398 for ( $i = 0; $i < $strLen; $i++ ) {
399 $ccVals[$i] = ord( substr( $val, $i, 1 ) );
401 $ccVals[
'_type'] =
'ol';
402 $this->mFilteredExifData[
'ComponentsConfiguration'] = $ccVals;
412 if ( isset( $this->mFilteredExifData[
'GPSVersion'] ) ) {
413 $val = $this->mFilteredExifData[
'GPSVersion'];
416 $strLen = strlen( $val );
417 for ( $i = 0; $i < $strLen; $i++ ) {
421 $newVal .= ord( substr( $val, $i, 1 ) );
424 if ( $this->byteOrder ===
'LE' ) {
427 for ( $i = strlen( $newVal ) - 1; $i >= 0; $i-- ) {
428 $newVal2 .= substr( $newVal, $i, 1 );
430 $this->mFilteredExifData[
'GPSVersionID'] = $newVal2;
432 $this->mFilteredExifData[
'GPSVersionID'] = $newVal;
434 unset( $this->mFilteredExifData[
'GPSVersion'] );
445 if ( isset( $this->mFilteredExifData[$prop] ) ) {
446 if ( strlen( $this->mFilteredExifData[$prop] ) <= 8 ) {
449 $this->
debug( $this->mFilteredExifData[$prop], __FUNCTION__,
false );
450 unset( $this->mFilteredExifData[$prop] );
454 $charCode = substr( $this->mFilteredExifData[$prop], 0, 8 );
455 $val = substr( $this->mFilteredExifData[$prop], 8 );
457 switch ( $charCode ) {
458 case "\x4A\x49\x53\x00\x00\x00\x00\x00":
460 $charset =
"Shift-JIS";
470 Wikimedia\suppressWarnings();
471 $val = iconv( $charset,
'UTF-8//IGNORE', $val );
472 Wikimedia\restoreWarnings();
476 UtfNormal\Validator::quickIsNFCVerify( $valCopy );
477 if ( $valCopy !== $val ) {
478 Wikimedia\suppressWarnings();
479 $val = iconv(
'Windows-1252',
'UTF-8//IGNORE', $val );
480 Wikimedia\restoreWarnings();
486 if ( strlen( $val ) === 0 ) {
488 $this->
debug( $this->mFilteredExifData[$prop], __FUNCTION__,
"$prop: Is only whitespace" );
489 unset( $this->mFilteredExifData[$prop] );
495 $this->mFilteredExifData[$prop] = $val;