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