Index: trunk/phase3/includes/Exif.php |
— | — | @@ -17,7 +17,7 @@ |
18 | 18 | * |
19 | 19 | * @ingroup Media |
20 | 20 | * @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 |
22 | 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License |
23 | 23 | * @see http://exif.org/Exif2-2.PDF The Exif 2.2 specification |
24 | 24 | * @file |
— | — | @@ -51,11 +51,6 @@ |
52 | 52 | var $mExifTags; |
53 | 53 | |
54 | 54 | /** |
55 | | - * A one dimentional array of all Exif tags |
56 | | - */ |
57 | | - var $mFlatExifTags; |
58 | | - |
59 | | - /** |
60 | 55 | * The raw Exif data returned by exif_read_data() |
61 | 56 | */ |
62 | 57 | var $mRawExifData; |
— | — | @@ -109,192 +104,165 @@ |
110 | 105 | */ |
111 | 106 | $this->mExifTags = array( |
112 | 107 | # TIFF Rev. 6.0 Attribute Information (p22) |
113 | | - 'tiff' => array( |
| 108 | + 'IFD0' => array( |
114 | 109 | # 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) |
132 | 125 | |
133 | 126 | # 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 |
141 | 132 | |
142 | 133 | # 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 |
150 | 139 | |
151 | 140 | # 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 |
161 | 148 | ), |
162 | 149 | |
163 | 150 | # 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 |
172 | 156 | |
173 | 157 | # 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 |
177 | 159 | |
178 | 160 | # 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 |
185 | 165 | |
186 | 166 | # 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 |
191 | 169 | |
192 | 170 | # Tags relating to related file information |
193 | | - 'related' => array( |
194 | | - 'RelatedSoundFile' => Exif::ASCII, # Related audio file |
195 | | - ), |
| 171 | + 'RelatedSoundFile' => Exif::ASCII, # Related audio file |
196 | 172 | |
197 | 173 | # 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 |
205 | 179 | |
206 | 180 | # 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 |
249 | 221 | |
250 | | - 'other' => array( |
251 | | - 'ImageUniqueID' => Exif::ASCII, # Unique image ID |
252 | | - ), |
| 222 | + 'ImageUniqueID' => Exif::ASCII, # Unique image ID |
253 | 223 | ), |
254 | 224 | |
255 | 225 | # 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 |
288 | 258 | ), |
289 | 259 | ); |
290 | 260 | |
291 | 261 | $this->file = $file; |
292 | 262 | $this->basename = wfBaseName( $this->file ); |
293 | 263 | |
294 | | - $this->makeFlatExifTags(); |
295 | | - |
296 | 264 | $this->debugFile( $this->basename, __FUNCTION__, true ); |
297 | 265 | wfSuppressWarnings(); |
298 | | - $data = exif_read_data( $this->file ); |
| 266 | + $data = exif_read_data( $this->file, 0, true ); |
299 | 267 | wfRestoreWarnings(); |
300 | 268 | /** |
301 | 269 | * exif_read_data() will return false on invalid input, such as |
— | — | @@ -302,56 +270,35 @@ |
303 | 271 | * containing random gibberish. |
304 | 272 | */ |
305 | 273 | $this->mRawExifData = $data ? $data : array(); |
306 | | - |
307 | 274 | $this->makeFilteredData(); |
308 | 275 | $this->makeFormattedData(); |
309 | | - |
310 | 276 | $this->debugFile( __FUNCTION__, false ); |
311 | 277 | } |
312 | 278 | |
313 | | - /**#@+ |
314 | | - * @private |
315 | | - */ |
316 | 279 | /** |
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 | | - /** |
340 | 280 | * Make $this->mFilteredExifData |
341 | 281 | */ |
342 | 282 | function makeFilteredData() { |
343 | 283 | $this->mFilteredExifData = $this->mRawExifData; |
344 | 284 | |
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; |
349 | 290 | } |
350 | | - } |
351 | 291 | |
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 | + } |
356 | 303 | } |
357 | 304 | } |
358 | 305 | } |
— | — | @@ -403,7 +350,7 @@ |
404 | 351 | * @return int |
405 | 352 | */ |
406 | 353 | public static function version() { |
407 | | - return 1; // We don't need no bloddy constants! |
| 354 | + return 2; // We don't need no bloddy constants! |
408 | 355 | } |
409 | 356 | |
410 | 357 | /**#@+ |
— | — | @@ -507,15 +454,36 @@ |
508 | 455 | * Validates if a tag has a legal value according to the Exif spec |
509 | 456 | * |
510 | 457 | * @private |
511 | | - * |
| 458 | + * @param $section String: section where tag is located. |
512 | 459 | * @param $tag String: the tag to check. |
513 | 460 | * @param $val Mixed: the value of the tag. |
| 461 | + * @param $recursive Boolean: true if called recursively for array types. |
514 | 462 | * @return bool |
515 | 463 | */ |
516 | | - function validate( $tag, $val ) { |
| 464 | + function validate( $section, $tag, $val, $recursive = false ) { |
517 | 465 | $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 | + } |
518 | 486 | // Does not work if not typecast |
519 | | - switch( (string)$this->mFlatExifTags[$tag] ) { |
| 487 | + switch( (string)$etype ) { |
520 | 488 | case (string)Exif::BYTE: |
521 | 489 | $this->debug( $val, __FUNCTION__, $debug ); |
522 | 490 | return $this->isByte( $val ); |
— | — | @@ -633,11 +601,12 @@ |
634 | 602 | function getFormattedData() { |
635 | 603 | global $wgLang; |
636 | 604 | |
637 | | - $tags =& $this->mExif; |
| 605 | + $sections =& $this->mExif; |
638 | 606 | |
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'] ); |
641 | 609 | |
| 610 | + foreach( $sections as $section => &$tags ) { |
642 | 611 | foreach( $tags as $tag => $val ) { |
643 | 612 | switch( $tag ) { |
644 | 613 | case 'Compression': |
— | — | @@ -703,7 +672,7 @@ |
704 | 673 | break; |
705 | 674 | |
706 | 675 | // TODO: YCbCrCoefficients #p27 (see annex E) |
707 | | - case 'ExifVersion': case 'FlashpixVersion': |
| 676 | + case 'ExifVersion': case 'FlashPixVersion': |
708 | 677 | $tags[$tag] = "$val"/100; |
709 | 678 | break; |
710 | 679 | |
— | — | @@ -967,6 +936,24 @@ |
968 | 937 | } |
969 | 938 | break; |
970 | 939 | |
| 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 | + |
971 | 958 | case 'GPSStatus': |
972 | 959 | switch( $val ) { |
973 | 960 | case 'A': case 'V': |
— | — | @@ -990,7 +977,6 @@ |
991 | 978 | break; |
992 | 979 | |
993 | 980 | case 'GPSSpeedRef': |
994 | | - case 'GPSDestDistanceRef': |
995 | 981 | switch( $val ) { |
996 | 982 | case 'K': case 'M': case 'N': |
997 | 983 | $tags[$tag] = $this->msg( 'GPSSpeed', $val ); |
— | — | @@ -1001,6 +987,17 @@ |
1002 | 988 | } |
1003 | 989 | break; |
1004 | 990 | |
| 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 | + |
1005 | 1002 | case 'GPSTrackRef': |
1006 | 1003 | case 'GPSImgDirectionRef': |
1007 | 1004 | case 'GPSDestBearingRef': |
— | — | @@ -1048,8 +1045,9 @@ |
1049 | 1046 | break; |
1050 | 1047 | } |
1051 | 1048 | } |
| 1049 | + } |
1052 | 1050 | |
1053 | | - return $tags; |
| 1051 | + return $this->mExif; |
1054 | 1052 | } |
1055 | 1053 | |
1056 | 1054 | /** |
— | — | @@ -1072,7 +1070,7 @@ |
1073 | 1071 | |
1074 | 1072 | /** |
1075 | 1073 | * Format a number, convert numbers from fractions into floating point |
1076 | | - * numbers |
| 1074 | + * numbers, joins arrays of numbers with commas. |
1077 | 1075 | * |
1078 | 1076 | * @private |
1079 | 1077 | * |
— | — | @@ -1080,7 +1078,15 @@ |
1081 | 1079 | * @return mixed A floating point number or whatever we were fed |
1082 | 1080 | */ |
1083 | 1081 | function formatNum( $num ) { |
| 1082 | + global $wgLang; |
1084 | 1083 | $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 | + } |
1085 | 1091 | if ( preg_match( '/^(\d+)\/(\d+)$/', $num, $m ) ) |
1086 | 1092 | return $m[2] != 0 ? $m[1] / $m[2] : $num; |
1087 | 1093 | else |
— | — | @@ -1135,6 +1141,29 @@ |
1136 | 1142 | } |
1137 | 1143 | return $a; |
1138 | 1144 | } |
| 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 | + |
1139 | 1168 | } |
1140 | 1169 | |
1141 | 1170 | /** |
Index: trunk/phase3/includes/media/Bitmap.php |
— | — | @@ -326,14 +326,16 @@ |
327 | 327 | $formatted = $format->getFormattedData(); |
328 | 328 | // Sort fields into visible and collapsed |
329 | 329 | $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 | + } |
338 | 340 | } |
339 | 341 | return $result; |
340 | 342 | } |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -3536,17 +3536,26 @@ |
3537 | 3537 | 'exif-gpslongitude-e' => 'East longitude', |
3538 | 3538 | 'exif-gpslongitude-w' => 'West longitude', |
3539 | 3539 | |
| 3540 | +# Pseudotags used for GPSAltitudeRef |
| 3541 | +'exif-gpsaltitude-0' => 'Metres above sea level', |
| 3542 | +'exif-gpsaltitude-1' => 'Metres below sea level', |
| 3543 | + |
3540 | 3544 | 'exif-gpsstatus-a' => 'Measurement in progress', |
3541 | 3545 | 'exif-gpsstatus-v' => 'Measurement interoperability', |
3542 | 3546 | |
3543 | 3547 | 'exif-gpsmeasuremode-2' => '2-dimensional measurement', |
3544 | 3548 | 'exif-gpsmeasuremode-3' => '3-dimensional measurement', |
3545 | 3549 | |
3546 | | -# Pseudotags used for GPSSpeedRef and GPSDestDistanceRef |
| 3550 | +# Pseudotags used for GPSSpeedRef |
3547 | 3551 | 'exif-gpsspeed-k' => 'Kilometres per hour', |
3548 | 3552 | 'exif-gpsspeed-m' => 'Miles per hour', |
3549 | 3553 | 'exif-gpsspeed-n' => 'Knots', |
3550 | 3554 | |
| 3555 | +# Pseudotags used for GPSDestDistanceRef |
| 3556 | +'exif-gpsdestdistance-k' => 'Kilometres', |
| 3557 | +'exif-gpsdestdistance-m' => 'Miles', |
| 3558 | +'exif-gpsdestdistance-n' => 'Nautical miles', |
| 3559 | + |
3551 | 3560 | # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef |
3552 | 3561 | 'exif-gpsdirection-t' => 'True direction', |
3553 | 3562 | 'exif-gpsdirection-m' => 'Magnetic direction', |