1 //--------------------------------------------------------------------------
2 // Parsing of GPS info from exif header.
3 //
4 // Matthias Wandel,  Dec 1999 - Dec 2002
5 //--------------------------------------------------------------------------
6 #include "jhead.h"
7 
8 #include <string.h>
9 #include <utils/Log.h>
10 
11 
12 #define TAG_GPS_LAT_REF    1
13 #define TAG_GPS_LAT        2
14 #define TAG_GPS_LONG_REF   3
15 #define TAG_GPS_LONG       4
16 #define TAG_GPS_ALT_REF    5
17 #define TAG_GPS_ALT        6
18 #define TAG_GPS_TIMESTAMP  7
19 #define TAG_GPS_PROCESSING_METHOD 27
20 #define TAG_GPS_DATESTAMP  29
21 
22 static TagTable_t GpsTags[]= {
23     { 0x00, "GPSVersionID", FMT_BYTE, 4},
24     { 0x01, "GPSLatitudeRef", FMT_STRING, 2},
25     { 0x02, "GPSLatitude", FMT_URATIONAL, 3},
26     { 0x03, "GPSLongitudeRef", FMT_STRING, 2},
27     { 0x04, "GPSLongitude", FMT_URATIONAL, 3},
28     { 0x05, "GPSAltitudeRef", FMT_BYTE, 1},
29     { 0x06, "GPSAltitude", FMT_URATIONAL, 1},
30     { 0x07, "GPSTimeStamp", FMT_SRATIONAL, 3},
31     { 0x08, "GPSSatellites", FMT_STRING, -1},
32     { 0x09, "GPSStatus", FMT_STRING, 2},
33     { 0x0A, "GPSMeasureMode", FMT_STRING, 2},
34     { 0x0B, "GPSDOP", FMT_SRATIONAL, 1},
35     { 0x0C, "GPSSpeedRef", FMT_STRING, 2},
36     { 0x0D, "GPSSpeed", FMT_SRATIONAL, 1},
37     { 0x0E, "GPSTrackRef", FMT_STRING, 2},
38     { 0x0F, "GPSTrack", FMT_SRATIONAL, 1},
39     { 0x10, "GPSImgDirectionRef", FMT_STRING, -1},
40     { 0x11, "GPSImgDirection", FMT_SRATIONAL, 1},
41     { 0x12, "GPSMapDatum", FMT_STRING, -1},
42     { 0x13, "GPSDestLatitudeRef", FMT_STRING, 2},
43     { 0x14, "GPSDestLatitude", FMT_SRATIONAL, 3},
44     { 0x15, "GPSDestLongitudeRef", FMT_STRING, 2},
45     { 0x16, "GPSDestLongitude", FMT_SRATIONAL, 3},
46     { 0x17, "GPSDestBearingRef", FMT_STRING, 1},
47     { 0x18, "GPSDestBearing", FMT_SRATIONAL, 1},
48     { 0x19, "GPSDestDistanceRef", FMT_STRING, 2},
49     { 0x1A, "GPSDestDistance", FMT_SRATIONAL, 1},
50     { 0x1B, "GPSProcessingMethod", FMT_UNDEFINED, -1},
51     { 0x1C, "GPSAreaInformation", FMT_STRING, -1},
52     { 0x1D, "GPSDateStamp", FMT_STRING, 11},
53     { 0x1E, "GPSDifferential", FMT_SSHORT, 1},
54 };
55 
56 #define MAX_GPS_TAG  (sizeof(GpsTags) / sizeof(TagTable_t))
57 #define EXIF_ASCII_PREFIX_LEN (sizeof(ExifAsciiPrefix))
58 
59 // Define the line below to turn on poor man's debugging output
60 #undef SUPERDEBUG
61 
62 #ifdef SUPERDEBUG
63 #define printf ALOGE
64 #endif
65 
66 
IsGpsTag(const char * tag)67 int IsGpsTag(const char* tag) {
68     return strstr(tag, "GPS") == tag;
69 }
70 
GpsTagToTagTableEntry(unsigned short tag)71 TagTable_t* GpsTagToTagTableEntry(unsigned short tag)
72 {
73     unsigned int i;
74     for (i = 0; i < MAX_GPS_TAG; i++) {
75         if (GpsTags[i].Tag == tag) {
76             printf("found tag %d", tag);
77             int format = GpsTags[i].Format;
78             if (format == 0) {
79                 printf("tag %s format not defined", GpsTags[i].Desc);
80                 return NULL;
81             }
82             return &GpsTags[i];
83         }
84     }
85     printf("tag %d NOT FOUND", tag);
86     return NULL;
87 }
88 
GpsTagToFormatType(unsigned short tag)89 int GpsTagToFormatType(unsigned short tag)
90 {
91     unsigned int i;
92     for (i = 0; i < MAX_GPS_TAG; i++) {
93         if (GpsTags[i].Tag == tag) {
94             printf("found tag %d", tag);
95             int format = GpsTags[i].Format;
96             if (format == 0) {
97                 printf("tag %s format not defined", GpsTags[i].Desc);
98                 return -1;
99             }
100             return format;
101         }
102     }
103     printf("tag %d NOT FOUND", tag);
104     return -1;
105 }
106 
GpsTagNameToValue(const char * tagName)107 int GpsTagNameToValue(const char* tagName)
108 {
109     unsigned int i;
110     for (i = 0; i < MAX_GPS_TAG; i++) {
111         if (strcmp(GpsTags[i].Desc, tagName) == 0) {
112             printf("found GPS tag %s val %d", GpsTags[i].Desc, GpsTags[i].Tag);
113             return GpsTags[i].Tag;
114         }
115     }
116     printf("GPS tag %s NOT FOUND", tagName);
117     return -1;
118 }
119 
120 
121 //--------------------------------------------------------------------------
122 // Process GPS info directory
123 //--------------------------------------------------------------------------
ProcessGpsInfo(unsigned char * DirStart,int ByteCountUnused,unsigned char * OffsetBase,unsigned ExifLength)124 void ProcessGpsInfo(unsigned char * DirStart, int ByteCountUnused, unsigned char * OffsetBase, unsigned ExifLength)
125 {
126     int de;
127     unsigned a;
128     int NumDirEntries;
129 
130     NumDirEntries = Get16u(DirStart);
131     #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
132 
133     if (ShowTags){
134         printf("(dir has %d entries)\n",NumDirEntries);
135     }
136 
137     ImageInfo.GpsInfoPresent = TRUE;
138     strcpy(ImageInfo.GpsLat, "? ?");
139     strcpy(ImageInfo.GpsLong, "? ?");
140     ImageInfo.GpsAlt[0] = 0;
141 
142     for (de=0;de<NumDirEntries;de++){
143         unsigned Tag, Format, Components;
144         unsigned char * ValuePtr;
145         int ComponentSize;
146         unsigned ByteCount;
147         unsigned char * DirEntry;
148         DirEntry = DIR_ENTRY_ADDR(DirStart, de);
149 
150         if (DirEntry+12 > OffsetBase+ExifLength){
151             ErrNonfatal("GPS info directory goes past end of exif",0,0);
152             return;
153         }
154 
155         Tag = Get16u(DirEntry);
156         Format = Get16u(DirEntry+2);
157         Components = Get32u(DirEntry+4);
158 
159         if ((Format-1) >= NUM_FORMATS) {
160             // (-1) catches illegal zero case as unsigned underflows to positive large.
161             ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
162             continue;
163         }
164 
165         ComponentSize = BytesPerFormat[Format];
166         ByteCount = Components * ComponentSize;
167 
168 #ifdef SUPERDEBUG
169     printf("GPS tag %x format %s #components %d componentsize %d bytecount %d", Tag, formatStr(Format), Components, ComponentSize,
170             ByteCount);
171 #endif
172 
173         if (ByteCount > 4){
174             unsigned OffsetVal;
175             OffsetVal = Get32u(DirEntry+8);
176             // If its bigger than 4 bytes, the dir entry contains an offset.
177             if (OffsetVal+ByteCount > ExifLength){
178                 // Bogus pointer offset and / or bytecount value
179                 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
180                 continue;
181             }
182             ValuePtr = OffsetBase+OffsetVal;
183         }else{
184             // 4 bytes or less and value is in the dir entry itself
185             ValuePtr = DirEntry+8;
186         }
187 
188         switch(Tag){
189             char FmtString[21];
190             char TempString[MAX_BUF_SIZE];
191             double Values[3];
192 
193             case TAG_GPS_LAT_REF:
194                 ImageInfo.GpsLat[0] = ValuePtr[0];
195                 ImageInfo.GpsLatRef[0] = ValuePtr[0];
196                 ImageInfo.GpsLatRef[1] = '\0';
197                 break;
198 
199             case TAG_GPS_LONG_REF:
200                 ImageInfo.GpsLong[0] = ValuePtr[0];
201                 ImageInfo.GpsLongRef[0] = ValuePtr[0];
202                 ImageInfo.GpsLongRef[1] = '\0';
203                 break;
204 
205             case TAG_GPS_LAT:
206             case TAG_GPS_LONG:
207                 if (Format != FMT_URATIONAL){
208                     ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0);
209                 }
210                 strcpy(FmtString, "%0.0fd %0.0fm %0.0fs");
211                 for (a=0;a<3;a++){
212                     int den, digits;
213 
214                     den = Get32s(ValuePtr+4+a*ComponentSize);
215                     digits = 0;
216                     while (den > 1 && digits <= 6){
217                         den = den / 10;
218                         digits += 1;
219                     }
220                     if (digits > 6) digits = 6;
221                     FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0));
222                     FmtString[3+a*7] = (char)('0'+digits);
223 
224                     Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
225                 }
226 
227                 sprintf(TempString, FmtString, Values[0], Values[1], Values[2]);
228 
229                 if (Tag == TAG_GPS_LAT){
230                     strncpy(ImageInfo.GpsLat+2, TempString, 29);
231                 }else{
232                     strncpy(ImageInfo.GpsLong+2, TempString, 29);
233                 }
234 
235                 sprintf(TempString, "%d/%d,%d/%d,%d/%d",
236                     Get32s(ValuePtr), Get32s(4+(char*)ValuePtr),
237                     Get32s(8+(char*)ValuePtr), Get32s(12+(char*)ValuePtr),
238                     Get32s(16+(char*)ValuePtr), Get32s(20+(char*)ValuePtr));
239                 if (Tag == TAG_GPS_LAT){
240                     strncpy(ImageInfo.GpsLatRaw, TempString, MAX_BUF_SIZE);
241                 }else{
242                     strncpy(ImageInfo.GpsLongRaw, TempString, MAX_BUF_SIZE);
243                 }
244                 break;
245 
246             case TAG_GPS_ALT_REF:
247                 ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' ');
248                 ImageInfo.GpsAltRef = (char)ValuePtr[0];
249                 break;
250 
251             case TAG_GPS_ALT:
252                 sprintf(ImageInfo.GpsAlt + 1, "%.2fm",
253                     ConvertAnyFormat(ValuePtr, Format));
254                 ImageInfo.GpsAltRaw.num = Get32u(ValuePtr);
255                 ImageInfo.GpsAltRaw.denom = Get32u(4+(char *)ValuePtr);
256                 break;
257 
258             case TAG_GPS_TIMESTAMP:
259                 snprintf(ImageInfo.GpsTimeStamp,
260                     sizeof(ImageInfo.GpsTimeStamp), "%d:%d:%d",
261                     (int) ConvertAnyFormat(ValuePtr, Format),
262                     (int) ConvertAnyFormat(ValuePtr + 8, Format),
263                     (int) ConvertAnyFormat(ValuePtr + 16, Format)
264                 );
265                 break;
266 
267             case TAG_GPS_DATESTAMP:
268                 strncpy(ImageInfo.GpsDateStamp, (char*)ValuePtr, sizeof(ImageInfo.GpsDateStamp));
269                 break;
270 
271             case TAG_GPS_PROCESSING_METHOD:
272                 if (ByteCount > EXIF_ASCII_PREFIX_LEN &&
273                     memcmp(ValuePtr, ExifAsciiPrefix, EXIF_ASCII_PREFIX_LEN) == 0) {
274                     int length =
275                         ByteCount < GPS_PROCESSING_METHOD_LEN + EXIF_ASCII_PREFIX_LEN ?
276                         ByteCount - EXIF_ASCII_PREFIX_LEN : GPS_PROCESSING_METHOD_LEN;
277                     memcpy(ImageInfo.GpsProcessingMethod,
278                         (char*)(ValuePtr + EXIF_ASCII_PREFIX_LEN), length);
279                     ImageInfo.GpsProcessingMethod[length] = 0;
280                 } else {
281                     ALOGW("Unsupported encoding for GPSProcessingMethod");
282                 }
283                 break;
284         }
285 
286         if (ShowTags){
287             // Show tag value.
288             if (Tag < MAX_GPS_TAG){
289                 printf("        %s =", GpsTags[Tag].Desc);
290             }else{
291                 // Show unknown tag
292                 printf("        Illegal GPS tag %04x=", Tag);
293             }
294 
295             switch(Format){
296                 case FMT_UNDEFINED:
297                     // Undefined is typically an ascii string.
298 
299                 case FMT_STRING:
300                     // String arrays printed without function call (different from int arrays)
301                     {
302                         printf("\"");
303                         for (a=0;a<ByteCount;a++){
304                             int ZeroSkipped = 0;
305                             if (ValuePtr[a] >= 32){
306                                 if (ZeroSkipped){
307                                     printf("?");
308                                     ZeroSkipped = 0;
309                                 }
310                                 putchar(ValuePtr[a]);
311                             }else{
312                                 if (ValuePtr[a] == 0){
313                                     ZeroSkipped = 1;
314                                 }
315                             }
316                         }
317                         printf("\"\n");
318                     }
319                     break;
320 
321                 default:
322                     // Handle arrays of numbers later (will there ever be?)
323                     for (a=0;;){
324                         PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount);
325                         if (++a >= Components) break;
326                         printf(", ");
327                     }
328                     printf("\n");
329             }
330         }
331     }
332 }
333 
334 
335