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 LOGE 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_USHORT, 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 //-------------------------------------------------------------------------- 315 // Convert a 16 bit unsigned value to file's native byte order 316 //-------------------------------------------------------------------------- 317 static void Put16u(void * Short, unsigned short PutValue) 318 { 319 if (MotorolaOrder){ 320 ((uchar *)Short)[0] = (uchar)(PutValue>>8); 321 ((uchar *)Short)[1] = (uchar)PutValue; 322 }else{ 323 ((uchar *)Short)[0] = (uchar)PutValue; 324 ((uchar *)Short)[1] = (uchar)(PutValue>>8); 325 } 326 } 327 328 //-------------------------------------------------------------------------- 329 // Convert a 16 bit unsigned value from file's native byte order 330 //-------------------------------------------------------------------------- 331 int Get16u(void * Short) 332 { 333 if (MotorolaOrder){ 334 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; 335 }else{ 336 return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; 337 } 338 } 339 340 //-------------------------------------------------------------------------- 341 // Convert a 32 bit signed value from file's native byte order 342 //-------------------------------------------------------------------------- 343 int Get32s(void * Long) 344 { 345 if (MotorolaOrder){ 346 return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) 347 | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); 348 }else{ 349 return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) 350 | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); 351 } 352 } 353 354 //-------------------------------------------------------------------------- 355 // Convert a 32 bit unsigned value to file's native byte order 356 //-------------------------------------------------------------------------- 357 void Put32u(void * Value, unsigned PutValue) 358 { 359 if (MotorolaOrder){ 360 ((uchar *)Value)[0] = (uchar)(PutValue>>24); 361 ((uchar *)Value)[1] = (uchar)(PutValue>>16); 362 ((uchar *)Value)[2] = (uchar)(PutValue>>8); 363 ((uchar *)Value)[3] = (uchar)PutValue; 364 }else{ 365 ((uchar *)Value)[0] = (uchar)PutValue; 366 ((uchar *)Value)[1] = (uchar)(PutValue>>8); 367 ((uchar *)Value)[2] = (uchar)(PutValue>>16); 368 ((uchar *)Value)[3] = (uchar)(PutValue>>24); 369 } 370 } 371 372 //-------------------------------------------------------------------------- 373 // Convert a 32 bit unsigned value from file's native byte order 374 //-------------------------------------------------------------------------- 375 unsigned Get32u(void * Long) 376 { 377 return (unsigned)Get32s(Long) & 0xffffffff; 378 } 379 380 //-------------------------------------------------------------------------- 381 // Display a number as one of its many formats 382 //-------------------------------------------------------------------------- 383 void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount) 384 { 385 int s,n; 386 387 for(n=0;n<16;n++){ 388 switch(Format){ 389 case FMT_SBYTE: 390 case FMT_BYTE: printf("%02x",*(uchar *)ValuePtr); s=1; break; 391 case FMT_USHORT: printf("%d",Get16u(ValuePtr)); s=2; break; 392 case FMT_ULONG: 393 case FMT_SLONG: printf("%d",Get32s(ValuePtr)); s=4; break; 394 case FMT_SSHORT: printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break; 395 case FMT_URATIONAL: 396 case FMT_SRATIONAL: 397 printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr)); 398 s = 8; 399 break; 400 401 case FMT_SINGLE: printf("%f",(double)*(float *)ValuePtr); s=8; break; 402 case FMT_DOUBLE: printf("%f",*(double *)ValuePtr); s=8; break; 403 default: 404 printf("Unknown format %d:", Format); 405 return; 406 } 407 ByteCount -= s; 408 if (ByteCount <= 0) break; 409 printf(", "); 410 ValuePtr = (void *)((char *)ValuePtr + s); 411 412 } 413 if (n >= 16) printf("..."); 414 } 415 416 417 //-------------------------------------------------------------------------- 418 // Evaluate number, be it int, rational, or float from directory. 419 //-------------------------------------------------------------------------- 420 double ConvertAnyFormat(void * ValuePtr, int Format) 421 { 422 double Value; 423 Value = 0; 424 425 switch(Format){ 426 case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; 427 case FMT_BYTE: Value = *(uchar *)ValuePtr; break; 428 429 case FMT_USHORT: Value = Get16u(ValuePtr); break; 430 case FMT_ULONG: Value = Get32u(ValuePtr); break; 431 432 case FMT_URATIONAL: 433 case FMT_SRATIONAL: 434 { 435 int Num,Den; 436 Num = Get32s(ValuePtr); 437 Den = Get32s(4+(char *)ValuePtr); 438 if (Den == 0){ 439 Value = 0; 440 }else{ 441 Value = (double)Num/Den; 442 } 443 break; 444 } 445 446 case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break; 447 case FMT_SLONG: Value = Get32s(ValuePtr); break; 448 449 // Not sure if this is correct (never seen float used in Exif format) 450 case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; 451 case FMT_DOUBLE: Value = *(double *)ValuePtr; break; 452 453 default: 454 ErrNonfatal("Illegal format code %d",Format,0); 455 } 456 return Value; 457 } 458 459 //-------------------------------------------------------------------------- 460 // Process one of the nested EXIF directories. 461 //-------------------------------------------------------------------------- 462 static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, 463 unsigned ExifLength, int NestingLevel) 464 { 465 int de; 466 int a; 467 int NumDirEntries; 468 unsigned ThumbnailOffset = 0; 469 unsigned ThumbnailSize = 0; 470 char IndentString[25]; 471 472 printf("ProcessExifDir"); 473 if (NestingLevel > 4){ 474 ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0); 475 return; 476 } 477 478 memset(IndentString, ' ', 25); 479 IndentString[NestingLevel * 4] = '\0'; 480 481 482 NumDirEntries = Get16u(DirStart); 483 #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) 484 485 { 486 unsigned char * DirEnd; 487 DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); 488 if (DirEnd+4 > (OffsetBase+ExifLength)){ 489 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){ 490 // Version 1.3 of jhead would truncate a bit too much. 491 // This also caught later on as well. 492 }else{ 493 ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0); 494 return; 495 } 496 } 497 if (DumpExifMap){ 498 printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase)); 499 } 500 501 502 } 503 504 if (ShowTags){ 505 printf("(dir has %d entries)\n",NumDirEntries); 506 } 507 508 for (de=0;de<NumDirEntries;de++){ 509 int Tag, Format, Components; 510 unsigned char * ValuePtr; 511 int ByteCount; 512 unsigned char * DirEntry; 513 DirEntry = DIR_ENTRY_ADDR(DirStart, de); 514 515 Tag = Get16u(DirEntry); 516 Format = Get16u(DirEntry+2); 517 Components = Get32u(DirEntry+4); 518 519 if ((Format-1) >= NUM_FORMATS) { 520 // (-1) catches illegal zero case as unsigned underflows to positive large. 521 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); 522 continue; 523 } 524 525 if ((unsigned)Components > 0x10000){ 526 ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag); 527 continue; 528 } 529 530 ByteCount = Components * BytesPerFormat[Format]; 531 532 if (ByteCount > 4){ 533 unsigned OffsetVal; 534 OffsetVal = Get32u(DirEntry+8); 535 // If its bigger than 4 bytes, the dir entry contains an offset. 536 if (OffsetVal+ByteCount > ExifLength){ 537 // Bogus pointer offset and / or bytecount value 538 ErrNonfatal("Illegal value pointer for tag %04x", Tag,0); 539 continue; 540 } 541 ValuePtr = OffsetBase+OffsetVal; 542 543 if (OffsetVal > ImageInfo.LargestExifOffset){ 544 ImageInfo.LargestExifOffset = OffsetVal; 545 } 546 547 if (DumpExifMap){ 548 printf("Map: %05d-%05d: Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag); 549 } 550 }else{ 551 // 4 bytes or less and value is in the dir entry itself 552 ValuePtr = DirEntry+8; 553 } 554 555 if (Tag == TAG_MAKER_NOTE){ 556 if (ShowTags){ 557 printf("%s Maker note: ",IndentString); 558 } 559 ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength); 560 continue; 561 } 562 563 if (ShowTags){ 564 // Show tag name 565 for (a=0;;a++){ 566 if (a >= (int)TAG_TABLE_SIZE){ 567 printf("%s", IndentString); 568 printf(" Unknown Tag %04x Value = ", Tag); 569 break; 570 } 571 if (TagTable[a].Tag == Tag){ 572 printf("%s", IndentString); 573 printf(" %s = ",TagTable[a].Desc); 574 break; 575 } 576 } 577 578 // Show tag value. 579 switch(Format){ 580 case FMT_BYTE: 581 if(ByteCount>1){ 582 printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr); 583 }else{ 584 PrintFormatNumber(ValuePtr, Format, ByteCount); 585 printf("\n"); 586 } 587 break; 588 589 case FMT_UNDEFINED: 590 // Undefined is typically an ascii string. 591 592 case FMT_STRING: 593 // String arrays printed without function call (different from int arrays) 594 { 595 printf("\"%s\"", ValuePtr); 596 // int NoPrint = 0; 597 // printf("\""); 598 // for (a=0;a<ByteCount;a++){ 599 // if (ValuePtr[a] >= 32){ 600 // putchar(ValuePtr[a]); 601 // NoPrint = 0; 602 // }else{ 603 // // Avoiding indicating too many unprintable characters of proprietary 604 // // bits of binary information this program may not know how to parse. 605 // if (!NoPrint && a != ByteCount-1){ 606 // putchar('?'); 607 // NoPrint = 1; 608 // } 609 // } 610 // } 611 // printf("\"\n"); 612 } 613 break; 614 615 default: 616 // Handle arrays of numbers later (will there ever be?) 617 PrintFormatNumber(ValuePtr, Format, ByteCount); 618 printf("\n"); 619 } 620 } 621 622 // Extract useful components of tag 623 switch(Tag){ 624 625 case TAG_MAKE: 626 strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31); 627 break; 628 629 case TAG_MODEL: 630 strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39); 631 break; 632 633 case TAG_DATETIME_ORIGINAL: 634 // If we get a DATETIME_ORIGINAL, we use that one. 635 strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19); 636 // Fallthru... 637 638 case TAG_DATETIME_DIGITIZED: 639 case TAG_DATETIME: 640 if (!isdigit(ImageInfo.DateTime[0])){ 641 // If we don't already have a DATETIME_ORIGINAL, use whatever 642 // time fields we may have. 643 strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19); 644 } 645 646 if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){ 647 ErrNonfatal("More than %d date fields! This is nuts", MAX_DATE_COPIES, 0); 648 break; 649 } 650 ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] = 651 (char *)ValuePtr - (char *)OffsetBase; 652 break; 653 654 case TAG_WINXP_COMMENT: 655 if (ImageInfo.Comments[0]){ // We already have a jpeg comment. 656 // Already have a comment (probably windows comment), skip this one. 657 if (ShowTags) printf("Windows XP commend and other comment in header\n"); 658 break; // Already have a windows comment, skip this one. 659 } 660 661 if (ByteCount > 1){ 662 if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE; 663 memcpy(ImageInfo.Comments, ValuePtr, ByteCount); 664 ImageInfo.CommentWidchars = ByteCount/2; 665 } 666 break; 667 668 case TAG_USERCOMMENT: 669 if (ImageInfo.Comments[0]){ // We already have a jpeg comment. 670 // Already have a comment (probably windows comment), skip this one. 671 if (ShowTags) printf("Multiple comments in exif header\n"); 672 break; // Already have a windows comment, skip this one. 673 } 674 675 // Comment is often padded with trailing spaces. Remove these first. 676 for (a=ByteCount;;){ 677 a--; 678 if ((ValuePtr)[a] == ' '){ 679 (ValuePtr)[a] = '\0'; 680 }else{ 681 break; 682 } 683 if (a == 0) break; 684 } 685 686 // Copy the comment 687 if (memcmp(ValuePtr, "ASCII",5) == 0){ 688 for (a=5;a<10;a++){ 689 int c; 690 c = (ValuePtr)[a]; 691 if (c != '\0' && c != ' '){ 692 strncpy(ImageInfo.Comments, (char *)ValuePtr+a, 199); 693 break; 694 } 695 } 696 }else{ 697 strncpy(ImageInfo.Comments, (char *)ValuePtr, MAX_COMMENT_SIZE-1); 698 } 699 break; 700 701 case TAG_FNUMBER: 702 // Simplest way of expressing aperture, so I trust it the most. 703 // (overwrite previously computd value if there is one) 704 ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); 705 break; 706 707 case TAG_APERTURE: 708 case TAG_MAXAPERTURE: 709 // More relevant info always comes earlier, so only use this field if we don't 710 // have appropriate aperture information yet. 711 if (ImageInfo.ApertureFNumber == 0){ 712 ImageInfo.ApertureFNumber 713 = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); 714 } 715 break; 716 717 case TAG_FOCALLENGTH: 718 // Nice digital cameras actually save the focal length as a function 719 // of how farthey are zoomed in. 720 ImageInfo.FocalLength.num = Get32u(ValuePtr); 721 ImageInfo.FocalLength.denom = Get32u(4+(char *)ValuePtr); 722 break; 723 724 case TAG_SUBJECT_DISTANCE: 725 // Inidcates the distacne the autofocus camera is focused to. 726 // Tends to be less accurate as distance increases. 727 ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format); 728 break; 729 730 case TAG_EXPOSURETIME: 731 // Simplest way of expressing exposure time, so I trust it most. 732 // (overwrite previously computd value if there is one) 733 ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); 734 break; 735 736 case TAG_SHUTTERSPEED: 737 // More complicated way of expressing exposure time, so only use 738 // this value if we don't already have it from somewhere else. 739 if (ImageInfo.ExposureTime == 0){ 740 ImageInfo.ExposureTime 741 = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); 742 } 743 break; 744 745 746 case TAG_FLASH: 747 ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format); 748 break; 749 750 case TAG_ORIENTATION: 751 if (NumOrientations >= 2){ 752 // Can have another orientation tag for the thumbnail, but if there's 753 // a third one, things are stringae. 754 ErrNonfatal("More than two orientation tags!",0,0); 755 break; 756 } 757 OrientationPtr[NumOrientations] = ValuePtr; 758 OrientationNumFormat[NumOrientations] = Format; 759 if (NumOrientations == 0){ 760 ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format); 761 } 762 if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){ 763 ErrNonfatal("Undefined rotation value %d", ImageInfo.Orientation, 0); 764 ImageInfo.Orientation = 0; 765 } 766 NumOrientations += 1; 767 break; 768 769 case TAG_EXIF_IMAGELENGTH: 770 case TAG_EXIF_IMAGEWIDTH: 771 // Use largest of height and width to deal with images that have been 772 // rotated to portrait format. 773 a = (int)ConvertAnyFormat(ValuePtr, Format); 774 if (ExifImageWidth < a) ExifImageWidth = a; 775 break; 776 777 case TAG_FOCAL_PLANE_XRES: 778 FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); 779 break; 780 781 case TAG_FOCAL_PLANE_UNITS: 782 switch((int)ConvertAnyFormat(ValuePtr, Format)){ 783 case 1: FocalplaneUnits = 25.4; break; // inch 784 case 2: 785 // According to the information I was using, 2 means meters. 786 // But looking at the Cannon powershot's files, inches is the only 787 // sensible value. 788 FocalplaneUnits = 25.4; 789 break; 790 791 case 3: FocalplaneUnits = 10; break; // centimeter 792 case 4: FocalplaneUnits = 1; break; // millimeter 793 case 5: FocalplaneUnits = .001; break; // micrometer 794 } 795 break; 796 797 case TAG_EXPOSURE_BIAS: 798 ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); 799 break; 800 801 case TAG_WHITEBALANCE: 802 ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); 803 break; 804 805 case TAG_LIGHT_SOURCE: 806 ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format); 807 break; 808 809 case TAG_METERING_MODE: 810 ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); 811 break; 812 813 case TAG_EXPOSURE_PROGRAM: 814 ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); 815 break; 816 817 case TAG_EXPOSURE_INDEX: 818 if (ImageInfo.ISOequivalent == 0){ 819 // Exposure index and ISO equivalent are often used interchangeably, 820 // so we will do the same in jhead. 821 // http://photography.about.com/library/glossary/bldef_ei.htm 822 ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); 823 } 824 break; 825 826 case TAG_EXPOSURE_MODE: 827 ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format); 828 break; 829 830 case TAG_ISO_EQUIVALENT: 831 ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); 832 if ( ImageInfo.ISOequivalent < 50 ){ 833 // Fixes strange encoding on some older digicams. 834 ImageInfo.ISOequivalent *= 200; 835 } 836 break; 837 838 case TAG_DIGITALZOOMRATIO: 839 ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format); 840 break; 841 842 case TAG_THUMBNAIL_OFFSET: 843 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); 844 DirWithThumbnailPtrs = DirStart; 845 break; 846 847 case TAG_THUMBNAIL_LENGTH: 848 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); 849 ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase; 850 break; 851 852 case TAG_EXIF_OFFSET: 853 if (ShowTags) printf("%s Exif Dir:",IndentString); 854 855 case TAG_INTEROP_OFFSET: 856 if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s Interop Dir:",IndentString); 857 { 858 unsigned char * SubdirStart; 859 SubdirStart = OffsetBase + Get32u(ValuePtr); 860 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ 861 ErrNonfatal("Illegal exif or interop ofset directory link",0,0); 862 }else{ 863 ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1); 864 } 865 continue; 866 } 867 break; 868 869 case TAG_GPSINFO: 870 if (ShowTags) printf("%s GPS info dir:",IndentString); 871 { 872 unsigned char * SubdirStart; 873 SubdirStart = OffsetBase + Get32u(ValuePtr); 874 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ 875 ErrNonfatal("Illegal GPS directory link",0,0); 876 }else{ 877 ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength); 878 } 879 continue; 880 } 881 break; 882 883 case TAG_FOCALLENGTH_35MM: 884 // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002) 885 // if its present, use it to compute equivalent focal length instead of 886 // computing it from sensor geometry and actual focal length. 887 ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format); 888 break; 889 890 case TAG_DISTANCE_RANGE: 891 // Three possible standard values: 892 // 1 = macro, 2 = close, 3 = distant 893 ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format); 894 break; 895 } 896 } 897 898 899 { 900 // In addition to linking to subdirectories via exif tags, 901 // there's also a potential link to another directory at the end of each 902 // directory. this has got to be the result of a committee! 903 unsigned char * SubdirStart; 904 unsigned Offset; 905 906 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){ 907 printf("DirStart %d offset from dirstart %d", (int)DirStart, 2+12*NumDirEntries); 908 Offset = Get32u(DirStart+2+12*NumDirEntries); 909 if (Offset){ 910 SubdirStart = OffsetBase + Offset; 911 if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){ 912 printf("SubdirStart %d OffsetBase %d ExifLength %d Offset %d", 913 (int)SubdirStart, (int)OffsetBase, ExifLength, Offset); 914 if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){ 915 // Jhead 1.3 or earlier would crop the whole directory! 916 // As Jhead produces this form of format incorrectness, 917 // I'll just let it pass silently 918 if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n"); 919 }else{ 920 ErrNonfatal("Illegal subdirectory link",0,0); 921 } 922 }else{ 923 if (SubdirStart <= OffsetBase+ExifLength){ 924 if (ShowTags) printf("%s Continued directory ",IndentString); 925 ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1); 926 } 927 } 928 if (Offset > ImageInfo.LargestExifOffset){ 929 ImageInfo.LargestExifOffset = Offset; 930 } 931 } 932 }else{ 933 // The exif header ends before the last next directory pointer. 934 } 935 } 936 937 if (ThumbnailOffset){ 938 ImageInfo.ThumbnailAtEnd = FALSE; 939 940 if (DumpExifMap){ 941 printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize); 942 } 943 944 if (ThumbnailOffset <= ExifLength){ 945 if (ThumbnailSize > ExifLength-ThumbnailOffset){ 946 // If thumbnail extends past exif header, only save the part that 947 // actually exists. Canon's EOS viewer utility will do this - the 948 // thumbnail extracts ok with this hack. 949 ThumbnailSize = ExifLength-ThumbnailOffset; 950 if (ShowTags) printf("Thumbnail incorrectly placed in header\n"); 951 952 } 953 // The thumbnail pointer appears to be valid. Store it. 954 ImageInfo.ThumbnailOffset = ThumbnailOffset; 955 ImageInfo.ThumbnailSize = ThumbnailSize; 956 957 if (ShowTags){ 958 printf("Thumbnail size: %d bytes\n",ThumbnailSize); 959 } 960 } 961 } 962 printf("returning from ProcessExifDir"); 963 } 964 965 966 //-------------------------------------------------------------------------- 967 // Process a EXIF marker 968 // Describes all the drivel that most digital cameras include... 969 //-------------------------------------------------------------------------- 970 void process_EXIF (unsigned char * ExifSection, unsigned int length) 971 { 972 int FirstOffset; 973 974 FocalplaneXRes = 0; 975 FocalplaneUnits = 0; 976 ExifImageWidth = 0; 977 NumOrientations = 0; 978 979 if (ShowTags){ 980 printf("Exif header %d bytes long\n",length); 981 } 982 983 { // Check the EXIF header component 984 static uchar ExifHeader[] = "Exif\0\0"; 985 if (memcmp(ExifSection+2, ExifHeader,6)){ 986 ErrNonfatal("Incorrect Exif header",0,0); 987 return; 988 } 989 } 990 991 if (memcmp(ExifSection+8,"II",2) == 0){ 992 if (ShowTags) printf("Exif section in Intel order\n"); 993 MotorolaOrder = 0; 994 }else{ 995 if (memcmp(ExifSection+8,"MM",2) == 0){ 996 if (ShowTags) printf("Exif section in Motorola order\n"); 997 MotorolaOrder = 1; 998 }else{ 999 ErrNonfatal("Invalid Exif alignment marker.",0,0); 1000 return; 1001 } 1002 } 1003 1004 // Check the next value for correctness. 1005 if (Get16u(ExifSection+10) != 0x2a){ 1006 ErrNonfatal("Invalid Exif start (1)",0,0); 1007 return; 1008 } 1009 1010 FirstOffset = Get32u(ExifSection+12); 1011 if (FirstOffset < 8 || FirstOffset > 16){ 1012 // Usually set to 8, but other values valid too. 1013 ErrNonfatal("Suspicious offset of first IFD value",0,0); 1014 return; 1015 } 1016 1017 DirWithThumbnailPtrs = NULL; 1018 1019 1020 // First directory starts 16 bytes in. All offset are relative to 8 bytes in. 1021 ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0); 1022 1023 ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE; 1024 #ifdef SUPERDEBUG 1025 printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at")); 1026 #endif 1027 if (DumpExifMap){ 1028 unsigned a,b; 1029 printf("Map: %05d- End of exif\n",length-8); 1030 // for (a=0;a<length-8;a+= 10){ 1031 // printf("Map: %05d ",a); 1032 // for (b=0;b<10;b++) printf(" %02x",*(ExifSection+8+a+b)); 1033 // printf("\n"); 1034 // } 1035 for (a = 0; a < length - 8; ++a) { 1036 unsigned char c = *(ExifSection+8+a); 1037 unsigned pc = isprint(c) ? c : ' '; 1038 printf("Map: %4d %02x %c", a, c, pc); 1039 } 1040 } 1041 1042 1043 // Compute the CCD width, in millimeters. 1044 if (FocalplaneXRes != 0){ 1045 // Note: With some cameras, its not possible to compute this correctly because 1046 // they don't adjust the indicated focal plane resolution units when using less 1047 // than maximum resolution, so the CCDWidth value comes out too small. Nothing 1048 // that Jhad can do about it - its a camera problem. 1049 ImageInfo.CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); 1050 1051 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0 1052 && ImageInfo.FocalLength35mmEquiv == 0){ 1053 // Compute 35 mm equivalent focal length based on sensor geometry if we haven't 1054 // already got it explicitly from a tag. 1055 ImageInfo.FocalLength35mmEquiv = (int)( 1056 (double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom 1057 / ImageInfo.CCDWidth * 36 + 0.5); 1058 } 1059 } 1060 } 1061 1062 static const TagTable_t* TagToTagTableEntry(unsigned short tag) 1063 { 1064 unsigned int i; 1065 for (i = 0; i < TAG_TABLE_SIZE; i++) { 1066 if (TagTable[i].Tag == tag) { 1067 printf("found tag %d", tag); 1068 int format = TagTable[i].Format; 1069 if (format == 0) { 1070 printf("tag %s format not defined ***** YOU MUST ADD THE FORMAT TO THE TagTable in exif.c!!!!", TagTable[i].Desc); 1071 return NULL; 1072 } 1073 return &TagTable[i]; 1074 } 1075 } 1076 printf("tag %d NOT FOUND", tag); 1077 return NULL; 1078 } 1079 1080 static void writeExifTagAndData(int tag, 1081 int format, 1082 long components, 1083 long value, 1084 int valueInString, 1085 char* Buffer, 1086 int* DirIndex, 1087 int* DataWriteIndex) { 1088 Put16u(Buffer+ (*DirIndex), tag); // Tag 1089 Put16u(Buffer+(*DirIndex) + 2, format); // Format 1090 if (format == FMT_STRING && components == -1) { 1091 components = strlen((char*)value) + 1; // account for null terminator 1092 if (components & 1) ++components; // no odd lengths 1093 } 1094 Put32u(Buffer+(*DirIndex) + 4, components); // Components 1095 printf("# components: %ld", components); 1096 if (format == FMT_STRING) { 1097 // short strings can fit right in the long, otherwise have to 1098 // go in the data area 1099 if (components <= 4) { 1100 strcpy(Buffer+(*DirIndex) + 8, (char*)value); 1101 } else { 1102 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer 1103 printf("copying value %s to %d", (char*)value, (*DataWriteIndex)); 1104 strncpy(Buffer+(*DataWriteIndex), (char*)value, components); 1105 (*DataWriteIndex) += components; 1106 } 1107 } else if (!valueInString) { 1108 Put32u(Buffer+(*DirIndex) + 8, value); // Value 1109 } else { 1110 Put32u(Buffer+(*DirIndex) + 8, (*DataWriteIndex)-8); // Pointer 1111 char* curElement = strtok((char*)value, ","); 1112 int i; 1113 for (i = 0; i < components && curElement != NULL; i++) { 1114 #ifdef SUPERDEBUG 1115 printf("processing component %s format %s", curElement, formatStr(format)); 1116 #endif 1117 // elements are separated by commas 1118 if (format == FMT_URATIONAL) { 1119 char* separator = strchr(curElement, '/'); 1120 if (separator) { 1121 unsigned int numerator = atoi(curElement); 1122 unsigned int denominator = atoi(separator + 1); 1123 Put32u(Buffer+(*DataWriteIndex), numerator); 1124 Put32u(Buffer+(*DataWriteIndex) + 4, denominator); 1125 (*DataWriteIndex) += 8; 1126 } 1127 } else if (format == FMT_SRATIONAL) { 1128 char* separator = strchr(curElement, '/'); 1129 if (separator) { 1130 int numerator = atoi(curElement); 1131 int denominator = atoi(separator + 1); 1132 Put32u(Buffer+(*DataWriteIndex), numerator); 1133 Put32u(Buffer+(*DataWriteIndex) + 4, denominator); 1134 (*DataWriteIndex) += 8; 1135 } 1136 } else { 1137 // TODO: doesn't handle multiple components yet -- if more than one, have to put in data write area. 1138 value = atoi(curElement); 1139 Put32u(Buffer+(*DirIndex) + 8, value); // Value 1140 } 1141 curElement = strtok(NULL, ","); 1142 } 1143 } 1144 (*DirIndex) += 12; 1145 } 1146 1147 #ifdef SUPERDEBUG 1148 char* formatStr(int format) { 1149 switch (format) { 1150 case FMT_BYTE: return "FMT_BYTE"; break; 1151 case FMT_STRING: return "FMT_STRING"; break; 1152 case FMT_USHORT: return "FMT_USHORT"; break; 1153 case FMT_ULONG: return "FMT_ULONG"; break; 1154 case FMT_URATIONAL: return "FMT_URATIONAL"; break; 1155 case FMT_SBYTE: return "FMT_SBYTE"; break; 1156 case FMT_UNDEFINED: return "FMT_UNDEFINED"; break; 1157 case FMT_SSHORT: return "FMT_SSHORT"; break; 1158 case FMT_SLONG: return "FMT_SLONG"; break; 1159 case FMT_SRATIONAL: return "FMT_SRATIONAL"; break; 1160 case FMT_SINGLE: return "FMT_SINGLE"; break; 1161 case FMT_DOUBLE: return "FMT_SINGLE"; break; 1162 default: return "UNKNOWN"; 1163 } 1164 } 1165 #endif 1166 1167 //-------------------------------------------------------------------------- 1168 // Create minimal exif header - just date and thumbnail pointers, 1169 // so that date and thumbnail may be filled later. 1170 //-------------------------------------------------------------------------- 1171 void create_EXIF(ExifElement_t* elements, int exifTagCount, int gpsTagCount) 1172 { 1173 // TODO: We need to dynamically allocate this buffer and resize it when 1174 // necessary while writing so we don't do a buffer overflow. 1175 char Buffer[1024]; 1176 1177 unsigned short NumEntries; 1178 int DataWriteIndex; 1179 int DirIndex; 1180 int DirContinuation = 0; 1181 1182 #ifdef SUPERDEBUG 1183 LOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount); 1184 #endif 1185 1186 MotorolaOrder = 0; 1187 1188 memcpy(Buffer+2, "Exif\0\0II",8); 1189 Put16u(Buffer+10, 0x2a); 1190 1191 DataWriteIndex = 16; 1192 Put32u(Buffer+12, DataWriteIndex-8); // first IFD offset. Means start 16 bytes in. 1193 1194 { 1195 DirIndex = DataWriteIndex; 1196 NumEntries = 2 + exifTagCount; // the two extra are the datetime and the thumbnail 1197 if (gpsTagCount) { 1198 ++NumEntries; // allow for the GPS info tag 1199 } 1200 DataWriteIndex += 2 + NumEntries*12 + 4; 1201 1202 Put16u(Buffer+DirIndex, NumEntries); // Number of entries 1203 DirIndex += 2; 1204 1205 // Entries go here... 1206 { 1207 // Date/time entry 1208 char* dateTime = NULL; 1209 char dateBuf[20]; 1210 if (ImageInfo.numDateTimeTags){ 1211 // If we had a pre-existing exif header, use time from that. 1212 dateTime = ImageInfo.DateTime; 1213 } else { 1214 // Oterwise, use the file's timestamp. 1215 FileTimeAsString(dateBuf); 1216 dateTime = dateBuf; 1217 } 1218 writeExifTagAndData(TAG_DATETIME, 1219 FMT_STRING, 1220 20, 1221 (long)(char*)dateBuf, 1222 FALSE, 1223 Buffer, 1224 &DirIndex, 1225 &DataWriteIndex); 1226 1227 } 1228 if (exifTagCount > 0) { 1229 int i; 1230 for (i = 0; i < exifTagCount + gpsTagCount; i++) { 1231 if (elements[i].GpsTag) { 1232 continue; 1233 } 1234 const TagTable_t* entry = TagToTagTableEntry(elements[i].Tag); 1235 if (entry == NULL) { 1236 continue; 1237 } 1238 #ifdef SUPERDEBUG 1239 LOGE("create_EXIF saving tag %x value \"%s\"",elements[i].Tag, elements[i].Value); 1240 #endif 1241 writeExifTagAndData(elements[i].Tag, 1242 entry->Format, 1243 entry->DataLength, 1244 (long)elements[i].Value, 1245 TRUE, 1246 Buffer, 1247 &DirIndex, 1248 &DataWriteIndex); 1249 } 1250 1251 if (gpsTagCount) { 1252 // Link to gps dir entry 1253 writeExifTagAndData(TAG_GPSINFO, 1254 FMT_ULONG, 1255 1, 1256 DataWriteIndex-8, 1257 FALSE, 1258 Buffer, 1259 &DirIndex, 1260 &DataWriteIndex); 1261 } 1262 1263 // Link to exif dir entry 1264 int exifDirPtr = DataWriteIndex-8; 1265 if (gpsTagCount) { 1266 exifDirPtr += 2 + gpsTagCount*12 + 4; 1267 } 1268 DirContinuation = DirIndex; 1269 writeExifTagAndData(TAG_EXIF_OFFSET, 1270 FMT_ULONG, 1271 1, 1272 exifDirPtr, 1273 FALSE, 1274 Buffer, 1275 &DirIndex, 1276 &DataWriteIndex); 1277 } 1278 1279 // End of directory - contains optional link to continued directory. 1280 DirContinuation = DirIndex; 1281 printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex); 1282 } 1283 1284 1285 // GPS Section 1286 if (gpsTagCount) { 1287 DirIndex = DataWriteIndex; 1288 printf("Starting GPS section DirIndex = %d", DirIndex); 1289 NumEntries = gpsTagCount; 1290 DataWriteIndex += 2 + NumEntries*12 + 4; 1291 1292 Put16u(Buffer+DirIndex, NumEntries); // Number of entries 1293 DirIndex += 2; 1294 { 1295 int i; 1296 for (i = 0; i < exifTagCount + gpsTagCount; i++) { 1297 if (!elements[i].GpsTag) { 1298 continue; 1299 } 1300 const TagTable_t* entry = GpsTagToTagTableEntry(elements[i].Tag); 1301 if (entry == NULL) { 1302 continue; 1303 } 1304 #ifdef SUPERDEBUG 1305 LOGE("create_EXIF saving GPS tag %x value \"%s\"",elements[i].Tag, elements[i].Value); 1306 #endif 1307 writeExifTagAndData(elements[i].Tag, 1308 entry->Format, 1309 entry->DataLength, 1310 (long)elements[i].Value, 1311 TRUE, 1312 Buffer, 1313 &DirIndex, 1314 &DataWriteIndex); 1315 } 1316 } 1317 1318 // End of directory - contains optional link to continued directory. 1319 Put32u(Buffer+DirIndex, 0); 1320 printf("Ending GPS section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex); 1321 } 1322 1323 { 1324 //Continuation which links to this directory; 1325 Put32u(Buffer+DirContinuation, DataWriteIndex-8); 1326 1327 printf("Starting Thumbnail section DirIndex = %d", DirIndex); 1328 DirIndex = DataWriteIndex; 1329 NumEntries = 2; 1330 DataWriteIndex += 2 + NumEntries*12 + 4; 1331 1332 Put16u(Buffer+DirIndex, NumEntries); // Number of entries 1333 DirIndex += 2; 1334 { 1335 // Link to exif dir entry 1336 writeExifTagAndData(TAG_THUMBNAIL_OFFSET, 1337 FMT_ULONG, 1338 1, 1339 DataWriteIndex-8, 1340 FALSE, 1341 Buffer, 1342 &DirIndex, 1343 &DataWriteIndex); 1344 } 1345 1346 { 1347 // Link to exif dir entry 1348 writeExifTagAndData(TAG_THUMBNAIL_LENGTH, 1349 FMT_ULONG, 1350 1, 1351 0, 1352 FALSE, 1353 Buffer, 1354 &DirIndex, 1355 &DataWriteIndex); 1356 } 1357 1358 // End of directory - contains optional link to continued directory. 1359 Put32u(Buffer+DirIndex, 0); 1360 printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex); 1361 } 1362 1363 1364 Buffer[0] = (unsigned char)(DataWriteIndex >> 8); 1365 Buffer[1] = (unsigned char)DataWriteIndex; 1366 1367 // Remove old exif section, if there was one. 1368 RemoveSectionType(M_EXIF); 1369 1370 { 1371 // Sections need malloced buffers, so do that now, especially because 1372 // we now know how big it needs to be allocated. 1373 unsigned char * NewBuf = malloc(DataWriteIndex); 1374 if (NewBuf == NULL){ 1375 ErrFatal("Could not allocate memory"); 1376 } 1377 memcpy(NewBuf, Buffer, DataWriteIndex); 1378 1379 CreateSection(M_EXIF, NewBuf, DataWriteIndex); 1380 1381 // Re-parse new exif section, now that its in place 1382 // otherwise, we risk touching data that has already been freed. 1383 process_EXIF(NewBuf, DataWriteIndex); 1384 } 1385 } 1386 1387 //-------------------------------------------------------------------------- 1388 // Cler the rotation tag in the exif header to 1. 1389 //-------------------------------------------------------------------------- 1390 const char * ClearOrientation(void) 1391 { 1392 int a; 1393 if (NumOrientations == 0) return NULL; 1394 1395 for (a=0;a<NumOrientations;a++){ 1396 switch(OrientationNumFormat[a]){ 1397 case FMT_SBYTE: 1398 case FMT_BYTE: 1399 *(uchar *)(OrientationPtr[a]) = 1; 1400 break; 1401 1402 case FMT_USHORT: 1403 Put16u(OrientationPtr[a], 1); 1404 break; 1405 1406 case FMT_ULONG: 1407 case FMT_SLONG: 1408 memset(OrientationPtr, 0, 4); 1409 // Can't be bothered to write generic Put32 if I only use it once. 1410 if (MotorolaOrder){ 1411 ((uchar *)OrientationPtr[a])[3] = 1; 1412 }else{ 1413 ((uchar *)OrientationPtr[a])[0] = 1; 1414 } 1415 break; 1416 1417 default: 1418 return NULL; 1419 } 1420 } 1421 1422 return OrientTab[ImageInfo.Orientation]; 1423 } 1424 1425 1426 1427 //-------------------------------------------------------------------------- 1428 // Remove thumbnail out of the exif image. 1429 //-------------------------------------------------------------------------- 1430 int RemoveThumbnail(unsigned char * ExifSection) 1431 { 1432 if (!DirWithThumbnailPtrs || 1433 ImageInfo.ThumbnailOffset == 0 || 1434 ImageInfo.ThumbnailSize == 0){ 1435 // No thumbnail, or already deleted it. 1436 return 0; 1437 } 1438 if (ImageInfo.ThumbnailAtEnd == FALSE){ 1439 ErrNonfatal("Thumbnail is not at end of header, can't chop it off", 0, 0); 1440 return 0; 1441 } 1442 1443 { 1444 int de; 1445 int NumDirEntries; 1446 NumDirEntries = Get16u(DirWithThumbnailPtrs); 1447 1448 for (de=0;de<NumDirEntries;de++){ 1449 int Tag; 1450 unsigned char * DirEntry; 1451 DirEntry = DIR_ENTRY_ADDR(DirWithThumbnailPtrs, de); 1452 Tag = Get16u(DirEntry); 1453 if (Tag == TAG_THUMBNAIL_LENGTH){ 1454 // Set length to zero. 1455 if (Get16u(DirEntry+2) != FMT_ULONG){ 1456 // non standard format encoding. Can't do it. 1457 ErrNonfatal("Can't remove thumbnail", 0, 0); 1458 return 0; 1459 } 1460 Put32u(DirEntry+8, 0); 1461 } 1462 } 1463 } 1464 1465 // This is how far the non thumbnail data went. 1466 return ImageInfo.ThumbnailOffset+8; 1467 1468 } 1469 1470 1471 //-------------------------------------------------------------------------- 1472 // Convert exif time to Unix time structure 1473 //-------------------------------------------------------------------------- 1474 int Exif2tm(struct tm * timeptr, char * ExifTime) 1475 { 1476 int a; 1477 1478 timeptr->tm_wday = -1; 1479 1480 // Check for format: YYYY:MM:DD HH:MM:SS format. 1481 // Date and time normally separated by a space, but also seen a ':' there, so 1482 // skip the middle space with '%*c' so it can be any character. 1483 a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d", 1484 &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday, 1485 &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec); 1486 1487 1488 if (a == 6){ 1489 timeptr->tm_isdst = -1; 1490 timeptr->tm_mon -= 1; // Adjust for unix zero-based months 1491 timeptr->tm_year -= 1900; // Adjust for year starting at 1900 1492 return TRUE; // worked. 1493 } 1494 1495 return FALSE; // Wasn't in Exif date format. 1496 } 1497 1498 1499 //-------------------------------------------------------------------------- 1500 // Show the collected image info, displaying camera F-stop and shutter speed 1501 // in a consistent and legible fashion. 1502 //-------------------------------------------------------------------------- 1503 void ShowImageInfo(int ShowFileInfo) 1504 { 1505 if (ShowFileInfo){ 1506 printf("File name : %s\n",ImageInfo.FileName); 1507 printf("File size : %d bytes\n",ImageInfo.FileSize); 1508 1509 { 1510 char Temp[20]; 1511 FileTimeAsString(Temp); 1512 printf("File date : %s\n",Temp); 1513 } 1514 } 1515 1516 if (ImageInfo.CameraMake[0]){ 1517 printf("Camera make : %s\n",ImageInfo.CameraMake); 1518 printf("Camera model : %s\n",ImageInfo.CameraModel); 1519 } 1520 if (ImageInfo.DateTime[0]){ 1521 printf("Date/Time : %s\n",ImageInfo.DateTime); 1522 } 1523 printf("Resolution : %d x %d\n",ImageInfo.Width, ImageInfo.Height); 1524 1525 if (ImageInfo.Orientation > 1){ 1526 // Only print orientation if one was supplied, and if its not 1 (normal orientation) 1527 printf("Orientation : %s\n", OrientTab[ImageInfo.Orientation]); 1528 } 1529 1530 if (ImageInfo.IsColor == 0){ 1531 printf("Color/bw : Black and white\n"); 1532 } 1533 1534 if (ImageInfo.FlashUsed >= 0){ 1535 if (ImageInfo.FlashUsed & 1){ 1536 printf("Flash used : Yes"); 1537 switch (ImageInfo.FlashUsed){ 1538 case 0x5: printf(" (Strobe light not detected)"); break; 1539 case 0x7: printf(" (Strobe light detected) "); break; 1540 case 0x9: printf(" (manual)"); break; 1541 case 0xd: printf(" (manual, return light not detected)"); break; 1542 case 0xf: printf(" (manual, return light detected)"); break; 1543 case 0x19:printf(" (auto)"); break; 1544 case 0x1d:printf(" (auto, return light not detected)"); break; 1545 case 0x1f:printf(" (auto, return light detected)"); break; 1546 case 0x41:printf(" (red eye reduction mode)"); break; 1547 case 0x45:printf(" (red eye reduction mode return light not detected)"); break; 1548 case 0x47:printf(" (red eye reduction mode return light detected)"); break; 1549 case 0x49:printf(" (manual, red eye reduction mode)"); break; 1550 case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break; 1551 case 0x4f:printf(" (red eye reduction mode, return light detected)"); break; 1552 case 0x59:printf(" (auto, red eye reduction mode)"); break; 1553 case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break; 1554 case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break; 1555 } 1556 }else{ 1557 printf("Flash used : No"); 1558 switch (ImageInfo.FlashUsed){ 1559 case 0x18:printf(" (auto)"); break; 1560 } 1561 } 1562 printf("\n"); 1563 } 1564 1565 1566 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) { 1567 printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength.num / ImageInfo.FocalLength.denom); 1568 if (ImageInfo.FocalLength35mmEquiv){ 1569 printf(" (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv); 1570 } 1571 printf("\n"); 1572 } 1573 1574 if (ImageInfo.DigitalZoomRatio > 1){ 1575 // Digital zoom used. Shame on you! 1576 printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio); 1577 } 1578 1579 if (ImageInfo.CCDWidth){ 1580 printf("CCD width : %4.2fmm\n",(double)ImageInfo.CCDWidth); 1581 } 1582 1583 if (ImageInfo.ExposureTime){ 1584 if (ImageInfo.ExposureTime < 0.010){ 1585 printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime); 1586 }else{ 1587 printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime); 1588 } 1589 if (ImageInfo.ExposureTime <= 0.5){ 1590 printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime)); 1591 } 1592 printf("\n"); 1593 } 1594 if (ImageInfo.ApertureFNumber){ 1595 printf("Aperture : f/%3.1f\n",(double)ImageInfo.ApertureFNumber); 1596 } 1597 if (ImageInfo.Distance){ 1598 if (ImageInfo.Distance < 0){ 1599 printf("Focus dist. : Infinite\n"); 1600 }else{ 1601 printf("Focus dist. : %4.2fm\n",(double)ImageInfo.Distance); 1602 } 1603 } 1604 1605 if (ImageInfo.ISOequivalent){ 1606 printf("ISO equiv. : %2d\n",(int)ImageInfo.ISOequivalent); 1607 } 1608 1609 if (ImageInfo.ExposureBias){ 1610 // If exposure bias was specified, but set to zero, presumably its no bias at all, 1611 // so only show it if its nonzero. 1612 printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias); 1613 } 1614 1615 switch(ImageInfo.Whitebalance) { 1616 case 1: 1617 printf("Whitebalance : Manual\n"); 1618 break; 1619 case 0: 1620 printf("Whitebalance : Auto\n"); 1621 break; 1622 } 1623 1624 //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both 1625 switch(ImageInfo.LightSource) { 1626 case 1: 1627 printf("Light Source : Daylight\n"); 1628 break; 1629 case 2: 1630 printf("Light Source : Fluorescent\n"); 1631 break; 1632 case 3: 1633 printf("Light Source : Incandescent\n"); 1634 break; 1635 case 4: 1636 printf("Light Source : Flash\n"); 1637 break; 1638 case 9: 1639 printf("Light Source : Fine weather\n"); 1640 break; 1641 case 11: 1642 printf("Light Source : Shade\n"); 1643 break; 1644 default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs 1645 // If it just says 'unknown' or we don't know it, then 1646 // don't bother showing it - it doesn't add any useful information. 1647 } 1648 1649 if (ImageInfo.MeteringMode){ // 05-jan-2001 vcs 1650 switch(ImageInfo.MeteringMode) { 1651 case 2: 1652 printf("Metering Mode: center weight\n"); 1653 break; 1654 case 3: 1655 printf("Metering Mode: spot\n"); 1656 break; 1657 case 5: 1658 printf("Metering Mode: matrix\n"); 1659 break; 1660 } 1661 } 1662 1663 if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs 1664 switch(ImageInfo.ExposureProgram) { 1665 case 1: 1666 printf("Exposure : Manual\n"); 1667 break; 1668 case 2: 1669 printf("Exposure : program (auto)\n"); 1670 break; 1671 case 3: 1672 printf("Exposure : aperture priority (semi-auto)\n"); 1673 break; 1674 case 4: 1675 printf("Exposure : shutter priority (semi-auto)\n"); 1676 break; 1677 case 5: 1678 printf("Exposure : Creative Program (based towards depth of field)\n"); 1679 break; 1680 case 6: 1681 printf("Exposure : Action program (based towards fast shutter speed)\n"); 1682 break; 1683 case 7: 1684 printf("Exposure : Portrait Mode\n"); 1685 break; 1686 case 8: 1687 printf("Exposure : LandscapeMode \n"); 1688 break; 1689 default: 1690 break; 1691 } 1692 } 1693 switch(ImageInfo.ExposureMode){ 1694 case 0: // Automatic (not worth cluttering up output for) 1695 break; 1696 case 1: printf("Exposure Mode: Manual\n"); 1697 break; 1698 case 2: printf("Exposure Mode: Auto bracketing\n"); 1699 break; 1700 } 1701 1702 if (ImageInfo.DistanceRange) { 1703 printf("Focus range : "); 1704 switch(ImageInfo.DistanceRange) { 1705 case 1: 1706 printf("macro"); 1707 break; 1708 case 2: 1709 printf("close"); 1710 break; 1711 case 3: 1712 printf("distant"); 1713 break; 1714 } 1715 printf("\n"); 1716 } 1717 1718 1719 1720 if (ImageInfo.Process != M_SOF0){ 1721 // don't show it if its the plain old boring 'baseline' process, but do 1722 // show it if its something else, like 'progressive' (used on web sometimes) 1723 int a; 1724 for (a=0;;a++){ 1725 if (a >= (int)PROCESS_TABLE_SIZE){ 1726 // ran off the end of the table. 1727 printf("Jpeg process : Unknown\n"); 1728 break; 1729 } 1730 if (ProcessTable[a].Tag == ImageInfo.Process){ 1731 printf("Jpeg process : %s\n",ProcessTable[a].Desc); 1732 break; 1733 } 1734 } 1735 } 1736 1737 if (ImageInfo.GpsInfoPresent){ 1738 printf("GPS Latitude : %s\n",ImageInfo.GpsLat); 1739 printf("GPS Longitude: %s\n",ImageInfo.GpsLong); 1740 if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt); 1741 } 1742 1743 // Print the comment. Print 'Comment:' for each new line of comment. 1744 if (ImageInfo.Comments[0]){ 1745 int a,c; 1746 printf("Comment : "); 1747 if (!ImageInfo.CommentWidchars){ 1748 for (a=0;a<MAX_COMMENT_SIZE;a++){ 1749 c = ImageInfo.Comments[a]; 1750 if (c == '\0') break; 1751 if (c == '\n'){ 1752 // Do not start a new line if the string ends with a carriage return. 1753 if (ImageInfo.Comments[a+1] != '\0'){ 1754 printf("\nComment : "); 1755 }else{ 1756 printf("\n"); 1757 } 1758 }else{ 1759 putchar(c); 1760 } 1761 } 1762 printf("\n"); 1763 }else{ 1764 printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments); 1765 } 1766 } 1767 if (ImageInfo.ThumbnailOffset){ 1768 printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize); 1769 } else { 1770 printf("NO thumbnail"); 1771 } 1772 } 1773 1774 1775 //-------------------------------------------------------------------------- 1776 // Summarize highlights of image info on one line (suitable for grep-ing) 1777 //-------------------------------------------------------------------------- 1778 void ShowConciseImageInfo(void) 1779 { 1780 printf("\"%s\"",ImageInfo.FileName); 1781 1782 printf(" %dx%d",ImageInfo.Width, ImageInfo.Height); 1783 1784 if (ImageInfo.ExposureTime){ 1785 if (ImageInfo.ExposureTime <= 0.5){ 1786 printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime)); 1787 }else{ 1788 printf(" (%1.1f)",ImageInfo.ExposureTime); 1789 } 1790 } 1791 1792 if (ImageInfo.ApertureFNumber){ 1793 printf(" f/%3.1f",(double)ImageInfo.ApertureFNumber); 1794 } 1795 1796 if (ImageInfo.FocalLength35mmEquiv){ 1797 printf(" f(35)=%dmm",ImageInfo.FocalLength35mmEquiv); 1798 } 1799 1800 if (ImageInfo.FlashUsed >= 0 && ImageInfo.FlashUsed & 1){ 1801 printf(" (flash)"); 1802 } 1803 1804 if (ImageInfo.IsColor == 0){ 1805 printf(" (bw)"); 1806 } 1807 1808 printf("\n"); 1809 } 1810