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