r49677 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r49676‎ | r49677 | r49678 >
Date:22:29, 20 April 2009
Author:vyznev
Status:reverted (Comments)
Tags:
Comment:
Apply patch for bug 13172 by Brent G <overlordq@gmail.com> with minor modifications.
Also add pretty-printing of GPS coordinate values and correct display of altitude / destdistance reference values.
(We really should redesign the FormatExif class so that the "GPS*Ref" values can be combined with their parent values, but this will do for now.)
Modified paths:
  • /trunk/phase3/includes/Exif.php (modified) (history)
  • /trunk/phase3/includes/media/Bitmap.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/Exif.php
@@ -17,7 +17,7 @@
1818 *
1919 * @ingroup Media
2020 * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
21 - * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
 21+ * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason, 2009 Brent Garber
2222 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
2323 * @see http://exif.org/Exif2-2.PDF The Exif 2.2 specification
2424 * @file
@@ -51,11 +51,6 @@
5252 var $mExifTags;
5353
5454 /**
55 - * A one dimentional array of all Exif tags
56 - */
57 - var $mFlatExifTags;
58 -
59 - /**
6055 * The raw Exif data returned by exif_read_data()
6156 */
6257 var $mRawExifData;
@@ -109,192 +104,165 @@
110105 */
111106 $this->mExifTags = array(
112107 # TIFF Rev. 6.0 Attribute Information (p22)
113 - 'tiff' => array(
 108+ 'IFD0' => array(
114109 # Tags relating to image structure
115 - 'structure' => array(
116 - 'ImageWidth' => Exif::SHORT.','.Exif::LONG, # Image width
117 - 'ImageLength' => Exif::SHORT.','.Exif::LONG, # Image height
118 - 'BitsPerSample' => Exif::SHORT, # Number of bits per component
119 - # "When a primary image is JPEG compressed, this designation is not"
120 - # "necessary and is omitted." (p23)
121 - 'Compression' => Exif::SHORT, # Compression scheme #p23
122 - 'PhotometricInterpretation' => Exif::SHORT, # Pixel composition #p23
123 - 'Orientation' => Exif::SHORT, # Orientation of image #p24
124 - 'SamplesPerPixel' => Exif::SHORT, # Number of components
125 - 'PlanarConfiguration' => Exif::SHORT, # Image data arrangement #p24
126 - 'YCbCrSubSampling' => Exif::SHORT, # Subsampling ratio of Y to C #p24
127 - 'YCbCrPositioning' => Exif::SHORT, # Y and C positioning #p24-25
128 - 'XResolution' => Exif::RATIONAL, # Image resolution in width direction
129 - 'YResolution' => Exif::RATIONAL, # Image resolution in height direction
130 - 'ResolutionUnit' => Exif::SHORT, # Unit of X and Y resolution #(p26)
131 - ),
 110+ 'ImageWidth' => Exif::SHORT.','.Exif::LONG, # Image width
 111+ 'ImageLength' => Exif::SHORT.','.Exif::LONG, # Image height
 112+ 'BitsPerSample' => array( Exif::SHORT, 3 ), # Number of bits per component
 113+ # "When a primary image is JPEG compressed, this designation is not"
 114+ # "necessary and is omitted." (p23)
 115+ 'Compression' => Exif::SHORT, # Compression scheme #p23
 116+ 'PhotometricInterpretation' => Exif::SHORT, # Pixel composition #p23
 117+ 'Orientation' => Exif::SHORT, # Orientation of image #p24
 118+ 'SamplesPerPixel' => Exif::SHORT, # Number of components
 119+ 'PlanarConfiguration' => Exif::SHORT, # Image data arrangement #p24
 120+ 'YCbCrSubSampling' => array( Exif::SHORT, 2), # Subsampling ratio of Y to C #p24
 121+ 'YCbCrPositioning' => Exif::SHORT, # Y and C positioning #p24-25
 122+ 'XResolution' => Exif::RATIONAL, # Image resolution in width direction
 123+ 'YResolution' => Exif::RATIONAL, # Image resolution in height direction
 124+ 'ResolutionUnit' => Exif::SHORT, # Unit of X and Y resolution #(p26)
132125
133126 # Tags relating to recording offset
134 - 'offset' => array(
135 - 'StripOffsets' => Exif::SHORT.','.Exif::LONG, # Image data location
136 - 'RowsPerStrip' => Exif::SHORT.','.Exif::LONG, # Number of rows per strip
137 - 'StripByteCounts' => Exif::SHORT.','.Exif::LONG, # Bytes per compressed strip
138 - 'JPEGInterchangeFormat' => Exif::SHORT.','.Exif::LONG, # Offset to JPEG SOI
139 - 'JPEGInterchangeFormatLength' => Exif::SHORT.','.Exif::LONG, # Bytes of JPEG data
140 - ),
 127+ 'StripOffsets' => Exif::SHORT.','.Exif::LONG, # Image data location
 128+ 'RowsPerStrip' => Exif::SHORT.','.Exif::LONG, # Number of rows per strip
 129+ 'StripByteCounts' => Exif::SHORT.','.Exif::LONG, # Bytes per compressed strip
 130+ 'JPEGInterchangeFormat' => Exif::SHORT.','.Exif::LONG, # Offset to JPEG SOI
 131+ 'JPEGInterchangeFormatLength' => Exif::SHORT.','.Exif::LONG, # Bytes of JPEG data
141132
142133 # Tags relating to image data characteristics
143 - 'characteristics' => array(
144 - 'TransferFunction' => Exif::SHORT, # Transfer function
145 - 'WhitePoint' => Exif::RATIONAL, # White point chromaticity
146 - 'PrimaryChromaticities' => Exif::RATIONAL, # Chromaticities of primarities
147 - 'YCbCrCoefficients' => Exif::RATIONAL, # Color space transformation matrix coefficients #p27
148 - 'ReferenceBlackWhite' => Exif::RATIONAL # Pair of black and white reference values
149 - ),
 134+ 'TransferFunction' => Exif::SHORT, # Transfer function
 135+ 'WhitePoint' => array( Exif::RATIONAL, 2), # White point chromaticity
 136+ 'PrimaryChromaticities' => array( Exif::RATIONAL, 6), # Chromaticities of primarities
 137+ 'YCbCrCoefficients' => array( Exif::RATIONAL, 3), # Color space transformation matrix coefficients #p27
 138+ 'ReferenceBlackWhite' => array( Exif::RATIONAL, 6), # Pair of black and white reference values
150139
151140 # Other tags
152 - 'other' => array(
153 - 'DateTime' => Exif::ASCII, # File change date and time
154 - 'ImageDescription' => Exif::ASCII, # Image title
155 - 'Make' => Exif::ASCII, # Image input equipment manufacturer
156 - 'Model' => Exif::ASCII, # Image input equipment model
157 - 'Software' => Exif::ASCII, # Software used
158 - 'Artist' => Exif::ASCII, # Person who created the image
159 - 'Copyright' => Exif::ASCII, # Copyright holder
160 - ),
 141+ 'DateTime' => Exif::ASCII, # File change date and time
 142+ 'ImageDescription' => Exif::ASCII, # Image title
 143+ 'Make' => Exif::ASCII, # Image input equipment manufacturer
 144+ 'Model' => Exif::ASCII, # Image input equipment model
 145+ 'Software' => Exif::ASCII, # Software used
 146+ 'Artist' => Exif::ASCII, # Person who created the image
 147+ 'Copyright' => Exif::ASCII, # Copyright holder
161148 ),
162149
163150 # Exif IFD Attribute Information (p30-31)
164 - 'exif' => array(
165 - # Tags relating to version
166 - 'version' => array(
167 - # TODO: NOTE: Nonexistence of this field is taken to mean nonconformance
168 - # to the EXIF 2.1 AND 2.2 standards
169 - 'ExifVersion' => Exif::UNDEFINED, # Exif version
170 - 'FlashpixVersion' => Exif::UNDEFINED, # Supported Flashpix version #p32
171 - ),
 151+ 'EXIF' => array(
 152+ # TODO: NOTE: Nonexistence of this field is taken to mean nonconformance
 153+ # to the EXIF 2.1 AND 2.2 standards
 154+ 'ExifVersion' => array( Exif::UNDEFINED, 4 ), # Exif version
 155+ 'FlashPixVersion' => array( Exif::UNDEFINED, 4 ), # Supported Flashpix version #p32
172156
173157 # Tags relating to Image Data Characteristics
174 - 'characteristics' => array(
175 - 'ColorSpace' => Exif::SHORT, # Color space information #p32
176 - ),
 158+ 'ColorSpace' => Exif::SHORT, # Color space information #p32
177159
178160 # Tags relating to image configuration
179 - 'configuration' => array(
180 - 'ComponentsConfiguration' => Exif::UNDEFINED, # Meaning of each component #p33
181 - 'CompressedBitsPerPixel' => Exif::RATIONAL, # Image compression mode
182 - 'PixelYDimension' => Exif::SHORT.','.Exif::LONG, # Valid image width
183 - 'PixelXDimension' => Exif::SHORT.','.Exif::LONG, # Valind image height
184 - ),
 161+ 'ComponentsConfiguration' => array( Exif::UNDEFINED, 1), # Meaning of each component #p33
 162+ 'CompressedBitsPerPixel' => Exif::RATIONAL, # Image compression mode
 163+ 'PixelYDimension' => Exif::SHORT.','.Exif::LONG, # Valid image width
 164+ 'PixelXDimension' => Exif::SHORT.','.Exif::LONG, # Valid image height
185165
186166 # Tags relating to related user information
187 - 'user' => array(
188 - 'MakerNote' => Exif::UNDEFINED, # Manufacturer notes
189 - 'UserComment' => Exif::UNDEFINED, # User comments #p34
190 - ),
 167+ 'MakerNote' => Exif::UNDEFINED, # Manufacturer notes
 168+ 'UserComment' => Exif::UNDEFINED, # User comments #p34
191169
192170 # Tags relating to related file information
193 - 'related' => array(
194 - 'RelatedSoundFile' => Exif::ASCII, # Related audio file
195 - ),
 171+ 'RelatedSoundFile' => Exif::ASCII, # Related audio file
196172
197173 # Tags relating to date and time
198 - 'dateandtime' => array(
199 - 'DateTimeOriginal' => Exif::ASCII, # Date and time of original data generation #p36
200 - 'DateTimeDigitized' => Exif::ASCII, # Date and time of original data generation
201 - 'SubSecTime' => Exif::ASCII, # DateTime subseconds
202 - 'SubSecTimeOriginal' => Exif::ASCII, # DateTimeOriginal subseconds
203 - 'SubSecTimeDigitized' => Exif::ASCII, # DateTimeDigitized subseconds
204 - ),
 174+ 'DateTimeOriginal' => Exif::ASCII, # Date and time of original data generation #p36
 175+ 'DateTimeDigitized' => Exif::ASCII, # Date and time of original data generation
 176+ 'SubSecTime' => Exif::ASCII, # DateTime subseconds
 177+ 'SubSecTimeOriginal' => Exif::ASCII, # DateTimeOriginal subseconds
 178+ 'SubSecTimeDigitized' => Exif::ASCII, # DateTimeDigitized subseconds
205179
206180 # Tags relating to picture-taking conditions (p31)
207 - 'conditions' => array(
208 - 'ExposureTime' => Exif::RATIONAL, # Exposure time
209 - 'FNumber' => Exif::RATIONAL, # F Number
210 - 'ExposureProgram' => Exif::SHORT, # Exposure Program #p38
211 - 'SpectralSensitivity' => Exif::ASCII, # Spectral sensitivity
212 - 'ISOSpeedRatings' => Exif::SHORT, # ISO speed rating
213 - 'OECF' => Exif::UNDEFINED, # Optoelectronic conversion factor
214 - 'ShutterSpeedValue' => Exif::SRATIONAL, # Shutter speed
215 - 'ApertureValue' => Exif::RATIONAL, # Aperture
216 - 'BrightnessValue' => Exif::SRATIONAL, # Brightness
217 - 'ExposureBiasValue' => Exif::SRATIONAL, # Exposure bias
218 - 'MaxApertureValue' => Exif::RATIONAL, # Maximum land aperture
219 - 'SubjectDistance' => Exif::RATIONAL, # Subject distance
220 - 'MeteringMode' => Exif::SHORT, # Metering mode #p40
221 - 'LightSource' => Exif::SHORT, # Light source #p40-41
222 - 'Flash' => Exif::SHORT, # Flash #p41-42
223 - 'FocalLength' => Exif::RATIONAL, # Lens focal length
224 - 'SubjectArea' => Exif::SHORT, # Subject area
225 - 'FlashEnergy' => Exif::RATIONAL, # Flash energy
226 - 'SpatialFrequencyResponse' => Exif::UNDEFINED, # Spatial frequency response
227 - 'FocalPlaneXResolution' => Exif::RATIONAL, # Focal plane X resolution
228 - 'FocalPlaneYResolution' => Exif::RATIONAL, # Focal plane Y resolution
229 - 'FocalPlaneResolutionUnit' => Exif::SHORT, # Focal plane resolution unit #p46
230 - 'SubjectLocation' => Exif::SHORT, # Subject location
231 - 'ExposureIndex' => Exif::RATIONAL, # Exposure index
232 - 'SensingMethod' => Exif::SHORT, # Sensing method #p46
233 - 'FileSource' => Exif::UNDEFINED, # File source #p47
234 - 'SceneType' => Exif::UNDEFINED, # Scene type #p47
235 - 'CFAPattern' => Exif::UNDEFINED, # CFA pattern
236 - 'CustomRendered' => Exif::SHORT, # Custom image processing #p48
237 - 'ExposureMode' => Exif::SHORT, # Exposure mode #p48
238 - 'WhiteBalance' => Exif::SHORT, # White Balance #p49
239 - 'DigitalZoomRatio' => Exif::RATIONAL, # Digital zoom ration
240 - 'FocalLengthIn35mmFilm' => Exif::SHORT, # Focal length in 35 mm film
241 - 'SceneCaptureType' => Exif::SHORT, # Scene capture type #p49
242 - 'GainControl' => Exif::RATIONAL, # Scene control #p49-50
243 - 'Contrast' => Exif::SHORT, # Contrast #p50
244 - 'Saturation' => Exif::SHORT, # Saturation #p50
245 - 'Sharpness' => Exif::SHORT, # Sharpness #p50
246 - 'DeviceSettingDescription' => Exif::UNDEFINED, # Desice settings description
247 - 'SubjectDistanceRange' => Exif::SHORT, # Subject distance range #p51
248 - ),
 181+ 'ExposureTime' => Exif::RATIONAL, # Exposure time
 182+ 'FNumber' => Exif::RATIONAL, # F Number
 183+ 'ExposureProgram' => Exif::SHORT, # Exposure Program #p38
 184+ 'SpectralSensitivity' => Exif::ASCII, # Spectral sensitivity
 185+ 'ISOSpeedRatings' => Exif::SHORT, # ISO speed rating
 186+ 'OECF' => Exif::UNDEFINED, # Optoelectronic conversion factor
 187+ 'ShutterSpeedValue' => Exif::SRATIONAL, # Shutter speed
 188+ 'ApertureValue' => Exif::RATIONAL, # Aperture
 189+ 'BrightnessValue' => Exif::SRATIONAL, # Brightness
 190+ 'ExposureBiasValue' => Exif::SRATIONAL, # Exposure bias
 191+ 'MaxApertureValue' => Exif::RATIONAL, # Maximum land aperture
 192+ 'SubjectDistance' => Exif::RATIONAL, # Subject distance
 193+ 'MeteringMode' => Exif::SHORT, # Metering mode #p40
 194+ 'LightSource' => Exif::SHORT, # Light source #p40-41
 195+ 'Flash' => Exif::SHORT, # Flash #p41-42
 196+ 'FocalLength' => Exif::RATIONAL, # Lens focal length
 197+ 'SubjectArea' => array( Exif::SHORT, 4 ), # Subject area
 198+ 'FlashEnergy' => Exif::RATIONAL, # Flash energy
 199+ 'SpatialFrequencyResponse' => Exif::UNDEFINED, # Spatial frequency response
 200+ 'FocalPlaneXResolution' => Exif::RATIONAL, # Focal plane X resolution
 201+ 'FocalPlaneYResolution' => Exif::RATIONAL, # Focal plane Y resolution
 202+ 'FocalPlaneResolutionUnit' => Exif::SHORT, # Focal plane resolution unit #p46
 203+ 'SubjectLocation' => array( Exif::SHORT, 2), # Subject location
 204+ 'ExposureIndex' => Exif::RATIONAL, # Exposure index
 205+ 'SensingMethod' => Exif::SHORT, # Sensing method #p46
 206+ 'FileSource' => Exif::UNDEFINED, # File source #p47
 207+ 'SceneType' => Exif::UNDEFINED, # Scene type #p47
 208+ 'CFAPattern' => Exif::UNDEFINED, # CFA pattern
 209+ 'CustomRendered' => Exif::SHORT, # Custom image processing #p48
 210+ 'ExposureMode' => Exif::SHORT, # Exposure mode #p48
 211+ 'WhiteBalance' => Exif::SHORT, # White Balance #p49
 212+ 'DigitalZoomRatio' => Exif::RATIONAL, # Digital zoom ration
 213+ 'FocalLengthIn35mmFilm' => Exif::SHORT, # Focal length in 35 mm film
 214+ 'SceneCaptureType' => Exif::SHORT, # Scene capture type #p49
 215+ 'GainControl' => Exif::RATIONAL, # Scene control #p49-50
 216+ 'Contrast' => Exif::SHORT, # Contrast #p50
 217+ 'Saturation' => Exif::SHORT, # Saturation #p50
 218+ 'Sharpness' => Exif::SHORT, # Sharpness #p50
 219+ 'DeviceSettingDescription' => Exif::UNDEFINED, # Desice settings description
 220+ 'SubjectDistanceRange' => Exif::SHORT, # Subject distance range #p51
249221
250 - 'other' => array(
251 - 'ImageUniqueID' => Exif::ASCII, # Unique image ID
252 - ),
 222+ 'ImageUniqueID' => Exif::ASCII, # Unique image ID
253223 ),
254224
255225 # GPS Attribute Information (p52)
256 - 'gps' => array(
257 - 'GPSVersionID' => Exif::BYTE, # GPS tag version
258 - 'GPSLatitudeRef' => Exif::ASCII, # North or South Latitude #p52-53
259 - 'GPSLatitude' => Exif::RATIONAL, # Latitude
260 - 'GPSLongitudeRef' => Exif::ASCII, # East or West Longitude #p53
261 - 'GPSLongitude' => Exif::RATIONAL, # Longitude
262 - 'GPSAltitudeRef' => Exif::BYTE, # Altitude reference
263 - 'GPSAltitude' => Exif::RATIONAL, # Altitude
264 - 'GPSTimeStamp' => Exif::RATIONAL, # GPS time (atomic clock)
265 - 'GPSSatellites' => Exif::ASCII, # Satellites used for measurement
266 - 'GPSStatus' => Exif::ASCII, # Receiver status #p54
267 - 'GPSMeasureMode' => Exif::ASCII, # Measurement mode #p54-55
268 - 'GPSDOP' => Exif::RATIONAL, # Measurement precision
269 - 'GPSSpeedRef' => Exif::ASCII, # Speed unit #p55
270 - 'GPSSpeed' => Exif::RATIONAL, # Speed of GPS receiver
271 - 'GPSTrackRef' => Exif::ASCII, # Reference for direction of movement #p55
272 - 'GPSTrack' => Exif::RATIONAL, # Direction of movement
273 - 'GPSImgDirectionRef' => Exif::ASCII, # Reference for direction of image #p56
274 - 'GPSImgDirection' => Exif::RATIONAL, # Direction of image
275 - 'GPSMapDatum' => Exif::ASCII, # Geodetic survey data used
276 - 'GPSDestLatitudeRef' => Exif::ASCII, # Reference for latitude of destination #p56
277 - 'GPSDestLatitude' => Exif::RATIONAL, # Latitude destination
278 - 'GPSDestLongitudeRef' => Exif::ASCII, # Reference for longitude of destination #p57
279 - 'GPSDestLongitude' => Exif::RATIONAL, # Longitude of destination
280 - 'GPSDestBearingRef' => Exif::ASCII, # Reference for bearing of destination #p57
281 - 'GPSDestBearing' => Exif::RATIONAL, # Bearing of destination
282 - 'GPSDestDistanceRef' => Exif::ASCII, # Reference for distance to destination #p57-58
283 - 'GPSDestDistance' => Exif::RATIONAL, # Distance to destination
284 - 'GPSProcessingMethod' => Exif::UNDEFINED, # Name of GPS processing method
285 - 'GPSAreaInformation' => Exif::UNDEFINED, # Name of GPS area
286 - 'GPSDateStamp' => Exif::ASCII, # GPS date
287 - 'GPSDifferential' => Exif::SHORT, # GPS differential correction
 226+ 'GPS' => array(
 227+ 'GPSVersionID' => array( Exif::BYTE, 4 ), # GPS tag version
 228+ 'GPSLatitudeRef' => Exif::ASCII, # North or South Latitude #p52-53
 229+ 'GPSLatitude' => array( Exif::RATIONAL, 3 ), # Latitude
 230+ 'GPSLongitudeRef' => Exif::ASCII, # East or West Longitude #p53
 231+ 'GPSLongitude' => array( Exif::RATIONAL, 3), # Longitude
 232+ 'GPSAltitudeRef' => Exif::BYTE, # Altitude reference
 233+ 'GPSAltitude' => Exif::RATIONAL, # Altitude
 234+ 'GPSTimeStamp' => array( Exif::RATIONAL, 3), # GPS time (atomic clock)
 235+ 'GPSSatellites' => Exif::ASCII, # Satellites used for measurement
 236+ 'GPSStatus' => Exif::ASCII, # Receiver status #p54
 237+ 'GPSMeasureMode' => Exif::ASCII, # Measurement mode #p54-55
 238+ 'GPSDOP' => Exif::RATIONAL, # Measurement precision
 239+ 'GPSSpeedRef' => Exif::ASCII, # Speed unit #p55
 240+ 'GPSSpeed' => Exif::RATIONAL, # Speed of GPS receiver
 241+ 'GPSTrackRef' => Exif::ASCII, # Reference for direction of movement #p55
 242+ 'GPSTrack' => Exif::RATIONAL, # Direction of movement
 243+ 'GPSImgDirectionRef' => Exif::ASCII, # Reference for direction of image #p56
 244+ 'GPSImgDirection' => Exif::RATIONAL, # Direction of image
 245+ 'GPSMapDatum' => Exif::ASCII, # Geodetic survey data used
 246+ 'GPSDestLatitudeRef' => Exif::ASCII, # Reference for latitude of destination #p56
 247+ 'GPSDestLatitude' => array( Exif::RATIONAL, 3 ), # Latitude destination
 248+ 'GPSDestLongitudeRef' => Exif::ASCII, # Reference for longitude of destination #p57
 249+ 'GPSDestLongitude' => array( Exif::RATIONAL, 3 ), # Longitude of destination
 250+ 'GPSDestBearingRef' => Exif::ASCII, # Reference for bearing of destination #p57
 251+ 'GPSDestBearing' => Exif::RATIONAL, # Bearing of destination
 252+ 'GPSDestDistanceRef' => Exif::ASCII, # Reference for distance to destination #p57-58
 253+ 'GPSDestDistance' => Exif::RATIONAL, # Distance to destination
 254+ 'GPSProcessingMethod' => Exif::UNDEFINED, # Name of GPS processing method
 255+ 'GPSAreaInformation' => Exif::UNDEFINED, # Name of GPS area
 256+ 'GPSDateStamp' => Exif::ASCII, # GPS date
 257+ 'GPSDifferential' => Exif::SHORT, # GPS differential correction
288258 ),
289259 );
290260
291261 $this->file = $file;
292262 $this->basename = wfBaseName( $this->file );
293263
294 - $this->makeFlatExifTags();
295 -
296264 $this->debugFile( $this->basename, __FUNCTION__, true );
297265 wfSuppressWarnings();
298 - $data = exif_read_data( $this->file );
 266+ $data = exif_read_data( $this->file, 0, true );
299267 wfRestoreWarnings();
300268 /**
301269 * exif_read_data() will return false on invalid input, such as
@@ -302,56 +270,35 @@
303271 * containing random gibberish.
304272 */
305273 $this->mRawExifData = $data ? $data : array();
306 -
307274 $this->makeFilteredData();
308275 $this->makeFormattedData();
309 -
310276 $this->debugFile( __FUNCTION__, false );
311277 }
312278
313 - /**#@+
314 - * @private
315 - */
316279 /**
317 - * Generate a flat list of the exif tags
318 - */
319 - function makeFlatExifTags() {
320 - $this->extractTags( $this->mExifTags );
321 - }
322 -
323 - /**
324 - * A recursing extractor function used by makeFlatExifTags()
325 - *
326 - * Note: This used to use an array_walk function, but it made PHP5
327 - * segfault, see `cvs diff -u -r 1.4 -r 1.5 Exif.php`
328 - */
329 - function extractTags( &$tagset ) {
330 - foreach( $tagset as $key => $val ) {
331 - if( is_array( $val ) ) {
332 - $this->extractTags( $val );
333 - } else {
334 - $this->mFlatExifTags[$key] = $val;
335 - }
336 - }
337 - }
338 -
339 - /**
340280 * Make $this->mFilteredExifData
341281 */
342282 function makeFilteredData() {
343283 $this->mFilteredExifData = $this->mRawExifData;
344284
345 - foreach( $this->mFilteredExifData as $k => $v ) {
346 - if ( !in_array( $k, array_keys( $this->mFlatExifTags ) ) ) {
347 - $this->debug( $v, __FUNCTION__, "'$k' is not a valid Exif tag" );
348 - unset( $this->mFilteredExifData[$k] );
 285+ foreach( array_keys( $this->mFilteredExifData ) as $section ) {
 286+ if ( !in_array( $section, array_keys( $this->mExifTags ) ) ) {
 287+ $this->debug( $section , __FUNCTION__, "'$section' is not a valid Exif section" );
 288+ unset( $this->mFilteredExifData[$section] );
 289+ continue;
349290 }
350 - }
351291
352 - foreach( $this->mFilteredExifData as $k => $v ) {
353 - if ( !$this->validate($k, $v) ) {
354 - $this->debug( $v, __FUNCTION__, "'$k' contained invalid data" );
355 - unset( $this->mFilteredExifData[$k] );
 292+ foreach( array_keys( $this->mFilteredExifData[$section] ) as $tag ) {
 293+ if ( !in_array( $tag, array_keys( $this->mExifTags[$section] ) ) ) {
 294+ $this->debug( $tag, __FUNCTION__, "'$tag' is not a valid tag in '$section'" );
 295+ unset( $this->mFilteredExifData[$section][$tag] );
 296+ continue;
 297+ }
 298+ $value = $this->mFilteredExifData[$section][$tag];
 299+ if( !$this->validate( $section, $tag, $value ) ) {
 300+ $this->debug( $value, __FUNCTION__, "'$tag' contained invalid data" );
 301+ unset( $this->mFilteredExifData[$section][$tag] );
 302+ }
356303 }
357304 }
358305 }
@@ -403,7 +350,7 @@
404351 * @return int
405352 */
406353 public static function version() {
407 - return 1; // We don't need no bloddy constants!
 354+ return 2; // We don't need no bloddy constants!
408355 }
409356
410357 /**#@+
@@ -507,15 +454,36 @@
508455 * Validates if a tag has a legal value according to the Exif spec
509456 *
510457 * @private
511 - *
 458+ * @param $section String: section where tag is located.
512459 * @param $tag String: the tag to check.
513460 * @param $val Mixed: the value of the tag.
 461+ * @param $recursive Boolean: true if called recursively for array types.
514462 * @return bool
515463 */
516 - function validate( $tag, $val ) {
 464+ function validate( $section, $tag, $val, $recursive = false ) {
517465 $debug = "tag is '$tag'";
 466+ $etype = $this->mExifTags[$section][$tag];
 467+ $ecount = 1;
 468+ if( is_array( $etype ) ) {
 469+ list( $etype, $ecount ) = $etype;
 470+ if ( $recursive )
 471+ $ecount = 1; // checking individual elements
 472+ }
 473+ $count = count( $val );
 474+ if( $ecount != $count ) {
 475+ $this->debug( $val, __FUNCTION__, "Expected $ecount elements for $tag but got $count" );
 476+ return false;
 477+ }
 478+ if( $count > 1 ) {
 479+ foreach( $val as $v ) {
 480+ if( !$this->validate( $section, $tag, $v, true ) ) {
 481+ return false;
 482+ }
 483+ }
 484+ return true;
 485+ }
518486 // Does not work if not typecast
519 - switch( (string)$this->mFlatExifTags[$tag] ) {
 487+ switch( (string)$etype ) {
520488 case (string)Exif::BYTE:
521489 $this->debug( $val, __FUNCTION__, $debug );
522490 return $this->isByte( $val );
@@ -633,11 +601,12 @@
634602 function getFormattedData() {
635603 global $wgLang;
636604
637 - $tags =& $this->mExif;
 605+ $sections =& $this->mExif;
638606
639 - $resolutionunit = !isset( $tags['ResolutionUnit'] ) || $tags['ResolutionUnit'] == 2 ? 2 : 3;
640 - unset( $tags['ResolutionUnit'] );
 607+ $resolutionunit = !isset( $sections['IFD0']['ResolutionUnit'] ) || $sections['IFD0']['ResolutionUnit'] == 2 ? 2 : 3;
 608+ unset( $sections['IFD0']['ResolutionUnit'] );
641609
 610+ foreach( $sections as $section => &$tags ) {
642611 foreach( $tags as $tag => $val ) {
643612 switch( $tag ) {
644613 case 'Compression':
@@ -703,7 +672,7 @@
704673 break;
705674
706675 // TODO: YCbCrCoefficients #p27 (see annex E)
707 - case 'ExifVersion': case 'FlashpixVersion':
 676+ case 'ExifVersion': case 'FlashPixVersion':
708677 $tags[$tag] = "$val"/100;
709678 break;
710679
@@ -967,6 +936,24 @@
968937 }
969938 break;
970939
 940+ case 'GPSAltitudeRef':
 941+ switch( $val ) {
 942+ case 0: case 1:
 943+ $tags[$tag] = $this->msg( 'GPSAltitude', $val );
 944+ break;
 945+ default:
 946+ $tags[$tag] = $val;
 947+ break;
 948+ }
 949+ break;
 950+
 951+ case 'GPSLatitude':
 952+ case 'GPSDestLatitude':
 953+ case 'GPSLongitude':
 954+ case 'GPSDestLongitude':
 955+ $tags[$tag] = $this->formatCoords( $val );
 956+ break;
 957+
971958 case 'GPSStatus':
972959 switch( $val ) {
973960 case 'A': case 'V':
@@ -990,7 +977,6 @@
991978 break;
992979
993980 case 'GPSSpeedRef':
994 - case 'GPSDestDistanceRef':
995981 switch( $val ) {
996982 case 'K': case 'M': case 'N':
997983 $tags[$tag] = $this->msg( 'GPSSpeed', $val );
@@ -1001,6 +987,17 @@
1002988 }
1003989 break;
1004990
 991+ case 'GPSDestDistanceRef':
 992+ switch( $val ) {
 993+ case 'K': case 'M': case 'N':
 994+ $tags[$tag] = $this->msg( 'GPSDestDistance', $val );
 995+ break;
 996+ default:
 997+ $tags[$tag] = $val;
 998+ break;
 999+ }
 1000+ break;
 1001+
10051002 case 'GPSTrackRef':
10061003 case 'GPSImgDirectionRef':
10071004 case 'GPSDestBearingRef':
@@ -1048,8 +1045,9 @@
10491046 break;
10501047 }
10511048 }
 1049+ }
10521050
1053 - return $tags;
 1051+ return $this->mExif;
10541052 }
10551053
10561054 /**
@@ -1072,7 +1070,7 @@
10731071
10741072 /**
10751073 * Format a number, convert numbers from fractions into floating point
1076 - * numbers
 1074+ * numbers, joins arrays of numbers with commas.
10771075 *
10781076 * @private
10791077 *
@@ -1080,7 +1078,15 @@
10811079 * @return mixed A floating point number or whatever we were fed
10821080 */
10831081 function formatNum( $num ) {
 1082+ global $wgLang;
10841083 $m = array();
 1084+ if( is_array($num) ) {
 1085+ $out = array();
 1086+ foreach( $num as $number ) {
 1087+ $out[] = $this->formatNum($number);
 1088+ }
 1089+ return $wgLang->commaList( $out );
 1090+ }
10851091 if ( preg_match( '/^(\d+)\/(\d+)$/', $num, $m ) )
10861092 return $m[2] != 0 ? $m[1] / $m[2] : $num;
10871093 else
@@ -1135,6 +1141,29 @@
11361142 }
11371143 return $a;
11381144 }
 1145+
 1146+ /**
 1147+ * Format a coordinate value, convert numbers from fractions
 1148+ * into floating point numbers, .
 1149+ *
 1150+ * @private
 1151+ *
 1152+ * @param $coords Array: degrees, minutes and seconds
 1153+ * @param $ref String: reference direction (N/S/E/W), optional
 1154+ * @return mixed A floating point number or whatever we were fed
 1155+ */
 1156+ function formatCoords( $coords, $ref = null ) {
 1157+ list($deg, $min, $sec) = $coords;
 1158+ $deg = $this->formatNum($deg);
 1159+ $min = $this->formatNum($min);
 1160+ $sec = $this->formatNum($sec);
 1161+ $out = $deg . "°";
 1162+ if ($min) $out .= " " . $min . "'";
 1163+ if ($sec) $out .= " " . $sec . '"';
 1164+ if ($ref) $out .= " " . $ref;
 1165+ return $out;
 1166+ }
 1167+
11391168 }
11401169
11411170 /**
Index: trunk/phase3/includes/media/Bitmap.php
@@ -326,14 +326,16 @@
327327 $formatted = $format->getFormattedData();
328328 // Sort fields into visible and collapsed
329329 $visibleFields = $this->visibleMetadataFields();
330 - foreach ( $formatted as $name => $value ) {
331 - $tag = strtolower( $name );
332 - self::addMeta( $result,
333 - in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
334 - 'exif',
335 - $tag,
336 - $value
337 - );
 330+ foreach ( $formatted as $section => $tags ) {
 331+ foreach ( $tags as $name => $value ) {
 332+ $tag = strtolower( $name );
 333+ self::addMeta( $result,
 334+ in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
 335+ 'exif',
 336+ $tag,
 337+ $value
 338+ );
 339+ }
338340 }
339341 return $result;
340342 }
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -3536,17 +3536,26 @@
35373537 'exif-gpslongitude-e' => 'East longitude',
35383538 'exif-gpslongitude-w' => 'West longitude',
35393539
 3540+# Pseudotags used for GPSAltitudeRef
 3541+'exif-gpsaltitude-0' => 'Metres above sea level',
 3542+'exif-gpsaltitude-1' => 'Metres below sea level',
 3543+
35403544 'exif-gpsstatus-a' => 'Measurement in progress',
35413545 'exif-gpsstatus-v' => 'Measurement interoperability',
35423546
35433547 'exif-gpsmeasuremode-2' => '2-dimensional measurement',
35443548 'exif-gpsmeasuremode-3' => '3-dimensional measurement',
35453549
3546 -# Pseudotags used for GPSSpeedRef and GPSDestDistanceRef
 3550+# Pseudotags used for GPSSpeedRef
35473551 'exif-gpsspeed-k' => 'Kilometres per hour',
35483552 'exif-gpsspeed-m' => 'Miles per hour',
35493553 'exif-gpsspeed-n' => 'Knots',
35503554
 3555+# Pseudotags used for GPSDestDistanceRef
 3556+'exif-gpsdestdistance-k' => 'Kilometres',
 3557+'exif-gpsdestdistance-m' => 'Miles',
 3558+'exif-gpsdestdistance-n' => 'Nautical miles',
 3559+
35513560 # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
35523561 'exif-gpsdirection-t' => 'True direction',
35533562 'exif-gpsdirection-m' => 'Magnetic direction',

Follow-up revisions

RevisionCommit summaryAuthorDate
r49678relnotes for r49677vyznev22:32, 20 April 2009
r49695Follow up r49677: Add new messages to maintenance scriptsraymond11:41, 21 April 2009
r50018Followup to r49677: move en_gb spellings of 'metre' to MessagesEn_gb, stick t...brion21:17, 28 April 2009
r50459fix regression from r49677 by refusing to format old-style metadata even if a...vyznev20:33, 10 May 2009
r51540Revert r49677, r50459, Brent G's patch for fixing GPS coordinates in EXIF. As...tstarling15:48, 6 June 2009
r51557Follow up to r51540 (revert r49677, r50459): global $wgLang was missing causi...siebrand22:04, 6 June 2009
r51562Follow-up to r51540 (revert r49677, r50459): also revert changes to messages.incsiebrand10:46, 7 June 2009
r68295Merge r49695 and r49677 into this branch....bawolff22:36, 19 June 2010

Comments

#Comment by Brion VIBBER (talk | contribs)   21:19, 28 April 2009

I've tidied up the en_gb'isms in r50018; now using en_us spelling for meters, kilometers in MessagesEn, moved en_gb variants to MessagesEn_gb

#Comment by Nikerabbit (talk | contribs)   11:39, 9 May 2009

This causes the following for one of translatewiki.net images: [09-May-2009 11:31:48] PHP Warning: Invalid argument supplied for foreach() in /var/www/w/includes/Exif.php on line 610 [times N]

#Comment by Ilmari Karonen (talk | contribs)   14:52, 10 May 2009

Can't reproduce this right now. Can you tell what image is triggering it?

#Comment by Ilmari Karonen (talk | contribs)   20:52, 10 May 2009

Okay, should be fixed in r50459. The problem was that Commons, which is still running the pre-r49677 code, was handing out serialized metadata in the old format, and the foreign repo code was blindly trusting this metadata to be valid and trying to display it as if it were in the new format. I added an extra safety check to BitmapHandler::formatMetadata() so that it'll fail gracefully if fed metadata with the wrong version. It might be a good idea to sprinkle some more calls to isMetadataValid() into the filerepo code too, but at least the extra version check means formatMetadata() won't freak out just because someone fed it old data.

As a side effect, wikis running post-r50459 code won't show any metadata for Commons images until Commons itself starts providing metadata in the new format. Also, wikis running pre-r49677 code and pulling files from Commons will probably show broken metadata once Commons is upgraded past r49677. Alas, there's nothing I can do about the latter, except to ensure that it won't happen again (which is what r50459 does).

In principle, it shouldn't be too hard to kluge FormatExif and BitmapHandler to support both the v1 and v2 metadata formats. I'm not sure if it's worth it, though, given that this is all a transient issue that will go away once all wikis are upgraded to use the new format.

#Comment by Brion VIBBER (talk | contribs)   17:01, 11 May 2009

r50459 is definitely a problem -- it requires all clients to run the same version as the repo or things will mysteriously not show, which would be a maintenance nightmare. Backwards and forwards compatibility should be preserved if possible.

#Comment by Tim Starling (talk | contribs)   15:22, 6 June 2009

Almost missed this. Adding scaptrap tag since there's a possibility that this change could crash the site. We've seen this before with file metadata invalidation, an overload of the file storage backend leading to an overflow of connection limits everywhere and thus total downtime. Maybe it'll work, maybe not. Best to avoid the risk if possible.

Status & tagging log