1 : /******************************************************************************
2 : * $Id: gdalexif.cpp 24551 2012-06-09 20:19:04Z rouault $
3 : *
4 : * Project: GDAL
5 : * Purpose: Implements a EXIF directory reader
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Frank Warmerdam
10 : *
11 : * Portions Copyright (c) Her majesty the Queen in right of Canada as
12 : * represented by the Minister of National Defence, 2006.
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : ****************************************************************************/
32 :
33 : #include "cpl_conv.h"
34 : #include "cpl_port.h"
35 : #include "cpl_error.h"
36 : #include "cpl_string.h"
37 : #include "cpl_vsi.h"
38 :
39 : #include "gdalexif.h"
40 :
41 : CPL_CVSID("$Id: gdalexif.cpp 24551 2012-06-09 20:19:04Z rouault $");
42 :
43 : /************************************************************************/
44 : /* EXIFPrintData() */
45 : /************************************************************************/
46 81 : static void EXIFPrintData(char* pszData, GUInt16 type,
47 : GUInt32 count, unsigned char* data)
48 : {
49 81 : const char* sep = "";
50 : char pszTemp[128];
51 81 : char* pszDataEnd = pszData;
52 :
53 81 : pszData[0]='\0';
54 :
55 81 : switch (type) {
56 :
57 : case TIFF_UNDEFINED:
58 : case TIFF_BYTE:
59 40 : for(;count>0;count--) {
60 32 : sprintf(pszTemp, "%s%#02x", sep, *data++), sep = " ";
61 32 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
62 0 : break;
63 32 : strcat(pszDataEnd,pszTemp);
64 32 : pszDataEnd += strlen(pszDataEnd);
65 : }
66 8 : break;
67 :
68 : case TIFF_SBYTE:
69 0 : for(;count>0;count--) {
70 0 : sprintf(pszTemp, "%s%d", sep, *(char *)data++), sep = " ";
71 0 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
72 0 : break;
73 0 : strcat(pszDataEnd,pszTemp);
74 0 : pszDataEnd += strlen(pszDataEnd);
75 : }
76 0 : break;
77 :
78 : case TIFF_ASCII:
79 41 : memcpy( pszData, data, count );
80 41 : pszData[count] = '\0';
81 41 : break;
82 :
83 : case TIFF_SHORT: {
84 12 : register GUInt16 *wp = (GUInt16*)data;
85 24 : for(;count>0;count--) {
86 12 : sprintf(pszTemp, "%s%u", sep, *wp++), sep = " ";
87 12 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
88 0 : break;
89 12 : strcat(pszDataEnd,pszTemp);
90 12 : pszDataEnd += strlen(pszDataEnd);
91 : }
92 12 : break;
93 : }
94 : case TIFF_SSHORT: {
95 0 : register GInt16 *wp = (GInt16*)data;
96 0 : for(;count>0;count--) {
97 0 : sprintf(pszTemp, "%s%d", sep, *wp++), sep = " ";
98 0 : strcat(pszData,pszTemp);
99 : }
100 0 : break;
101 : }
102 : case TIFF_LONG: {
103 0 : register GUInt32 *lp = (GUInt32*)data;
104 0 : for(;count>0;count--) {
105 0 : sprintf(pszTemp, "%s%lu", sep, (unsigned long) *lp++);
106 0 : sep = " ";
107 0 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
108 0 : break;
109 0 : strcat(pszDataEnd,pszTemp);
110 0 : pszDataEnd += strlen(pszDataEnd);
111 : }
112 0 : break;
113 : }
114 : case TIFF_SLONG: {
115 0 : register GInt32 *lp = (GInt32*)data;
116 0 : for(;count>0;count--) {
117 0 : sprintf(pszTemp, "%s%ld", sep, (long) *lp++), sep = " ";
118 0 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
119 0 : break;
120 0 : strcat(pszDataEnd,pszTemp);
121 0 : pszDataEnd += strlen(pszDataEnd);
122 : }
123 0 : break;
124 : }
125 : case TIFF_RATIONAL: {
126 20 : register GUInt32 *lp = (GUInt32*)data;
127 : // if(bSwabflag)
128 : // TIFFSwabArrayOfLong((GUInt32*) data, 2*count);
129 64 : for(;count>0;count--) {
130 44 : if( (lp[0]==0) && (lp[1] == 0) ) {
131 0 : sprintf(pszTemp,"%s(0)",sep);
132 : }
133 : else{
134 : sprintf(pszTemp, "%s(%g)", sep,
135 44 : (double) lp[0]/ (double)lp[1]);
136 : }
137 44 : sep = " ";
138 44 : lp += 2;
139 44 : strcat(pszData,pszTemp);
140 : }
141 20 : break;
142 : }
143 : case TIFF_SRATIONAL: {
144 0 : register GInt32 *lp = (GInt32*)data;
145 0 : for(;count>0;count--) {
146 : sprintf(pszTemp, "%s(%g)", sep,
147 0 : (float) lp[0]/ (float) lp[1]);
148 0 : sep = " ";
149 0 : lp += 2;
150 0 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
151 0 : break;
152 0 : strcat(pszDataEnd,pszTemp);
153 0 : pszDataEnd += strlen(pszDataEnd);
154 : }
155 0 : break;
156 : }
157 : case TIFF_FLOAT: {
158 0 : register float *fp = (float *)data;
159 0 : for(;count>0;count--) {
160 0 : sprintf(pszTemp, "%s%g", sep, *fp++), sep = " ";
161 0 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
162 0 : break;
163 0 : strcat(pszDataEnd,pszTemp);
164 0 : pszDataEnd += strlen(pszDataEnd);
165 : }
166 0 : break;
167 : }
168 : case TIFF_DOUBLE: {
169 0 : register double *dp = (double *)data;
170 0 : for(;count>0;count--) {
171 0 : sprintf(pszTemp, "%s%g", sep, *dp++), sep = " ";
172 0 : if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH)
173 0 : break;
174 0 : strcat(pszDataEnd,pszTemp);
175 0 : pszDataEnd += strlen(pszDataEnd);
176 : }
177 0 : break;
178 : }
179 :
180 : default:
181 0 : return;
182 : }
183 :
184 81 : if (type != TIFF_ASCII && count != 0)
185 : {
186 0 : CPLError(CE_Warning, CPLE_AppDefined, "EXIF metadata truncated");
187 : }
188 : }
189 :
190 :
191 : /************************************************************************/
192 : /* EXIFExtractMetadata() */
193 : /* */
194 : /* Extract all entry from a IFD */
195 : /************************************************************************/
196 18 : CPLErr EXIFExtractMetadata(char**& papszMetadata,
197 : void *fpInL, int nOffset,
198 : int bSwabflag, int nTIFFHEADER,
199 : int& nExifOffset, int& nInterOffset, int& nGPSOffset)
200 : {
201 : GUInt16 nEntryCount;
202 : int space;
203 : unsigned int n,i;
204 : char pszTemp[MAXSTRINGLENGTH];
205 : char pszName[128];
206 :
207 18 : VSILFILE* fp = (VSILFILE* )fpInL;
208 :
209 : TIFFDirEntry *poTIFFDirEntry;
210 : TIFFDirEntry *poTIFFDir;
211 : const struct tagname *poExifTags ;
212 18 : const struct intr_tag *poInterTags = intr_tags;
213 : const struct gpsname *poGPSTags;
214 :
215 : /* -------------------------------------------------------------------- */
216 : /* Read number of entry in directory */
217 : /* -------------------------------------------------------------------- */
218 18 : if( VSIFSeekL(fp, nOffset+nTIFFHEADER, SEEK_SET) != 0
219 : || VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fp) != sizeof(GUInt16) )
220 : {
221 : CPLError( CE_Failure, CPLE_AppDefined,
222 : "Error reading EXIF Directory count at %d.",
223 0 : nOffset + nTIFFHEADER );
224 0 : return CE_Failure;
225 : }
226 :
227 18 : if (bSwabflag)
228 2 : TIFFSwabShort(&nEntryCount);
229 :
230 : // Some apps write empty directories - see bug 1523.
231 18 : if( nEntryCount == 0 )
232 0 : return CE_None;
233 :
234 : // Some files are corrupt, a large entry count is a sign of this.
235 18 : if( nEntryCount > 125 )
236 : {
237 : CPLError( CE_Warning, CPLE_AppDefined,
238 : "Ignoring EXIF directory with unlikely entry count (%d).",
239 1 : nEntryCount );
240 1 : return CE_Warning;
241 : }
242 :
243 17 : poTIFFDir = (TIFFDirEntry *)CPLMalloc(nEntryCount * sizeof(TIFFDirEntry));
244 :
245 : /* -------------------------------------------------------------------- */
246 : /* Read all directory entries */
247 : /* -------------------------------------------------------------------- */
248 17 : n = VSIFReadL(poTIFFDir, 1,nEntryCount*sizeof(TIFFDirEntry),fp);
249 17 : if (n != nEntryCount*sizeof(TIFFDirEntry))
250 : {
251 : CPLError( CE_Failure, CPLE_AppDefined,
252 0 : "Could not read all directories");
253 0 : return CE_Failure;
254 : }
255 :
256 : /* -------------------------------------------------------------------- */
257 : /* Parse all entry information in this directory */
258 : /* -------------------------------------------------------------------- */
259 107 : for(poTIFFDirEntry = poTIFFDir,i=nEntryCount; i > 0; i--,poTIFFDirEntry++) {
260 90 : if (bSwabflag) {
261 2 : TIFFSwabShort(&poTIFFDirEntry->tdir_tag);
262 2 : TIFFSwabShort(&poTIFFDirEntry->tdir_type);
263 2 : TIFFSwabLong (&poTIFFDirEntry->tdir_count);
264 2 : TIFFSwabLong (&poTIFFDirEntry->tdir_offset);
265 : }
266 :
267 : /* -------------------------------------------------------------------- */
268 : /* Find Tag name in table */
269 : /* -------------------------------------------------------------------- */
270 90 : pszName[0] = '\0';
271 90 : pszTemp[0] = '\0';
272 :
273 5533 : for (poExifTags = tagnames; poExifTags->tag; poExifTags++)
274 5494 : if(poExifTags->tag == poTIFFDirEntry->tdir_tag) {
275 51 : CPLAssert( NULL != poExifTags && NULL != poExifTags->name );
276 :
277 51 : strcpy(pszName, poExifTags->name);
278 51 : break;
279 : }
280 :
281 :
282 90 : if( nOffset == nGPSOffset) {
283 90 : for( poGPSTags = gpstags; poGPSTags->tag != 0xffff; poGPSTags++ )
284 90 : if( poGPSTags->tag == poTIFFDirEntry->tdir_tag ) {
285 30 : CPLAssert( NULL != poGPSTags && NULL != poGPSTags->name );
286 30 : strcpy(pszName, poGPSTags->name);
287 30 : break;
288 : }
289 : }
290 : /* -------------------------------------------------------------------- */
291 : /* If the tag was not found, look into the interoperability table */
292 : /* -------------------------------------------------------------------- */
293 90 : if( nOffset == nInterOffset ) {
294 0 : for(poInterTags = intr_tags; poInterTags->tag; poInterTags++)
295 0 : if(poInterTags->tag == poTIFFDirEntry->tdir_tag) {
296 0 : CPLAssert( NULL != poInterTags && NULL != poInterTags->name );
297 0 : strcpy(pszName, poInterTags->name);
298 0 : break;
299 : }
300 : }
301 :
302 : /* -------------------------------------------------------------------- */
303 : /* Save important directory tag offset */
304 : /* -------------------------------------------------------------------- */
305 90 : if( poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG )
306 5 : nExifOffset=poTIFFDirEntry->tdir_offset;
307 90 : if( poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET )
308 0 : nInterOffset=poTIFFDirEntry->tdir_offset;
309 90 : if( poTIFFDirEntry->tdir_tag == GPSOFFSETTAG ) {
310 4 : nGPSOffset=poTIFFDirEntry->tdir_offset;
311 : }
312 :
313 : /* -------------------------------------------------------------------- */
314 : /* If we didn't recognise the tag just ignore it. To see all */
315 : /* tags comment out the continue. */
316 : /* -------------------------------------------------------------------- */
317 90 : if( pszName[0] == '\0' )
318 : {
319 9 : sprintf( pszName, "EXIF_%d", poTIFFDirEntry->tdir_tag );
320 9 : continue;
321 : }
322 :
323 : /* -------------------------------------------------------------------- */
324 : /* For UserComment we need to ignore the language binding and */
325 : /* just return the actual contents. */
326 : /* -------------------------------------------------------------------- */
327 81 : if( EQUAL(pszName,"EXIF_UserComment") )
328 : {
329 0 : poTIFFDirEntry->tdir_type = TIFF_ASCII;
330 :
331 0 : if( poTIFFDirEntry->tdir_count >= 8 )
332 : {
333 0 : poTIFFDirEntry->tdir_count -= 8;
334 0 : poTIFFDirEntry->tdir_offset += 8;
335 : }
336 : }
337 :
338 : /* -------------------------------------------------------------------- */
339 : /* Make some UNDEFINED or BYTE fields ASCII for readability. */
340 : /* -------------------------------------------------------------------- */
341 81 : if( EQUAL(pszName,"EXIF_ExifVersion")
342 : || EQUAL(pszName,"EXIF_FlashPixVersion")
343 : || EQUAL(pszName,"EXIF_MakerNote")
344 : || EQUAL(pszName,"GPSProcessingMethod") )
345 10 : poTIFFDirEntry->tdir_type = TIFF_ASCII;
346 :
347 : /* -------------------------------------------------------------------- */
348 : /* Print tags */
349 : /* -------------------------------------------------------------------- */
350 81 : int nDataWidth = TIFFDataWidth((TIFFDataType) poTIFFDirEntry->tdir_type);
351 81 : space = poTIFFDirEntry->tdir_count * nDataWidth;
352 :
353 : /* Previous multiplication could overflow, hence this additional check */
354 81 : if (poTIFFDirEntry->tdir_count > MAXSTRINGLENGTH)
355 : {
356 : CPLError( CE_Warning, CPLE_AppDefined,
357 : "Too many bytes in tag: %u, ignoring tag.",
358 0 : poTIFFDirEntry->tdir_count );
359 : }
360 81 : else if (nDataWidth == 0 || poTIFFDirEntry->tdir_type >= TIFF_IFD )
361 : {
362 : CPLError( CE_Warning, CPLE_AppDefined,
363 : "Invalid or unhandled EXIF data type: %d, ignoring tag.",
364 0 : poTIFFDirEntry->tdir_type );
365 : }
366 : /* -------------------------------------------------------------------- */
367 : /* This is at most 4 byte data so we can read it from tdir_offset */
368 : /* -------------------------------------------------------------------- */
369 131 : else if (space >= 0 && space <= 4) {
370 :
371 : unsigned char data[4];
372 50 : memcpy(data, &poTIFFDirEntry->tdir_offset, 4);
373 50 : if (bSwabflag)
374 : {
375 : // Unswab 32bit value, and reswab per data type.
376 0 : TIFFSwabLong((GUInt32*) data);
377 :
378 0 : switch (poTIFFDirEntry->tdir_type) {
379 : case TIFF_LONG:
380 : case TIFF_SLONG:
381 : case TIFF_FLOAT:
382 0 : TIFFSwabLong((GUInt32*) data);
383 0 : break;
384 :
385 : case TIFF_SSHORT:
386 : case TIFF_SHORT:
387 : TIFFSwabArrayOfShort((GUInt16*) data,
388 0 : poTIFFDirEntry->tdir_count);
389 : break;
390 :
391 : default:
392 : break;
393 : }
394 : }
395 :
396 : EXIFPrintData(pszTemp,
397 : poTIFFDirEntry->tdir_type,
398 50 : poTIFFDirEntry->tdir_count, data);
399 : }
400 : /* -------------------------------------------------------------------- */
401 : /* The data is being read where tdir_offset point to in the file */
402 : /* -------------------------------------------------------------------- */
403 62 : else if (space > 0 && space < MAXSTRINGLENGTH)
404 : {
405 31 : unsigned char *data = (unsigned char *)VSIMalloc(space);
406 :
407 31 : if (data) {
408 31 : VSIFSeekL(fp,poTIFFDirEntry->tdir_offset+nTIFFHEADER,SEEK_SET);
409 31 : VSIFReadL(data, 1, space, fp);
410 :
411 31 : if (bSwabflag) {
412 1 : switch (poTIFFDirEntry->tdir_type) {
413 : case TIFF_SHORT:
414 : case TIFF_SSHORT:
415 : TIFFSwabArrayOfShort((GUInt16*) data,
416 0 : poTIFFDirEntry->tdir_count);
417 0 : break;
418 : case TIFF_LONG:
419 : case TIFF_SLONG:
420 : case TIFF_FLOAT:
421 : TIFFSwabArrayOfLong((GUInt32*) data,
422 0 : poTIFFDirEntry->tdir_count);
423 0 : break;
424 : case TIFF_RATIONAL:
425 : case TIFF_SRATIONAL:
426 : TIFFSwabArrayOfLong((GUInt32*) data,
427 0 : 2*poTIFFDirEntry->tdir_count);
428 0 : break;
429 : case TIFF_DOUBLE:
430 : TIFFSwabArrayOfDouble((double*) data,
431 0 : poTIFFDirEntry->tdir_count);
432 : break;
433 : default:
434 : break;
435 : }
436 : }
437 :
438 : EXIFPrintData(pszTemp, poTIFFDirEntry->tdir_type,
439 31 : poTIFFDirEntry->tdir_count, data);
440 31 : CPLFree(data);
441 : }
442 : }
443 : else
444 : {
445 : CPLError( CE_Warning, CPLE_AppDefined,
446 : "Invalid EXIF header size: %ld, ignoring tag.",
447 0 : (long) space );
448 : }
449 :
450 81 : papszMetadata = CSLSetNameValue(papszMetadata, pszName, pszTemp);
451 : }
452 17 : CPLFree(poTIFFDir);
453 :
454 17 : return CE_None;
455 : }
|