blob: 0377300cf2b3c67d7128ca602c44eda2cd1cad7d [file] [log] [blame]
The Android Open Source Project34a25642009-03-03 19:30:03 -08001//--------------------------------------------------------------------------
2// Parsing of GPS info from exif header.
3//
Ray Chen434623a2010-03-10 14:59:44 -08004// Matthias Wandel, Dec 1999 - Dec 2002
The Android Open Source Project34a25642009-03-03 19:30:03 -08005//--------------------------------------------------------------------------
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
Ray Chena39920c2010-01-15 13:58:10 -080018#define TAG_GPS_TIMESTAMP 7
Ray Chen434623a2010-03-10 14:59:44 -080019#define TAG_GPS_PROCESSING_METHOD 27
Ray Chena39920c2010-01-15 13:58:10 -080020#define TAG_GPS_DATESTAMP 29
The Android Open Source Project34a25642009-03-03 19:30:03 -080021
22static 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},
Wu-cheng Li1ed81972010-05-21 12:02:32 +080029 { 0x06, "GPSAltitude", FMT_URATIONAL, 1},
The Android Open Source Project34a25642009-03-03 19:30:03 -080030 { 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},
Tyler Luubd900942011-10-12 17:46:32 -050050 { 0x1B, "GPSProcessingMethod", FMT_UNDEFINED, -1},
The Android Open Source Project34a25642009-03-03 19:30:03 -080051 { 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))
Ray Chen434623a2010-03-10 14:59:44 -080057#define EXIF_ASCII_PREFIX_LEN (sizeof(ExifAsciiPrefix))
The Android Open Source Project34a25642009-03-03 19:30:03 -080058
59// Define the line below to turn on poor man's debugging output
60#undef SUPERDEBUG
61
62#ifdef SUPERDEBUG
Steve Block7a314da2012-01-06 19:10:19 +000063#define printf ALOGE
The Android Open Source Project34a25642009-03-03 19:30:03 -080064#endif
65
66
67int IsGpsTag(const char* tag) {
68 return strstr(tag, "GPS") == tag;
69}
70
71TagTable_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
89int 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
107int 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//--------------------------------------------------------------------------
124void 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.
Teow Wan Yee92f207c2016-08-24 09:50:36 +0800177 if (OffsetVal > UINT32_MAX - ByteCount || OffsetVal+ByteCount > ExifLength){
The Android Open Source Project34a25642009-03-03 19:30:03 -0800178 // 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];
Wang Kun22bfc1902010-11-24 13:27:17 +0100190 char TempString[MAX_BUF_SIZE];
The Android Open Source Project34a25642009-03-03 19:30:03 -0800191 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");
The Android Open Source Project34a25642009-03-03 19:30:03 -0800211 for (a=0;a<3;a++){
212 int den, digits;
213
214 den = Get32s(ValuePtr+4+a*ComponentSize);
215 digits = 0;
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700216 while (den > 1 && digits <= 6){
The Android Open Source Project34a25642009-03-03 19:30:03 -0800217 den = den / 10;
218 digits += 1;
219 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700220 if (digits > 6) digits = 6;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800221 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 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700226
The Android Open Source Project34a25642009-03-03 19:30:03 -0800227 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){
Wang Kun22bfc1902010-11-24 13:27:17 +0100240 strncpy(ImageInfo.GpsLatRaw, TempString, MAX_BUF_SIZE);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800241 }else{
Wang Kun22bfc1902010-11-24 13:27:17 +0100242 strncpy(ImageInfo.GpsLongRaw, TempString, MAX_BUF_SIZE);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800243 }
244 break;
245
246 case TAG_GPS_ALT_REF:
247 ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' ');
Wu-cheng Li1ed81972010-05-21 12:02:32 +0800248 ImageInfo.GpsAltRef = (char)ValuePtr[0];
The Android Open Source Project34a25642009-03-03 19:30:03 -0800249 break;
250
251 case TAG_GPS_ALT:
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700252 sprintf(ImageInfo.GpsAlt + 1, "%.2fm",
253 ConvertAnyFormat(ValuePtr, Format));
Wu-cheng Li1ed81972010-05-21 12:02:32 +0800254 ImageInfo.GpsAltRaw.num = Get32u(ValuePtr);
255 ImageInfo.GpsAltRaw.denom = Get32u(4+(char *)ValuePtr);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800256 break;
Ray Chena39920c2010-01-15 13:58:10 -0800257
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;
Ray Chen434623a2010-03-10 14:59:44 -0800270
271 case TAG_GPS_PROCESSING_METHOD:
Ray Chen8d617232010-03-10 14:59:44 -0800272 if (ByteCount > EXIF_ASCII_PREFIX_LEN &&
Ray Chen434623a2010-03-10 14:59:44 -0800273 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 {
Steve Blockd2826142012-01-05 23:18:54 +0000281 ALOGW("Unsupported encoding for GPSProcessingMethod");
Ray Chen434623a2010-03-10 14:59:44 -0800282 }
283 break;
The Android Open Source Project34a25642009-03-03 19:30:03 -0800284 }
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 }
Chih-Chung Changd6a02c32009-03-24 20:34:28 -0700294
The Android Open Source Project34a25642009-03-03 19:30:03 -0800295 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
Ray Chen434623a2010-03-10 14:59:44 -0800334