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