Home | History | Annotate | Download | only in jhead
      1 //--------------------------------------------------------------------------
      2 // Program to pull the information out of various types of EXIF digital
      3 // camera files and show it in a reasonably consistent way
      4 //
      5 // This module parses the very complicated exif structures.
      6 //
      7 // Matthias Wandel
      8 //--------------------------------------------------------------------------
      9 #include "jhead.h"
     10 
     11 #include <math.h>
     12 #include <ctype.h>
     13 #include <utils/Log.h>
     14 
     15 static unsigned char * DirWithThumbnailPtrs;
     16 static double FocalplaneXRes;
     17 static double FocalplaneUnits;
     18 static int ExifImageWidth;
     19 static int MotorolaOrder = 0;
     20 
     21 // for fixing the rotation.
     22 static void * OrientationPtr[2];
     23 static int    OrientationNumFormat[2];
     24 int NumOrientations = 0;
     25 
     26 
     27 // Define the line below to turn on poor man's debugging output
     28 #undef SUPERDEBUG
     29 
     30 #ifdef SUPERDEBUG
     31 #define printf ALOGE
     32 #endif
     33 
     34 //--------------------------------------------------------------------------
     35 // Table of Jpeg encoding process names
     36 static const TagTable_t ProcessTable[] = {
     37     { M_SOF0,   "Baseline", 0, 0},
     38     { M_SOF1,   "Extended sequential", 0, 0},
     39     { M_SOF2,   "Progressive", 0, 0},
     40     { M_SOF3,   "Lossless", 0, 0},
     41     { M_SOF5,   "Differential sequential", 0, 0},
     42     { M_SOF6,   "Differential progressive", 0, 0},
     43     { M_SOF7,   "Differential lossless", 0, 0},
     44     { M_SOF9,   "Extended sequential, arithmetic coding", 0, 0},
     45     { M_SOF10,  "Progressive, arithmetic coding", 0, 0},
     46     { M_SOF11,  "Lossless, arithmetic coding", 0, 0},
     47     { M_SOF13,  "Differential sequential, arithmetic coding", 0, 0},
     48     { M_SOF14,  "Differential progressive, arithmetic coding", 0, 0},
     49     { M_SOF15,  "Differential lossless, arithmetic coding", 0, 0},
     50 };
     51 
     52 #define PROCESS_TABLE_SIZE  (sizeof(ProcessTable) / sizeof(TagTable_t))
     53 
     54 // 1 - "The 0th row is at the visual top of the image,    and the 0th column is the visual left-hand side."
     55 // 2 - "The 0th row is at the visual top of the image,    and the 0th column is the visual right-hand side."
     56 // 3 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side."
     57 // 4 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side."
     58 
     59 // 5 - "The 0th row is the visual left-hand side of of the image,  and the 0th column is the visual top."
     60 // 6 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual top."
     61 // 7 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual bottom."
     62 // 8 - "The 0th row is the visual left-hand side of of the image,  and the 0th column is the visual bottom."
     63 
     64 // Note: The descriptions here are the same as the name of the command line
     65 // option to pass to jpegtran to right the image
     66 
     67 static const char * OrientTab[9] = {
     68     "Undefined",
     69     "Normal",           // 1
     70     "flip horizontal",  // left right reversed mirror
     71     "rotate 180",       // 3
     72     "flip vertical",    // upside down mirror
     73     "transpose",        // Flipped about top-left <--> bottom-right axis.
     74     "rotate 90",        // rotate 90 cw to right it.
     75     "transverse",       // flipped about top-right <--> bottom-left axis
     76     "rotate 270",       // rotate 270 to right it.
     77 };
     78 
     79 const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
     80 
     81 //--------------------------------------------------------------------------
     82 // Describes tag values
     83 
     84 #define TAG_INTEROP_INDEX          0x0001
     85 #define TAG_INTEROP_VERSION        0x0002
     86 #define TAG_IMAGE_WIDTH            0x0100
     87 #define TAG_IMAGE_LENGTH           0x0101
     88 #define TAG_BITS_PER_SAMPLE        0x0102
     89 #define TAG_COMPRESSION            0x0103
     90 #define TAG_PHOTOMETRIC_INTERP     0x0106
     91 #define TAG_FILL_ORDER             0x010A
     92 #define TAG_DOCUMENT_NAME          0x010D
     93 #define TAG_IMAGE_DESCRIPTION      0x010E
     94 #define TAG_MAKE                   0x010F
     95 #define TAG_MODEL                  0x0110
     96 #define TAG_SRIP_OFFSET            0x0111
     97 #define TAG_ORIENTATION            0x0112
     98 #define TAG_SAMPLES_PER_PIXEL      0x0115
     99 #define TAG_ROWS_PER_STRIP         0x0116
    100 #define TAG_STRIP_BYTE_COUNTS      0x0117
    101 #define TAG_X_RESOLUTION           0x011A
    102 #define TAG_Y_RESOLUTION           0x011B
    103 #define TAG_PLANAR_CONFIGURATION   0x011C
    104 #define TAG_RESOLUTION_UNIT        0x0128
    105 #define TAG_TRANSFER_FUNCTION      0x012D
    106 #define TAG_SOFTWARE               0x0131
    107 #define TAG_DATETIME               0x0132
    108 #define TAG_ARTIST                 0x013B
    109 #define TAG_WHITE_POINT            0x013E
    110 #define TAG_PRIMARY_CHROMATICITIES 0x013F
    111 #define TAG_TRANSFER_RANGE         0x0156
    112 #define TAG_JPEG_PROC              0x0200
    113 #define TAG_THUMBNAIL_OFFSET       0x0201
    114 #define TAG_THUMBNAIL_LENGTH       0x0202
    115 #define TAG_Y_CB_CR_COEFFICIENTS   0x0211
    116 #define TAG_Y_CB_CR_SUB_SAMPLING   0x0212
    117 #define TAG_Y_CB_CR_POSITIONING    0x0213
    118 #define TAG_REFERENCE_BLACK_WHITE  0x0214
    119 #define TAG_RELATED_IMAGE_WIDTH    0x1001
    120 #define TAG_RELATED_IMAGE_LENGTH   0x1002
    121 #define TAG_CFA_REPEAT_PATTERN_DIM 0x828D
    122 #define TAG_CFA_PATTERN1           0x828E
    123 #define TAG_BATTERY_LEVEL          0x828F
    124 #define TAG_COPYRIGHT              0x8298
    125 #define TAG_EXPOSURETIME           0x829A
    126 #define TAG_FNUMBER                0x829D
    127 #define TAG_IPTC_NAA               0x83BB
    128 #define TAG_EXIF_OFFSET            0x8769
    129 #define TAG_INTER_COLOR_PROFILE    0x8773
    130 #define TAG_EXPOSURE_PROGRAM       0x8822
    131 #define TAG_SPECTRAL_SENSITIVITY   0x8824
    132 #define TAG_GPSINFO                0x8825
    133 #define TAG_ISO_EQUIVALENT         0x8827
    134 #define TAG_OECF                   0x8828
    135 #define TAG_EXIF_VERSION           0x9000
    136 #define TAG_DATETIME_ORIGINAL      0x9003
    137 #define TAG_DATETIME_DIGITIZED     0x9004
    138 #define TAG_COMPONENTS_CONFIG      0x9101
    139 #define TAG_CPRS_BITS_PER_PIXEL    0x9102
    140 #define TAG_SHUTTERSPEED           0x9201
    141 #define TAG_APERTURE               0x9202
    142 #define TAG_BRIGHTNESS_VALUE       0x9203
    143 #define TAG_EXPOSURE_BIAS          0x9204
    144 #define TAG_MAXAPERTURE            0x9205
    145 #define TAG_SUBJECT_DISTANCE       0x9206
    146 #define TAG_METERING_MODE          0x9207
    147 #define TAG_LIGHT_SOURCE           0x9208
    148 #define TAG_FLASH                  0x9209
    149 #define TAG_FOCALLENGTH            0x920A
    150 #define TAG_MAKER_NOTE             0x927C
    151 #define TAG_USERCOMMENT            0x9286
    152 #define TAG_SUBSEC_TIME            0x9290
    153 #define TAG_SUBSEC_TIME_ORIG       0x9291
    154 #define TAG_SUBSEC_TIME_DIG        0x9292
    155 
    156 #define TAG_WINXP_TITLE            0x9c9b // Windows XP - not part of exif standard.
    157 #define TAG_WINXP_COMMENT          0x9c9c // Windows XP - not part of exif standard.
    158 #define TAG_WINXP_AUTHOR           0x9c9d // Windows XP - not part of exif standard.
    159 #define TAG_WINXP_KEYWORDS         0x9c9e // Windows XP - not part of exif standard.
    160 #define TAG_WINXP_SUBJECT          0x9c9f // Windows XP - not part of exif standard.
    161 
    162 #define TAG_FLASH_PIX_VERSION      0xA000
    163 #define TAG_COLOR_SPACE            0xA001
    164 #define TAG_EXIF_IMAGEWIDTH        0xA002
    165 #define TAG_EXIF_IMAGELENGTH       0xA003
    166 #define TAG_RELATED_AUDIO_FILE     0xA004
    167 #define TAG_INTEROP_OFFSET         0xA005
    168 #define TAG_FLASH_ENERGY           0xA20B
    169 #define TAG_SPATIAL_FREQ_RESP      0xA20C
    170 #define TAG_FOCAL_PLANE_XRES       0xA20E
    171 #define TAG_FOCAL_PLANE_YRES       0xA20F
    172 #define TAG_FOCAL_PLANE_UNITS      0xA210
    173 #define TAG_SUBJECT_LOCATION       0xA214
    174 #define TAG_EXPOSURE_INDEX         0xA215
    175 #define TAG_SENSING_METHOD         0xA217
    176 #define TAG_FILE_SOURCE            0xA300
    177 #define TAG_SCENE_TYPE             0xA301
    178 #define TAG_CFA_PATTERN            0xA302
    179 #define TAG_CUSTOM_RENDERED        0xA401
    180 #define TAG_EXPOSURE_MODE          0xA402
    181 #define TAG_WHITEBALANCE           0xA403
    182 #define TAG_DIGITALZOOMRATIO       0xA404
    183 #define TAG_FOCALLENGTH_35MM       0xA405
    184 #define TAG_SCENE_CAPTURE_TYPE     0xA406
    185 #define TAG_GAIN_CONTROL           0xA407
    186 #define TAG_CONTRAST               0xA408
    187 #define TAG_SATURATION             0xA409
    188 #define TAG_SHARPNESS              0xA40A
    189 #define TAG_DISTANCE_RANGE         0xA40C
    190 
    191 // TODO: replace the ", 0" values in this table with the correct format, e.g. ", FMT_USHORT"
    192 static const TagTable_t TagTable[] = {
    193   { TAG_INTEROP_INDEX,          "InteropIndex", 0, 0},
    194   { TAG_INTEROP_VERSION,        "InteropVersion", 0, 0},
    195   { TAG_IMAGE_WIDTH,            "ImageWidth", FMT_USHORT, 1},
    196   { TAG_IMAGE_LENGTH,           "ImageLength", FMT_USHORT, 1},
    197   { TAG_BITS_PER_SAMPLE,        "BitsPerSample", FMT_USHORT, 3},
    198   { TAG_COMPRESSION,            "Compression", FMT_USHORT, 1},
    199   { TAG_PHOTOMETRIC_INTERP,     "PhotometricInterpretation", FMT_USHORT, 1},
    200   { TAG_FILL_ORDER,             "FillOrder", 0, 0},
    201   { TAG_DOCUMENT_NAME,          "DocumentName", 0, 0},
    202   { TAG_IMAGE_DESCRIPTION,      "ImageDescription", 0, 0 },
    203   { TAG_MAKE,                   "Make", FMT_STRING, -1},
    204   { TAG_MODEL,                  "Model", FMT_STRING, -1},
    205   { TAG_SRIP_OFFSET,            "StripOffsets", FMT_USHORT, 1},
    206   { TAG_ORIENTATION,            "Orientation", FMT_USHORT, 1},
    207   { TAG_SAMPLES_PER_PIXEL,      "SamplesPerPixel", FMT_USHORT, 3},
    208   { TAG_ROWS_PER_STRIP,         "RowsPerStrip", FMT_USHORT, 1},
    209   { TAG_STRIP_BYTE_COUNTS,      "StripByteCounts", FMT_USHORT, 1},
    210   { TAG_X_RESOLUTION,           "XResolution", FMT_URATIONAL, 1},
    211   { TAG_Y_RESOLUTION,           "YResolution", FMT_URATIONAL, 1},
    212   { TAG_PLANAR_CONFIGURATION,   "PlanarConfiguration", FMT_USHORT, 1},
    213   { TAG_RESOLUTION_UNIT,        "ResolutionUnit", FMT_USHORT, 1},
    214   { TAG_TRANSFER_FUNCTION,      "TransferFunction", FMT_USHORT, 768},
    215   { TAG_SOFTWARE,               "Software", FMT_STRING, -1},
    216   { TAG_DATETIME,               "DateTime", FMT_STRING, 20},
    217   { TAG_ARTIST,                 "Artist", FMT_STRING, -1},
    218   { TAG_WHITE_POINT,            "WhitePoint", FMT_SRATIONAL, 2},
    219   { TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities", FMT_SRATIONAL, 6},
    220   { TAG_TRANSFER_RANGE,         "TransferRange", 0, 0},
    221   { TAG_JPEG_PROC,              "JPEGProc", 0, 0},
    222   { TAG_THUMBNAIL_OFFSET,       "ThumbnailOffset", 0, 0},
    223   { TAG_THUMBNAIL_LENGTH,       "ThumbnailLength", 0, 0},
    224   { TAG_Y_CB_CR_COEFFICIENTS,   "YCbCrCoefficients", FMT_SRATIONAL, 3},
    225   { TAG_Y_CB_CR_SUB_SAMPLING,   "YCbCrSubSampling", FMT_USHORT, 2},
    226   { TAG_Y_CB_CR_POSITIONING,    "YCbCrPositioning", FMT_USHORT, 1},
    227   { TAG_REFERENCE_BLACK_WHITE,  "ReferenceBlackWhite", FMT_SRATIONAL, 6},
    228   { TAG_RELATED_IMAGE_WIDTH,    "RelatedImageWidth", 0, 0},
    229   { TAG_RELATED_IMAGE_LENGTH,   "RelatedImageLength", 0, 0},
    230   { TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim", 0, 0},
    231   { TAG_CFA_PATTERN1,           "CFAPattern", 0, 0},
    232   { TAG_BATTERY_LEVEL,          "BatteryLevel", 0, 0},
    233   { TAG_COPYRIGHT,              "Copyright", FMT_STRING, -1},
    234   { TAG_EXPOSURETIME,           "ExposureTime", FMT_SRATIONAL, 1},
    235   { TAG_FNUMBER,                "FNumber", FMT_SRATIONAL, 1},
    236   { TAG_IPTC_NAA,               "IPTC/NAA", 0, 0},
    237   { TAG_EXIF_OFFSET,            "ExifOffset", 0, 0},
    238   { TAG_INTER_COLOR_PROFILE,    "InterColorProfile", 0, 0},
    239   { TAG_EXPOSURE_PROGRAM,       "ExposureProgram", FMT_SSHORT, 1},
    240   { TAG_SPECTRAL_SENSITIVITY,   "SpectralSensitivity", FMT_STRING, -1},
    241   { TAG_GPSINFO,                "GPS Dir offset", 0, 0},
    242   { TAG_ISO_EQUIVALENT,         "ISOSpeedRatings", FMT_SSHORT, -1},
    243   { TAG_OECF,                   "OECF", 0, 0},
    244   { TAG_EXIF_VERSION,           "ExifVersion", FMT_BYTE, 4},
    245   { TAG_DATETIME_ORIGINAL,      "DateTimeOriginal", FMT_STRING, 20},
    246   { TAG_DATETIME_DIGITIZED,     "DateTimeDigitized", FMT_STRING, 20},
    247   { TAG_COMPONENTS_CONFIG,      "ComponentsConfiguration", FMT_BYTE, 4},
    248   { TAG_CPRS_BITS_PER_PIXEL,    "CompressedBitsPerPixel", FMT_SRATIONAL, 1},
    249   { TAG_SHUTTERSPEED,           "ShutterSpeedValue", FMT_SRATIONAL, 1},
    250   { TAG_APERTURE,               "ApertureValue", FMT_URATIONAL, 1},
    251   { TAG_BRIGHTNESS_VALUE,       "BrightnessValue", FMT_SRATIONAL, 1},
    252   { TAG_EXPOSURE_BIAS,          "ExposureBiasValue", FMT_SRATIONAL, 1},
    253   { TAG_MAXAPERTURE,            "MaxApertureValue", FMT_URATIONAL, 1},
    254   { TAG_SUBJECT_DISTANCE,       "SubjectDistance", FMT_URATIONAL, 1},
    255   { TAG_METERING_MODE,          "MeteringMode", FMT_USHORT, 1},
    256   { TAG_LIGHT_SOURCE,           "LightSource", FMT_USHORT, 1},
    257   { TAG_FLASH,                  "Flash", FMT_USHORT, 1},
    258   { TAG_FOCALLENGTH,            "FocalLength", FMT_URATIONAL, 1},
    259   { TAG_MAKER_NOTE,             "MakerNote", FMT_STRING, -1},
    260   { TAG_USERCOMMENT,            "UserComment", FMT_STRING, -1},
    261   { TAG_SUBSEC_TIME,            "SubSecTime", FMT_STRING, -1},
    262   { TAG_SUBSEC_TIME_ORIG,       "SubSecTimeOriginal", FMT_STRING, -1},
    263   { TAG_SUBSEC_TIME_DIG,        "SubSecTimeDigitized", FMT_STRING, -1},
    264   { TAG_WINXP_TITLE,            "Windows-XP Title", 0, 0},
    265   { TAG_WINXP_COMMENT,          "Windows-XP comment", 0, 0},
    266   { TAG_WINXP_AUTHOR,           "Windows-XP author", 0, 0},
    267   { TAG_WINXP_KEYWORDS,         "Windows-XP keywords", 0, 0},
    268   { TAG_WINXP_SUBJECT,          "Windows-XP subject", 0, 0},
    269   { TAG_FLASH_PIX_VERSION,      "FlashPixVersion", FMT_BYTE, 4},
    270   { TAG_COLOR_SPACE,            "ColorSpace", FMT_USHORT, 1},
    271   { TAG_EXIF_IMAGEWIDTH,        "ExifImageWidth", 0, 0},
    272   { TAG_EXIF_IMAGELENGTH,       "ExifImageLength", 0, 0},
    273   { TAG_RELATED_AUDIO_FILE,     "RelatedAudioFile", 0, 0},
    274   { TAG_INTEROP_OFFSET,         "InteroperabilityOffset", 0, 0},
    275   { TAG_FLASH_ENERGY,           "FlashEnergy", FMT_URATIONAL, 1},
    276   { TAG_SPATIAL_FREQ_RESP,      "SpatialFrequencyResponse", FMT_STRING, -1},
    277   { TAG_FOCAL_PLANE_XRES,       "FocalPlaneXResolution", FMT_URATIONAL, 1},
    278   { TAG_FOCAL_PLANE_YRES,       "FocalPlaneYResolution", FMT_URATIONAL, 1},
    279   { TAG_FOCAL_PLANE_UNITS,      "FocalPlaneResolutionUnit", FMT_USHORT, 1},
    280   { TAG_SUBJECT_LOCATION,       "SubjectLocation", FMT_USHORT, 2},
    281   { TAG_EXPOSURE_INDEX,         "ExposureIndex", FMT_URATIONAL, 1},
    282   { TAG_SENSING_METHOD,         "SensingMethod", FMT_USHORT, 1},
    283   { TAG_FILE_SOURCE,            "FileSource", 0, 1},
    284   { TAG_SCENE_TYPE,             "SceneType", 0, 1},
    285   { TAG_CFA_PATTERN,            "CFA Pattern", 0, -1},
    286   { TAG_CUSTOM_RENDERED,        "CustomRendered", FMT_USHORT, 1},
    287   { TAG_EXPOSURE_MODE,          "ExposureMode", FMT_USHORT, 1},
    288   { TAG_WHITEBALANCE,           "WhiteBalance", FMT_USHORT, 1},
    289   { TAG_DIGITALZOOMRATIO,       "DigitalZoomRatio", FMT_URATIONAL, 1},
    290   { TAG_FOCALLENGTH_35MM,       "FocalLengthIn35mmFilm", FMT_USHORT, 1},
    291   { TAG_SCENE_CAPTURE_TYPE,     "SceneCaptureType", FMT_USHORT, 1},
    292   { TAG_GAIN_CONTROL,           "GainControl", FMT_URATIONAL, 1},
    293   { TAG_CONTRAST,               "Contrast", FMT_USHORT, 1},
    294   { TAG_SATURATION,             "Saturation", FMT_USHORT, 1},
    295   { TAG_SHARPNESS,              "Sharpness", FMT_USHORT, 1},
    296   { TAG_DISTANCE_RANGE,         "SubjectDistanceRange", FMT_USHORT, 1},
    297 } ;
    298 
    299 #define TAG_TABLE_SIZE  (sizeof(TagTable) / sizeof(TagTable_t))
    300 
    301 int TagNameToValue(const char* tagName)
    302 {
    303     unsigned int i;
    304     for (i = 0; i < TAG_TABLE_SIZE; i++) {
    305         if (strcmp(TagTable[i].Desc, tagName) == 0) {
    306             printf("found tag %s val %d", TagTable[i].Desc, TagTable[i].Tag);
    307             return TagTable[i].Tag;
    308         }
    309     }
    310     printf("tag %s NOT FOUND", tagName);
    311     return -1;
    312 }
    313 
    314 int IsDateTimeTag(unsigned short tag)
    315 {
    316     return ((tag == TAG_DATETIME)? TRUE: FALSE);
    317 }
    318 
    319 //--------------------------------------------------------------------------
    320 // Convert a 16 bit unsigned value to file's native byte order
    321 //--------------------------------------------------------------------------
    322 static void Put16u(void * Short, unsigned short PutValue)
    323 {
    324     if (MotorolaOrder){
    325         ((uchar *)Short)[0] = (uchar)(PutValue>>8);
    326         ((uchar *)Short)[1] = (uchar)PutValue;
    327     }else{
    328         ((uchar *)Short)[0] = (uchar)PutValue;
    329         ((uchar *)Short)[1] = (uchar)(PutValue>>8);
    330     }
    331 }
    332 
    333 //--------------------------------------------------------------------------
    334 // Convert a 16 bit unsigned value from file's native byte order
    335 //--------------------------------------------------------------------------
    336 int Get16u(void * Short)
    337 {
    338     if (MotorolaOrder){
    339         return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
    340     }else{
    341         return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
    342     }
    343 }
    344 
    345 //--------------------------------------------------------------------------
    346 // Convert a 32 bit signed value from file's native byte order
    347 //--------------------------------------------------------------------------
    348 int Get32s(void * Long)
    349 {
    350     if (MotorolaOrder){
    351         return  ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
    352               | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
    353     }else{
    354         return  ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
    355               | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
    356     }
    357 }
    358 
    359 //--------------------------------------------------------------------------
    360 // Convert a 32 bit unsigned value to file's native byte order
    361 //--------------------------------------------------------------------------
    362 void Put32u(void * Value, unsigned PutValue)
    363 {
    364     if (MotorolaOrder){
    365         ((uchar *)Value)[0] = (uchar)(PutValue>>24);
    366         ((uchar *)Value)[1] = (uchar)(PutValue>>16);
    367         ((uchar *)Value)[2] = (uchar)(PutValue>>8);
    368         ((uchar *)Value)[3] = (uchar)PutValue;
    369     }else{
    370         ((uchar *)Value)[0] = (uchar)PutValue;
    371         ((uchar *)Value)[1] = (uchar)(PutValue>>8);
    372         ((uchar *)Value)[2] = (uchar)(PutValue>>16);
    373         ((uchar *)Value)[3] = (uchar)(PutValue>>24);
    374     }
    375 }
    376 
    377 //--------------------------------------------------------------------------
    378 // Convert a 32 bit unsigned value from file's native byte order
    379 //--------------------------------------------------------------------------
    380 unsigned Get32u(void * Long)
    381 {
    382     return (unsigned)Get32s(Long) & 0xffffffff;
    383 }
    384 
    385 //--------------------------------------------------------------------------
    386 // Display a number as one of its many formats
    387 //--------------------------------------------------------------------------
    388 void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount)
    389 {
    390     int s,n;
    391 
    392     for(n=0;n<16;n++){
    393         switch(Format){
    394             case FMT_SBYTE:
    395             case FMT_BYTE:      printf("%02x",*(uchar *)ValuePtr); s=1;  break;
    396             case FMT_USHORT:    printf("%d",Get16u(ValuePtr)); s=2;      break;
    397             case FMT_ULONG:
    398             case FMT_SLONG:     printf("%d",Get32s(ValuePtr)); s=4;      break;
    399             case FMT_SSHORT:    printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break;
    400             case FMT_URATIONAL:
    401             case FMT_SRATIONAL:
    402                printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
    403                s = 8;
    404                break;
    405 
    406             case FMT_SINGLE:    printf("%f",(double)*(float *)ValuePtr); s=8; break;
    407             case FMT_DOUBLE:    printf("%f",*(double *)ValuePtr);        s=8; break;
    408             default:
    409                 printf("Unknown format %d:", Format);
    410                 return;
    411         }
    412         ByteCount -= s;
    413         if (ByteCount <= 0) break;
    414         printf(", ");
    415         ValuePtr = (void *)((char *)ValuePtr + s);
    416 
    417     }
    418     if (n >= 16) printf("...");
    419 }
    420 
    421 
    422 //--------------------------------------------------------------------------
    423 // Evaluate number, be it int, rational, or float from directory.
    424 //--------------------------------------------------------------------------
    425 double ConvertAnyFormat(void * ValuePtr, int Format)
    426 {
    427     double Value;
    428     Value = 0;
    429 
    430     switch(Format){
    431         case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
    432         case FMT_BYTE:      Value = *(uchar *)ValuePtr;        break;
    433 
    434         case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
    435         case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
    436 
    437         case FMT_URATIONAL:
    438         case FMT_SRATIONAL:
    439             {
    440                 int Num,Den;
    441                 Num = Get32s(ValuePtr);
    442                 Den = Get32s(4+(char *)ValuePtr);
    443                 if (Den == 0){
    444                     Value = 0;
    445                 }else{
    446                     Value = (double)Num/Den;
    447                 }
    448                 break;
    449             }
    450 
    451         case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
    452         case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
    453 
    454         // Not sure if this is correct (never seen float used in Exif format)
    455         case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
    456         case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
    457 
    458         default:
    459             ErrNonfatal("Illegal format code %d",Format,0);
    460     }
    461     return Value;
    462 }
    463 
    464 //--------------------------------------------------------------------------
    465 // Process one of the nested EXIF directories.
    466 //--------------------------------------------------------------------------
    467 static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,
    468         unsigned ExifLength, int NestingLevel)
    469 {
    470     int de;
    471     int a;
    472     int NumDirEntries;
    473     unsigned ThumbnailOffset = 0;
    474     unsigned ThumbnailSize = 0;
    475     char IndentString[25];
    476 
    477     printf("ProcessExifDir");
    478     if (NestingLevel > 4){
    479         ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
    480         return;
    481     }
    482 
    483     memset(IndentString, ' ', 25);
    484     IndentString[NestingLevel * 4] = '\0';
    485 
    486 
    487     NumDirEntries = Get16u(DirStart);
    488     #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
    489 
    490     {
    491         unsigned char * DirEnd;
    492         DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
    493         if (DirEnd+4 > (OffsetBase+ExifLength)){
    494             if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
    495                 // Version 1.3 of jhead would truncate a bit too much.
    496                 // This also caught later on as well.
    497             }else{
    498                 ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0);
    499                 return;
    500             }
    501         }
    502         if (DumpExifMap){
    503             printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
    504         }
    505 
    506 
    507     }
    508 
    509     if (ShowTags){
    510         printf("(dir has %d entries)\n",NumDirEntries);
    511     }
    512 
    513     for (de=0;de<NumDirEntries;de++){
    514         int Tag, Format, Components;
    515         unsigned char * ValuePtr;
    516         int ByteCount;
    517         unsigned char * DirEntry;
    518         DirEntry = DIR_ENTRY_ADDR(DirStart, de);
    519 
    520         Tag = Get16u(DirEntry);
    521         Format = Get16u(DirEntry+2);
    522         Components = Get32u(DirEntry+4);
    523 
    524         if ((Format-1) >= NUM_FORMATS) {
    525             // (-1) catches illegal zero case as unsigned underflows to positive large.
    526             ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
    527             continue;
    528         }
    529 
    530         if ((unsigned)Components > 0x10000){
    531             ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
    532             continue;
    533         }
    534 
    535         ByteCount = Components * BytesPerFormat[Format];
    536 
    537         if (ByteCount > 4){
    538             unsigned OffsetVal;
    539             OffsetVal = Get32u(DirEntry+8);
    540             // If its bigger than 4 bytes, the dir entry contains an offset.
    541             if (OffsetVal+ByteCount > ExifLength){
    542                 // Bogus pointer offset and / or bytecount value
    543                 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
    544                 continue;
    545             }
    546             ValuePtr = OffsetBase+OffsetVal;
    547 
    548             if (OffsetVal > ImageInfo.LargestExifOffset){
    549                 ImageInfo.LargestExifOffset = OffsetVal;
    550             }
    551 
    552             if (DumpExifMap){
    553                 printf("Map: %05d-%05d:   Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag);
    554             }
    555         }else{
    556             // 4 bytes or less and value is in the dir entry itself
    557             ValuePtr = DirEntry+8;
    558         }
    559 
    560         if (Tag == TAG_MAKER_NOTE){
    561             if (ShowTags){
    562                 printf("%s    Maker note: ",IndentString);
    563             }
    564             ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
    565             continue;
    566         }
    567 
    568         if (ShowTags){
    569             // Show tag name
    570             for (a=0;;a++){
    571                 if (a >= (int)TAG_TABLE_SIZE){
    572                     printf("%s", IndentString);
    573                     printf("    Unknown Tag %04x Value = ", Tag);
    574                     break;
    575                 }
    576                 if (TagTable[a].Tag == Tag){
    577                     printf("%s", IndentString);
    578                     printf("    %s = ",TagTable[a].Desc);
    579                     break;
    580                 }
    581             }
    582 
    583             // Show tag value.
    584             switch(Format){
    585                 case FMT_BYTE:
    586                     if(ByteCount>1){
    587                         printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr);
    588                     }else{
    589                         PrintFormatNumber(ValuePtr, Format, ByteCount);
    590                         printf("\n");
    591                     }
    592                     break;
    593 
    594                 case FMT_UNDEFINED:
    595                     // Undefined is typically an ascii string.
    596 
    597                 case FMT_STRING:
    598                     // String arrays printed without function call (different from int arrays)
    599                     {
    600                           printf("\"%s\"", ValuePtr);
    601 //                        int NoPrint = 0;
    602 //                        printf("\"");
    603 //                        for (a=0;a<ByteCount;a++){
    604 //                            if (ValuePtr[a] >= 32){
    605 //                                putchar(ValuePtr[a]);
    606 //                                NoPrint = 0;
    607 //                            }else{
    608 //                                // Avoiding indicating too many unprintable characters of proprietary
    609 //                                // bits of binary information this program may not know how to parse.
    610 //                                if (!NoPrint && a != ByteCount-1){
    611 //                                    putchar('?');
    612 //                                    NoPrint = 1;
    613 //                                }
    614 //                            }
    615 //                        }
    616 //                        printf("\"\n");
    617                     }
    618                     break;
    619 
    620                 default:
    621                     // Handle arrays of numbers later (will there ever be?)
    622                     PrintFormatNumber(ValuePtr, Format, ByteCount);
    623                     printf("\n");
    624             }
    625         }
    626 
    627         // Extract useful components of tag
    628         switch(Tag){
    629 
    630             case TAG_MAKE:
    631                 strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31);
    632                 break;
    633 
    634             case TAG_MODEL:
    635                 strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39);
    636                 break;
    637 
    638             case TAG_DATETIME_ORIGINAL:
    639                 // If we get a DATETIME_ORIGINAL, we use that one.
    640                 strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
    641                 // Fallthru...
    642 
    643             case TAG_DATETIME_DIGITIZED:
    644             case TAG_DATETIME:
    645                 if (!isdigit(ImageInfo.DateTime[0])){
    646                     // If we don't already have a DATETIME_ORIGINAL, use whatever
    647                     // time fields we may have.
    648                     strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
    649                 }
    650 
    651                 if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
    652                     ErrNonfatal("More than %d date fields!  This is nuts", MAX_DATE_COPIES, 0);
    653                     break;
    654                 }
    655                 ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
    656                     (char *)ValuePtr - (char *)OffsetBase;
    657                 break;
    658 
    659             case TAG_WINXP_COMMENT:
    660                 if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
    661                     // Already have a comment (probably windows comment), skip this one.
    662                     if (ShowTags) printf("Windows XP commend and other comment in header\n");
    663                     break; // Already have a windows comment, skip this one.
    664                 }
    665 
    666                 if (ByteCount > 1){
    667                     if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
    668                     memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
    669                     ImageInfo.CommentWidchars = ByteCount/2;
    670                 }
    671                 break;
    672 
    673             case TAG_USERCOMMENT:
    674                 if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
    675                     // Already have a comment (probably windows comment), skip this one.
    676                     if (ShowTags) printf("Multiple comments in exif header\n");
    677                     break; // Already have a windows comment, skip this one.
    678                 }
    679 
    680                 // Comment is often padded with trailing spaces.  Remove these first.
    681                 for (a=ByteCount;;){
    682                     a--;
    683                     if ((ValuePtr)[a] == ' '){
    684                         (ValuePtr)[a] = '\0';
    685                     }else{
    686                         break;
    687                     }
    688                     if (a == 0) break;
    689                 }
    690 
    691                 // Copy the comment
    692                 if (memcmp(ValuePtr, "ASCII",5) == 0){
    693                     for (a=5;a<10;a++){
    694                         int c;
    695                         c = (ValuePtr)[a];
    696                         if (c != '\0' && c != ' '){
    697                             strncpy(ImageInfo.Comments, (char *)ValuePtr+a, 199);
    698                             break;
    699                         }
    700                     }
    701                 }else{
    702                     strncpy(ImageInfo.Comments, (char *)ValuePtr, MAX_COMMENT_SIZE-1);
    703                 }
    704                 break;
    705 
    706             case TAG_FNUMBER:
    707                 // Simplest way of expressing aperture, so I trust it the most.
    708                 // (overwrite previously computd value if there is one)
    709                 ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
    710                 break;
    711 
    712             case TAG_APERTURE:
    713             case TAG_MAXAPERTURE:
    714                 // More relevant info always comes earlier, so only use this field if we don't
    715                 // have appropriate aperture information yet.
    716                 if (ImageInfo.ApertureFNumber == 0){
    717                     ImageInfo.ApertureFNumber
    718                         = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
    719                 }
    720                 break;
    721 
    722             case TAG_FOCALLENGTH:
    723                 // Nice digital cameras actually save the focal length as a function
    724                 // of how farthey are zoomed in.
    725                 ImageInfo.FocalLength.num = Get32u(ValuePtr);
    726                 ImageInfo.FocalLength.denom = Get32u(4+(char *)ValuePtr);
    727                 break;
    728 
    729             case TAG_SUBJECT_DISTANCE:
    730                 // Inidcates the distacne the autofocus camera is focused to.
    731                 // Tends to be less accurate as distance increases.
    732                 ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
    733                 break;
    734 
    735             case TAG_EXPOSURETIME:
    736                 // Simplest way of expressing exposure time, so I trust it most.
    737                 // (overwrite previously computd value if there is one)
    738                 ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
    739                 break;
    740 
    741             case TAG_SHUTTERSPEED:
    742                 // More complicated way of expressing exposure time, so only use
    743                 // this value if we don't already have it from somewhere else.
    744                 if (ImageInfo.ExposureTime == 0){
    745                     ImageInfo.ExposureTime
    746                         = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
    747                 }
    748                 break;
    749 
    750 
    751             case TAG_FLASH:
    752                 ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format);
    753                 break;
    754 
    755             case TAG_ORIENTATION:
    756                 if (NumOrientations >= 2){
    757                     // Can have another orientation tag for the thumbnail, but if there's
    758                     // a third one, things are stringae.
    759                     ErrNonfatal("More than two orientation tags!",0,0);
    760                     break;
    761                 }
    762                 OrientationPtr[NumOrientations] = ValuePtr;
    763                 OrientationNumFormat[NumOrientations] = Format;
    764                 if (NumOrientations == 0){
    765                     ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
    766                 }
    767                 if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){
    768                     ErrNonfatal("Undefined rotation value %d", ImageInfo.Orientation, 0);
    769                     ImageInfo.Orientation = 0;
    770                 }
    771                 NumOrientations += 1;
    772                 break;
    773 
    774             case TAG_EXIF_IMAGELENGTH:
    775             case TAG_EXIF_IMAGEWIDTH:
    776                 // Use largest of height and width to deal with images that have been
    777                 // rotated to portrait format.
    778                 a = (int)ConvertAnyFormat(ValuePtr, Format);
    779                 if (ExifImageWidth < a) ExifImageWidth = a;
    780                 break;
    781 
    782             case TAG_FOCAL_PLANE_XRES:
    783                 FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
    784                 break;
    785 
    786             case TAG_FOCAL_PLANE_UNITS:
    787                 switch((int)ConvertAnyFormat(ValuePtr, Format)){
    788                     case 1: FocalplaneUnits = 25.4; break; // inch
    789                     case 2:
    790                         // According to the information I was using, 2 means meters.
    791                         // But looking at the Cannon powershot's files, inches is the only
    792                         // sensible value.
    793                         FocalplaneUnits = 25.4;
    794                         break;
    795 
    796                     case 3: FocalplaneUnits = 10;   break;  // centimeter
    797                     case 4: FocalplaneUnits = 1;    break;  // millimeter
    798                     case 5: FocalplaneUnits = .001; break;  // micrometer
    799                 }
    800                 break;
    801 
    802             case TAG_EXPOSURE_BIAS:
    803                 ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
    804                 break;
    805 
    806             case TAG_WHITEBALANCE:
    807                 ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
    808                 break;
    809 
    810             case TAG_LIGHT_SOURCE:
    811                 ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
    812                 break;
    813 
    814             case TAG_METERING_MODE:
    815                 ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
    816                 break;
    817 
    818             case TAG_EXPOSURE_PROGRAM:
    819                 ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
    820                 break;
    821 
    822             case TAG_EXPOSURE_INDEX:
    823                 if (ImageInfo.ISOequivalent == 0){
    824                     // Exposure index and ISO equivalent are often used interchangeably,
    825                     // so we will do the same in jhead.
    826                     // http://photography.about.com/library/glossary/bldef_ei.htm
    827                     ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
    828                 }
    829                 break;
    830 
    831             case TAG_EXPOSURE_MODE:
    832                 ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
    833                 break;
    834 
    835             case TAG_ISO_EQUIVALENT:
    836                 ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
    837                 if ( ImageInfo.ISOequivalent < 50 ){
    838                     // Fixes strange encoding on some older digicams.
    839                     ImageInfo.ISOequivalent *= 200;
    840                 }
    841                 break;
    842 
    843             case TAG_DIGITALZOOMRATIO:
    844                 ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
    845                 break;
    846 
    847             case TAG_THUMBNAIL_OFFSET:
    848                 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
    849                 DirWithThumbnailPtrs = DirStart;
    850                 break;
    851 
    852             case TAG_THUMBNAIL_LENGTH:
    853                 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
    854                 ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase;
    855                 break;
    856 
    857             case TAG_EXIF_OFFSET:
    858                 if (ShowTags) printf("%s    Exif Dir:",IndentString);
    859 
    860             case TAG_INTEROP_OFFSET:
    861                 if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s    Interop Dir:",IndentString);
    862                 {
    863                     unsigned char * SubdirStart;
    864                     SubdirStart = OffsetBase + Get32u(ValuePtr);
    865                     if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
    866                         ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
    867                     }else{
    868                         ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
    869                     }
    870                     continue;
    871                 }
    872                 break;
    873 
    874             case TAG_GPSINFO:
    875                 if (ShowTags) printf("%s    GPS info dir:",IndentString);
    876                 {
    877                     unsigned char * SubdirStart;
    878                     SubdirStart = OffsetBase + Get32u(ValuePtr);
    879                     if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
    880                         ErrNonfatal("Illegal GPS directory link",0,0);
    881                     }else{
    882                         ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
    883                     }
    884                     continue;
    885                 }
    886                 break;
    887 
    888             case TAG_FOCALLENGTH_35MM:
    889                 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
    890                 // if its present, use it to compute equivalent focal length instead of
    891                 // computing it from sensor geometry and actual focal length.
    892                 ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
    893                 break;
    894 
    895             case TAG_DISTANCE_RANGE:
    896                 // Three possible standard values:
    897                 //   1 = macro, 2 = close, 3 = distant
    898                 ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
    899                 break;
    900         }
    901     }
    902 
    903 
    904     {
    905         // In addition to linking to subdirectories via exif tags,
    906         // there's also a potential link to another directory at the end of each
    907         // directory.  this has got to be the result of a committee!
    908         unsigned char * SubdirStart;
    909         unsigned Offset;
    910 
    911         if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
    912             printf("DirStart %d offset from dirstart %d", (int)DirStart, 2+12*NumDirEntries);
    913             Offset = Get32u(DirStart+2+12*NumDirEntries);
    914             if (Offset){
    915                 SubdirStart = OffsetBase + Offset;
    916                 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){
    917                     printf("SubdirStart %d OffsetBase %d ExifLength %d Offset %d",
    918                         (int)SubdirStart, (int)OffsetBase, ExifLength, Offset);
    919                     if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){
    920                         // Jhead 1.3 or earlier would crop the whole directory!
    921                         // As Jhead produces this form of format incorrectness,
    922                         // I'll just let it pass silently
    923                         if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n");
    924                     }else{
    925                         ErrNonfatal("Illegal subdirectory link",0,0);
    926                     }
    927                 }else{
    928                     if (SubdirStart <= OffsetBase+ExifLength){
    929                         if (ShowTags) printf("%s    Continued directory ",IndentString);
    930                         ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
    931                     }
    932                 }
    933                 if (Offset > ImageInfo.LargestExifOffset){
    934                     ImageInfo.LargestExifOffset = Offset;
    935                 }
    936             }
    937         }else{
    938             // The exif header ends before the last next directory pointer.
    939         }
    940     }
    941 
    942     if (ThumbnailOffset){
    943         ImageInfo.ThumbnailAtEnd = FALSE;
    944 
    945         if (DumpExifMap){
    946             printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize);
    947         }
    948 
    949         if (ThumbnailOffset <= ExifLength){
    950             if (ThumbnailSize > ExifLength-ThumbnailOffset){
    951                 // If thumbnail extends past exif header, only save the part that
    952                 // actually exists.  Canon's EOS viewer utility will do this - the
    953                 // thumbnail extracts ok with this hack.
    954                 ThumbnailSize = ExifLength-ThumbnailOffset;
    955                 if (ShowTags) printf("Thumbnail incorrectly placed in header\n");
    956 
    957             }
    958             // The thumbnail pointer appears to be valid.  Store it.
    959             ImageInfo.ThumbnailOffset = ThumbnailOffset;
    960             ImageInfo.ThumbnailSize = ThumbnailSize;
    961 
    962             if (ShowTags){
    963                 printf("Thumbnail size: %d bytes\n",ThumbnailSize);
    964             }
    965         }
    966     }
    967     printf("returning from ProcessExifDir");
    968 }
    969 
    970 
    971 //--------------------------------------------------------------------------
    972 // Process a EXIF marker
    973 // Describes all the drivel that most digital cameras include...
    974 //--------------------------------------------------------------------------
    975 void process_EXIF (unsigned char * ExifSection, unsigned int length)
    976 {
    977     int FirstOffset;
    978 
    979     FocalplaneXRes = 0;
    980     FocalplaneUnits = 0;
    981     ExifImageWidth = 0;
    982     NumOrientations = 0;
    983 
    984     if (ShowTags){
    985         printf("Exif header %d bytes long\n",length);
    986     }
    987 
    988     {   // Check the EXIF header component
    989         static uchar ExifHeader[] = "Exif\0\0";
    990         if (memcmp(ExifSection+2, ExifHeader,6)){
    991             ErrNonfatal("Incorrect Exif header",0,0);
    992             return;
    993         }
    994     }
    995 
    996     if (memcmp(ExifSection+8,"II",2) == 0){
    997         if (ShowTags) printf("Exif section in Intel order\n");
    998         MotorolaOrder = 0;
    999     }else{
   1000         if (memcmp(ExifSection+8,"MM",2) == 0){
   1001             if (ShowTags) printf("Exif section in Motorola order\n");
   1002             MotorolaOrder = 1;
   1003         }else{
   1004             ErrNonfatal("Invalid Exif alignment marker.",0,0);
   1005             return;
   1006         }
   1007     }
   1008 
   1009     // Check the next value for correctness.
   1010     if (Get16u(ExifSection+10) != 0x2a){
   1011         ErrNonfatal("Invalid Exif start (1)",0,0);
   1012         return;
   1013     }
   1014 
   1015     FirstOffset = Get32u(ExifSection+12);
   1016     if (FirstOffset < 8 || FirstOffset > 16){
   1017         // Usually set to 8, but other values valid too.
   1018         ErrNonfatal("Suspicious offset of first IFD value",0,0);
   1019         return;
   1020     }
   1021 
   1022     DirWithThumbnailPtrs = NULL;
   1023 
   1024 
   1025     // First directory starts 16 bytes in.  All offset are relative to 8 bytes in.
   1026     ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
   1027 
   1028     ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
   1029 #ifdef SUPERDEBUG
   1030     printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at"));
   1031 #endif
   1032     if (DumpExifMap){
   1033         unsigned a,b;
   1034         printf("Map: %05d- End of exif\n",length-8);
   1035 //        for (a=0;a<length-8;a+= 10){
   1036 //            printf("Map: %05d ",a);
   1037 //            for (b=0;b<10;b++) printf(" %02x",*(ExifSection+8+a+b));
   1038 //            printf("\n");
   1039 //        }
   1040         for (a = 0; a < length - 8; ++a) {
   1041             unsigned char c = *(ExifSection+8+a);
   1042             unsigned pc = isprint(c) ? c : ' ';
   1043             printf("Map: %4d %02x %c", a, c, pc);
   1044         }
   1045     }
   1046 
   1047 
   1048     // Compute the CCD width, in millimeters.
   1049     if (FocalplaneXRes != 0){
   1050         // Note: With some cameras, its not possible to compute this correctly because
   1051         // they don't adjust the indicated focal plane resolution units when using less
   1052         // than maximum resolution, so the CCDWidth value comes out too small.  Nothing
   1053         // that Jhad can do about it - its a camera problem.
   1054         ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
   1055 
   1056         if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0
   1057             && ImageInfo.FocalLength35mmEquiv == 0){
   1058             // Compute 35 mm equivalent focal length based on sensor geometry if we haven't
   1059             // already got it explicitly from a tag.
   1060             ImageInfo.FocalLength35mmEquiv = (int)(
   1061                 (double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom
   1062                 / ImageInfo.CCDWidth * 36 + 0.5);
   1063         }
   1064     }
   1065 }
   1066 
   1067 static const TagTable_t* TagToTagTableEntry(unsigned short tag)
   1068 {
   1069     unsigned int i;
   1070     for (i = 0; i < TAG_TABLE_SIZE; i++) {
   1071         if (TagTable[i].Tag == tag) {
   1072             printf("found tag %d", tag);
   1073             int format = TagTable[i].Format;
   1074             if (format == 0) {
   1075                 printf("tag %s format not defined ***** YOU MUST ADD THE FORMAT TO THE TagTable in exif.c!!!!", TagTable[i].Desc);
   1076                 return NULL;
   1077             }
   1078             return &TagTable[i];
   1079         }
   1080     }
   1081     printf("tag %d NOT FOUND", tag);
   1082     return NULL;
   1083 }
   1084 
   1085 static void writeExifTagAndData(int tag,
   1086                                 int format,
   1087                                 long components,
   1088                                 long value,
   1089                                 int valueInString,
   1090                                 char* Buffer,
   1091                                 int* DirIndex,
   1092                                 int* DataWriteIndex) {
   1093     void* componentsPosition = NULL; // for saving component position
   1094 
   1095     Put16u(Buffer+ (*DirIndex), tag);                    // Tag
   1096     Put16u(Buffer+(*DirIndex) + 2, format);              // Format
   1097     if (format == FMT_STRING && components == -1) {
   1098         components = strlen((char*)value) + 1;                 // account for null terminator
   1099         if (components & 1) ++components;               // no odd lengths
   1100     }
   1101     if (format == FMT_UNDEFINED && components == -1) {
   1102         // check if this UNDEFINED format is actually ASCII (as it usually is)
   1103         // if so, we can calculate the size
   1104         if(memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0) {
   1105             components = sizeof(ExifAsciiPrefix) +
   1106                          strlen((char*)value + sizeof(ExifAsciiPrefix)) + 1;
   1107             if (components & 1) ++components;               // no odd lengths
   1108         }
   1109     }
   1110     Put32u(Buffer+(*DirIndex) + 4, components);         // Components
   1111     componentsPosition = Buffer+(*DirIndex) + 4; // components # can change for lists
   1112     printf("# components: %ld", components);
   1113     if (format == FMT_STRING) {
   1114         // short strings can fit right in the long, otherwise have to
   1115         // go in the data area
   1116         if (components <= 4) {
   1117             strcpy(Buffer+(*DirIndex) + 8, (char*)value);
   1118         } else {
   1119             Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
   1120             printf("copying value %s to %d", (char*)value, (*DataWriteIndex));
   1121             strncpy(Buffer+(*DataWriteIndex), (char*)value, components);
   1122             (*DataWriteIndex) += components;
   1123         }
   1124     } else if ((format == FMT_UNDEFINED) &&
   1125                (memcmp((char*)value, ExifAsciiPrefix, sizeof(ExifAsciiPrefix)) == 0)) {
   1126         // short strings can fit right in the long, otherwise have to
   1127         // go in the data area
   1128         if (components <= 4) {
   1129             memcpy(Buffer+(*DirIndex) + 8, (char*)value, components);
   1130         } else {
   1131             Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
   1132             printf("copying %s to %d", (char*)value + sizeof(ExifAsciiPrefix), (*DataWriteIndex));
   1133             memcpy(Buffer+(*DataWriteIndex), (char*)value, components);
   1134             (*DataWriteIndex) += components;
   1135         }
   1136     } else if (!valueInString) {
   1137         Put32u(Buffer+(*DirIndex) + 8, value);   // Value
   1138     } else {
   1139         Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8);   // Pointer
   1140         char* curElement = strtok((char*)value, ",");
   1141         int i;
   1142 
   1143         // (components == -1) Need to handle lists with unknown length too
   1144         for (i = 0; ((i < components) || (components == -1)) && curElement != NULL; i++) {
   1145 #ifdef SUPERDEBUG
   1146             printf("processing component %s format %s", curElement, formatStr(format));
   1147 #endif
   1148             // elements are separated by commas
   1149             if (format == FMT_URATIONAL) {
   1150                 char* separator = strchr(curElement, '/');
   1151                 if (separator) {
   1152                     unsigned int numerator = atoi(curElement);
   1153                     unsigned int denominator = atoi(separator + 1);
   1154                     Put32u(Buffer+(*DataWriteIndex), numerator);
   1155                     Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
   1156                     (*DataWriteIndex) += 8;
   1157                 }
   1158             } else if (format == FMT_SRATIONAL) {
   1159                 char* separator = strchr(curElement, '/');
   1160                 if (separator) {
   1161                     int numerator = atoi(curElement);
   1162                     int denominator = atoi(separator + 1);
   1163                     Put32u(Buffer+(*DataWriteIndex), numerator);
   1164                     Put32u(Buffer+(*DataWriteIndex) + 4, denominator);
   1165                     (*DataWriteIndex) += 8;
   1166                 }
   1167             } else if ((components == -1) && ((format == FMT_USHORT) || (format == FMT_SSHORT))) {
   1168                 // variable components need to go into data write area
   1169                 value = atoi(curElement);
   1170                 Put16u(Buffer+(*DataWriteIndex), value);
   1171                 (*DataWriteIndex) += 4;
   1172             } else {
   1173                 // TODO: doesn't handle multiple components yet -- if more than one, have to put in data write area.
   1174                 value = atoi(curElement);
   1175                 Put32u(Buffer+(*DirIndex) + 8, value);   // Value
   1176             }
   1177             curElement = strtok(NULL, ",");
   1178         }
   1179         if (components == -1) Put32u(componentsPosition, i); // update component # for unknowns
   1180     }
   1181     (*DirIndex) += 12;
   1182 }
   1183 
   1184 #ifdef SUPERDEBUG
   1185 char* formatStr(int format) {
   1186     switch (format) {
   1187         case FMT_BYTE: return "FMT_BYTE"; break;
   1188         case FMT_STRING: return "FMT_STRING"; break;
   1189         case FMT_USHORT: return "FMT_USHORT"; break;
   1190         case FMT_ULONG: return "FMT_ULONG"; break;
   1191         case FMT_URATIONAL: return "FMT_URATIONAL"; break;
   1192         case FMT_SBYTE: return "FMT_SBYTE"; break;
   1193         case FMT_UNDEFINED: return "FMT_UNDEFINED"; break;
   1194         case FMT_SSHORT: return "FMT_SSHORT"; break;
   1195         case FMT_SLONG: return "FMT_SLONG"; break;
   1196         case FMT_SRATIONAL: return "FMT_SRATIONAL"; break;
   1197         case FMT_SINGLE: return "FMT_SINGLE"; break;
   1198         case FMT_DOUBLE: return "FMT_SINGLE"; break;
   1199         default: return "UNKNOWN";
   1200     }
   1201 }
   1202 #endif
   1203 
   1204 //--------------------------------------------------------------------------
   1205 // Create minimal exif header - just date and thumbnail pointers,
   1206 // so that date and thumbnail may be filled later.
   1207 //--------------------------------------------------------------------------
   1208 static void create_EXIF_internal(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag, char* Buffer)
   1209 {
   1210     unsigned short NumEntries;
   1211     int DataWriteIndex;
   1212     int DirIndex;
   1213     int DirExifLink = 0;
   1214 
   1215 #ifdef SUPERDEBUG
   1216     ALOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
   1217 #endif
   1218 
   1219     MotorolaOrder = 0;
   1220 
   1221     memcpy(Buffer+2, "Exif\0\0II",8);
   1222     Put16u(Buffer+10, 0x2a);
   1223 
   1224     DataWriteIndex = 16;
   1225     Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset.  Means start 16 bytes in.
   1226 
   1227     {
   1228         DirIndex = DataWriteIndex;
   1229         NumEntries = 1 + exifTagCount;  // the extra is the thumbnail
   1230         if (gpsTagCount) {
   1231             ++NumEntries;       // allow for the GPS info tag
   1232         }
   1233         if (!hasDateTimeTag) {
   1234             // We have to write extra date time tag. The entry number should be
   1235             // adjusted.
   1236             ++NumEntries;
   1237         }
   1238         DataWriteIndex += 2 + NumEntries*12 + 4;
   1239 
   1240         Put16u(Buffer+DirIndex, NumEntries); // Number of entries
   1241         DirIndex += 2;
   1242 
   1243         // Entries go here...
   1244         if (!hasDateTimeTag) {
   1245             // Date/time entry
   1246             char* dateTime = NULL;
   1247             char dateBuf[20];
   1248             if (ImageInfo.numDateTimeTags) {
   1249                 // If we had a pre-existing exif header, use time from that.
   1250                 dateTime = ImageInfo.DateTime;
   1251             } else {
   1252                 // Oterwise, use the file's timestamp.
   1253                 FileTimeAsString(dateBuf);
   1254                 dateTime = dateBuf;
   1255             }
   1256             writeExifTagAndData(TAG_DATETIME,
   1257                                 FMT_STRING,
   1258                                 20,
   1259                                 (long)(char*)dateBuf,
   1260                                 FALSE,
   1261                                 Buffer,
   1262                                 &DirIndex,
   1263                                 &DataWriteIndex);
   1264 
   1265         }
   1266         if (exifTagCount > 0) {
   1267             int i;
   1268             for (i = 0; i < exifTagCount + gpsTagCount; i++) {
   1269                 if (elements[i].GpsTag) {
   1270                     continue;
   1271                 }
   1272                 const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag);
   1273                 if (entry == NULL) {
   1274                     continue;
   1275                 }
   1276 #ifdef SUPERDEBUG
   1277                 ALOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
   1278 #endif
   1279                 writeExifTagAndData(elements[i].Tag,
   1280                                     entry->Format,
   1281                                     entry->DataLength,
   1282                                     (long)elements[i].Value,
   1283                                     TRUE,
   1284                                     Buffer,
   1285                                     &DirIndex,
   1286                                     &DataWriteIndex);
   1287             }
   1288 
   1289             if (gpsTagCount) {
   1290                 // Link to gps dir entry
   1291                 writeExifTagAndData(TAG_GPSINFO,
   1292                                     FMT_ULONG,
   1293                                     1,
   1294                                     DataWriteIndex-8,
   1295                                     FALSE,
   1296                                     Buffer,
   1297                                     &DirIndex,
   1298                                     &DataWriteIndex);
   1299             }
   1300 
   1301             // Link to exif dir entry
   1302             int exifDirPtr = DataWriteIndex-8;
   1303             if (gpsTagCount) {
   1304                 exifDirPtr += 2 + gpsTagCount*12 + 4;
   1305             }
   1306             DirExifLink = DirIndex;
   1307             writeExifTagAndData(TAG_EXIF_OFFSET,
   1308                                 FMT_ULONG,
   1309                                 1,
   1310                                 exifDirPtr,
   1311                                 FALSE,
   1312                                 Buffer,
   1313                                 &DirIndex,
   1314                                 &DataWriteIndex);
   1315         }
   1316 
   1317         // End of directory - contains optional link to continued directory.
   1318         Put32u(Buffer+DirIndex, 0);
   1319         printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
   1320     }
   1321 
   1322 
   1323     // GPS Section
   1324     if (gpsTagCount) {
   1325         DirIndex = DataWriteIndex;
   1326         printf("Starting GPS section DirIndex = %d", DirIndex);
   1327         NumEntries = gpsTagCount;
   1328         DataWriteIndex += 2 + NumEntries*12 + 4;
   1329 
   1330         Put16u(Buffer+DirIndex, NumEntries); // Number of entries
   1331         DirIndex += 2;
   1332         {
   1333             int i;
   1334             for (i = 0; i < exifTagCount + gpsTagCount; i++) {
   1335                 if (!elements[i].GpsTag) {
   1336                     continue;
   1337                 }
   1338                 const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag);
   1339                 if (entry == NULL) {
   1340                     continue;
   1341                 }
   1342 #ifdef SUPERDEBUG
   1343                 ALOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value);
   1344 #endif
   1345                 writeExifTagAndData(elements[i].Tag,
   1346                                     entry->Format,
   1347                                     entry->DataLength,
   1348                                     (long)elements[i].Value,
   1349                                     TRUE,
   1350                                     Buffer,
   1351                                     &DirIndex,
   1352                                     &DataWriteIndex);
   1353             }
   1354         }
   1355 
   1356         // End of directory - contains optional link to continued directory.
   1357         Put32u(Buffer+DirIndex, 0);
   1358         printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
   1359     }
   1360 
   1361     {
   1362         // Overwriting TAG_EXIF_OFFSET which links to this directory
   1363         Put32u(Buffer+DirExifLink+8, DataWriteIndex-8);
   1364 
   1365         printf("Starting Thumbnail section DirIndex = %d", DirIndex);
   1366         DirIndex = DataWriteIndex;
   1367         NumEntries = 2;
   1368         DataWriteIndex += 2 + NumEntries*12 + 4;
   1369 
   1370         Put16u(Buffer+DirIndex, NumEntries); // Number of entries
   1371         DirIndex += 2;
   1372         {
   1373             // Link to exif dir entry
   1374             writeExifTagAndData(TAG_THUMBNAIL_OFFSET,
   1375                                 FMT_ULONG,
   1376                                 1,
   1377                                 DataWriteIndex-8,
   1378                                 FALSE,
   1379                                 Buffer,
   1380                                 &DirIndex,
   1381                                 &DataWriteIndex);
   1382         }
   1383 
   1384         {
   1385             // Link to exif dir entry
   1386             writeExifTagAndData(TAG_THUMBNAIL_LENGTH,
   1387                                 FMT_ULONG,
   1388                                 1,
   1389                                 0,
   1390                                 FALSE,
   1391                                 Buffer,
   1392                                 &DirIndex,
   1393                                 &DataWriteIndex);
   1394         }
   1395 
   1396         // End of directory - contains optional link to continued directory.
   1397         Put32u(Buffer+DirIndex, 0);
   1398         printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
   1399     }
   1400 
   1401 
   1402     Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
   1403     Buffer[1] = (unsigned char)DataWriteIndex;
   1404 
   1405     // Remove old exif section, if there was one.
   1406     RemoveSectionType(M_EXIF);
   1407 
   1408     {
   1409         // Sections need malloced buffers, so do that now, especially because
   1410         // we now know how big it needs to be allocated.
   1411         unsigned char * NewBuf = malloc(DataWriteIndex);
   1412         if (NewBuf == NULL){
   1413             ErrFatal("Could not allocate memory");
   1414         }
   1415         memcpy(NewBuf, Buffer, DataWriteIndex);
   1416 
   1417         CreateSection(M_EXIF, NewBuf, DataWriteIndex);
   1418 
   1419         // Re-parse new exif section, now that its in place
   1420         // otherwise, we risk touching data that has already been freed.
   1421         process_EXIF(NewBuf, DataWriteIndex);
   1422     }
   1423 }
   1424 
   1425 void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount, int hasDateTimeTag)
   1426 {
   1427     // It is hard to calculate exact necessary size for editing the exif
   1428     // header dynamically, so we are using the maximum size of EXIF, 64K
   1429     const int EXIF_MAX_SIZE = 1024*64;
   1430     char* Buffer = malloc(EXIF_MAX_SIZE);
   1431 
   1432     if (Buffer != NULL) {
   1433         create_EXIF_internal(elements, exifTagCount, gpsTagCount, hasDateTimeTag, Buffer);
   1434         free(Buffer);
   1435     } else {
   1436         ErrFatal("Could not allocate memory");
   1437     }
   1438 }
   1439 
   1440 //--------------------------------------------------------------------------
   1441 // Cler the rotation tag in the exif header to 1.
   1442 //--------------------------------------------------------------------------
   1443 const char * ClearOrientation(void)
   1444 {
   1445     int a;
   1446     if (NumOrientations == 0) return NULL;
   1447 
   1448     for (a=0;a<NumOrientations;a++){
   1449         switch(OrientationNumFormat[a]){
   1450             case FMT_SBYTE:
   1451             case FMT_BYTE:
   1452                 *(uchar *)(OrientationPtr[a]) = 1;
   1453                 break;
   1454 
   1455             case FMT_USHORT:
   1456                 Put16u(OrientationPtr[a], 1);
   1457                 break;
   1458 
   1459             case FMT_ULONG:
   1460             case FMT_SLONG:
   1461                 memset(OrientationPtr, 0, 4);
   1462                 // Can't be bothered to write  generic Put32 if I only use it once.
   1463                 if (MotorolaOrder){
   1464                     ((uchar *)OrientationPtr[a])[3] = 1;
   1465                 }else{
   1466                     ((uchar *)OrientationPtr[a])[0] = 1;
   1467                 }
   1468                 break;
   1469 
   1470             default:
   1471                 return NULL;
   1472         }
   1473     }
   1474 
   1475     return OrientTab[ImageInfo.Orientation];
   1476 }
   1477 
   1478 
   1479 
   1480 //--------------------------------------------------------------------------
   1481 // Remove thumbnail out of the exif image.
   1482 //--------------------------------------------------------------------------
   1483 int RemoveThumbnail(unsigned char * ExifSection)
   1484 {
   1485     if (!DirWithThumbnailPtrs ||
   1486         ImageInfo.ThumbnailOffset == 0 ||
   1487         ImageInfo.ThumbnailSize == 0){
   1488         // No thumbnail, or already deleted it.
   1489         return 0;
   1490     }
   1491     if (ImageInfo.ThumbnailAtEnd == FALSE){
   1492         ErrNonfatal("Thumbnail is not at end of header, can't chop it off", 0, 0);
   1493         return 0;
   1494     }
   1495 
   1496     {
   1497         int de;
   1498         int NumDirEntries;
   1499         NumDirEntries = Get16u(DirWithThumbnailPtrs);
   1500 
   1501         for (de=0;de<NumDirEntries;de++){
   1502             int Tag;
   1503             unsigned char * DirEntry;
   1504             DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de);
   1505             Tag = Get16u(DirEntry);
   1506             if (Tag == TAG_THUMBNAIL_LENGTH){
   1507                 // Set length to zero.
   1508                 if (Get16u(DirEntry+2) != FMT_ULONG){
   1509                     // non standard format encoding.  Can't do it.
   1510                     ErrNonfatal("Can't remove thumbnail", 0, 0);
   1511                     return 0;
   1512                 }
   1513                 Put32u(DirEntry+8, 0);
   1514             }
   1515         }
   1516     }
   1517 
   1518     // This is how far the non thumbnail data went.
   1519     return ImageInfo.ThumbnailOffset+8;
   1520 
   1521 }
   1522 
   1523 
   1524 //--------------------------------------------------------------------------
   1525 // Convert exif time to Unix time structure
   1526 //--------------------------------------------------------------------------
   1527 int Exif2tm(struct tm * timeptr, char * ExifTime)
   1528 {
   1529     int a;
   1530 
   1531     timeptr->tm_wday = -1;
   1532 
   1533     // Check for format: YYYY:MM:DD HH:MM:SS format.
   1534     // Date and time normally separated by a space, but also seen a ':' there, so
   1535     // skip the middle space with '%*c' so it can be any character.
   1536     a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d",
   1537             &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
   1538             &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
   1539 
   1540 
   1541     if (a == 6){
   1542         timeptr->tm_isdst = -1;
   1543         timeptr->tm_mon -= 1;      // Adjust for unix zero-based months
   1544         timeptr->tm_year -= 1900;  // Adjust for year starting at 1900
   1545         return TRUE; // worked.
   1546     }
   1547 
   1548     return FALSE; // Wasn't in Exif date format.
   1549 }
   1550 
   1551 
   1552 //--------------------------------------------------------------------------
   1553 // Show the collected image info, displaying camera F-stop and shutter speed
   1554 // in a consistent and legible fashion.
   1555 //--------------------------------------------------------------------------
   1556 void ShowImageInfo(int ShowFileInfo)
   1557 {
   1558     if (ShowFileInfo){
   1559         printf("File name    : %s\n",ImageInfo.FileName);
   1560         printf("File size    : %d bytes\n",ImageInfo.FileSize);
   1561 
   1562         {
   1563             char Temp[20];
   1564             FileTimeAsString(Temp);
   1565             printf("File date    : %s\n",Temp);
   1566         }
   1567     }
   1568 
   1569     if (ImageInfo.CameraMake[0]){
   1570         printf("Camera make  : %s\n",ImageInfo.CameraMake);
   1571         printf("Camera model : %s\n",ImageInfo.CameraModel);
   1572     }
   1573     if (ImageInfo.DateTime[0]){
   1574         printf("Date/Time    : %s\n",ImageInfo.DateTime);
   1575     }
   1576     printf("Resolution   : %d x %d\n",ImageInfo.Width, ImageInfo.Height);
   1577 
   1578     if (ImageInfo.Orientation > 1){
   1579         // Only print orientation if one was supplied, and if its not 1 (normal orientation)
   1580         printf("Orientation  : %s\n", OrientTab[ImageInfo.Orientation]);
   1581     }
   1582 
   1583     if (ImageInfo.IsColor == 0){
   1584         printf("Color/bw     : Black and white\n");
   1585     }
   1586 
   1587     if (ImageInfo.FlashUsed >= 0){
   1588         if (ImageInfo.FlashUsed & 1){
   1589             printf("Flash used   : Yes");
   1590             switch (ImageInfo.FlashUsed){
   1591 	            case 0x5: printf(" (Strobe light not detected)"); break;
   1592 	            case 0x7: printf(" (Strobe light detected) "); break;
   1593 	            case 0x9: printf(" (manual)"); break;
   1594 	            case 0xd: printf(" (manual, return light not detected)"); break;
   1595 	            case 0xf: printf(" (manual, return light  detected)"); break;
   1596 	            case 0x19:printf(" (auto)"); break;
   1597 	            case 0x1d:printf(" (auto, return light not detected)"); break;
   1598 	            case 0x1f:printf(" (auto, return light detected)"); break;
   1599 	            case 0x41:printf(" (red eye reduction mode)"); break;
   1600 	            case 0x45:printf(" (red eye reduction mode return light not detected)"); break;
   1601 	            case 0x47:printf(" (red eye reduction mode return light  detected)"); break;
   1602 	            case 0x49:printf(" (manual, red eye reduction mode)"); break;
   1603 	            case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break;
   1604 	            case 0x4f:printf(" (red eye reduction mode, return light detected)"); break;
   1605 	            case 0x59:printf(" (auto, red eye reduction mode)"); break;
   1606 	            case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break;
   1607 	            case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break;
   1608             }
   1609         }else{
   1610             printf("Flash used   : No");
   1611             switch (ImageInfo.FlashUsed){
   1612 	            case 0x18:printf(" (auto)"); break;
   1613             }
   1614         }
   1615         printf("\n");
   1616     }
   1617 
   1618 
   1619     if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
   1620         printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom);
   1621         if (ImageInfo.FocalLength35mmEquiv){
   1622             printf("  (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv);
   1623         }
   1624         printf("\n");
   1625     }
   1626 
   1627     if (ImageInfo.DigitalZoomRatio > 1){
   1628         // Digital zoom used.  Shame on you!
   1629         printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio);
   1630     }
   1631 
   1632     if (ImageInfo.CCDWidth){
   1633         printf("CCD width    : %4.2fmm\n",(double)ImageInfo.CCDWidth);
   1634     }
   1635 
   1636     if (ImageInfo.ExposureTime){
   1637         if (ImageInfo.ExposureTime < 0.010){
   1638             printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime);
   1639         }else{
   1640             printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime);
   1641         }
   1642         if (ImageInfo.ExposureTime <= 0.5){
   1643             printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
   1644         }
   1645         printf("\n");
   1646     }
   1647     if (ImageInfo.ApertureFNumber){
   1648         printf("Aperture     : f/%3.1f\n",(double)ImageInfo.ApertureFNumber);
   1649     }
   1650     if (ImageInfo.Distance){
   1651         if (ImageInfo.Distance < 0){
   1652             printf("Focus dist.  : Infinite\n");
   1653         }else{
   1654             printf("Focus dist.  : %4.2fm\n",(double)ImageInfo.Distance);
   1655         }
   1656     }
   1657 
   1658     if (ImageInfo.ISOequivalent){
   1659         printf("ISO equiv.   : %2d\n",(int)ImageInfo.ISOequivalent);
   1660     }
   1661 
   1662     if (ImageInfo.ExposureBias){
   1663         // If exposure bias was specified, but set to zero, presumably its no bias at all,
   1664         // so only show it if its nonzero.
   1665         printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias);
   1666     }
   1667 
   1668     switch(ImageInfo.Whitebalance) {
   1669         case 1:
   1670             printf("Whitebalance : Manual\n");
   1671             break;
   1672         case 0:
   1673             printf("Whitebalance : Auto\n");
   1674             break;
   1675     }
   1676 
   1677     //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
   1678     switch(ImageInfo.LightSource) {
   1679         case 1:
   1680             printf("Light Source : Daylight\n");
   1681             break;
   1682         case 2:
   1683             printf("Light Source : Fluorescent\n");
   1684             break;
   1685         case 3:
   1686             printf("Light Source : Incandescent\n");
   1687             break;
   1688         case 4:
   1689             printf("Light Source : Flash\n");
   1690             break;
   1691         case 9:
   1692             printf("Light Source : Fine weather\n");
   1693             break;
   1694         case 11:
   1695             printf("Light Source : Shade\n");
   1696             break;
   1697         default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs
   1698             // If it just says 'unknown' or we don't know it, then
   1699             // don't bother showing it - it doesn't add any useful information.
   1700     }
   1701 
   1702     if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs
   1703         switch(ImageInfo.MeteringMode) {
   1704         case 2:
   1705             printf("Metering Mode: center weight\n");
   1706             break;
   1707         case 3:
   1708             printf("Metering Mode: spot\n");
   1709             break;
   1710         case 5:
   1711             printf("Metering Mode: matrix\n");
   1712             break;
   1713         }
   1714     }
   1715 
   1716     if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs
   1717         switch(ImageInfo.ExposureProgram) {
   1718         case 1:
   1719             printf("Exposure     : Manual\n");
   1720             break;
   1721         case 2:
   1722             printf("Exposure     : program (auto)\n");
   1723             break;
   1724         case 3:
   1725             printf("Exposure     : aperture priority (semi-auto)\n");
   1726             break;
   1727         case 4:
   1728             printf("Exposure     : shutter priority (semi-auto)\n");
   1729             break;
   1730         case 5:
   1731             printf("Exposure     : Creative Program (based towards depth of field)\n");
   1732             break;
   1733         case 6:
   1734             printf("Exposure     : Action program (based towards fast shutter speed)\n");
   1735             break;
   1736         case 7:
   1737             printf("Exposure     : Portrait Mode\n");
   1738             break;
   1739         case 8:
   1740             printf("Exposure     : LandscapeMode \n");
   1741             break;
   1742         default:
   1743             break;
   1744         }
   1745     }
   1746     switch(ImageInfo.ExposureMode){
   1747         case 0: // Automatic (not worth cluttering up output for)
   1748             break;
   1749         case 1: printf("Exposure Mode: Manual\n");
   1750             break;
   1751         case 2: printf("Exposure Mode: Auto bracketing\n");
   1752             break;
   1753     }
   1754 
   1755     if (ImageInfo.DistanceRange) {
   1756         printf("Focus range  : ");
   1757         switch(ImageInfo.DistanceRange) {
   1758             case 1:
   1759                 printf("macro");
   1760                 break;
   1761             case 2:
   1762                 printf("close");
   1763                 break;
   1764             case 3:
   1765                 printf("distant");
   1766                 break;
   1767         }
   1768         printf("\n");
   1769     }
   1770 
   1771 
   1772 
   1773     if (ImageInfo.Process != M_SOF0){
   1774         // don't show it if its the plain old boring 'baseline' process, but do
   1775         // show it if its something else, like 'progressive' (used on web sometimes)
   1776         int a;
   1777         for (a=0;;a++){
   1778             if (a >= (int)PROCESS_TABLE_SIZE){
   1779                 // ran off the end of the table.
   1780                 printf("Jpeg process : Unknown\n");
   1781                 break;
   1782             }
   1783             if (ProcessTable[a].Tag == ImageInfo.Process){
   1784                 printf("Jpeg process : %s\n",ProcessTable[a].Desc);
   1785                 break;
   1786             }
   1787         }
   1788     }
   1789 
   1790     if (ImageInfo.GpsInfoPresent){
   1791         printf("GPS Latitude : %s\n",ImageInfo.GpsLat);
   1792         printf("GPS Longitude: %s\n",ImageInfo.GpsLong);
   1793         if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt);
   1794     }
   1795 
   1796     // Print the comment. Print 'Comment:' for each new line of comment.
   1797     if (ImageInfo.Comments[0]){
   1798         int a,c;
   1799         printf("Comment      : ");
   1800         if (!ImageInfo.CommentWidchars){
   1801             for (a=0;a<MAX_COMMENT_SIZE;a++){
   1802                 c = ImageInfo.Comments[a];
   1803                 if (c == '\0') break;
   1804                 if (c == '\n'){
   1805                     // Do not start a new line if the string ends with a carriage return.
   1806                     if (ImageInfo.Comments[a+1] != '\0'){
   1807                         printf("\nComment      : ");
   1808                     }else{
   1809                         printf("\n");
   1810                     }
   1811                 }else{
   1812                     putchar(c);
   1813                 }
   1814             }
   1815             printf("\n");
   1816         }else{
   1817             printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
   1818         }
   1819     }
   1820     if (ImageInfo.ThumbnailOffset){
   1821         printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
   1822     } else {
   1823         printf("NO thumbnail");
   1824     }
   1825 }
   1826 
   1827 
   1828 //--------------------------------------------------------------------------
   1829 // Summarize highlights of image info on one line (suitable for grep-ing)
   1830 //--------------------------------------------------------------------------
   1831 void ShowConciseImageInfo(void)
   1832 {
   1833     printf("\"%s\"",ImageInfo.FileName);
   1834 
   1835     printf(" %dx%d",ImageInfo.Width, ImageInfo.Height);
   1836 
   1837     if (ImageInfo.ExposureTime){
   1838         if (ImageInfo.ExposureTime <= 0.5){
   1839             printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime));
   1840         }else{
   1841             printf(" (%1.1f)",ImageInfo.ExposureTime);
   1842         }
   1843     }
   1844 
   1845     if (ImageInfo.ApertureFNumber){
   1846         printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber);
   1847     }
   1848 
   1849     if (ImageInfo.FocalLength35mmEquiv){
   1850         printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv);
   1851     }
   1852 
   1853     if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){
   1854         printf(" (flash)");
   1855     }
   1856 
   1857     if (ImageInfo.IsColor == 0){
   1858         printf(" (bw)");
   1859     }
   1860 
   1861     printf("\n");
   1862 }
   1863