Automated import from //branches/master/...@141567,141567
diff --git a/exif.c b/exif.c
index b273c45..1efe3d9 100644
--- a/exif.c
+++ b/exif.c
@@ -81,143 +81,219 @@
//--------------------------------------------------------------------------
// Describes tag values
-#define TAG_MAKE 0x010F
-#define TAG_MODEL 0x0110
-#define TAG_ORIENTATION 0x0112
-#define TAG_DATETIME 0x0132
-#define TAG_THUMBNAIL_OFFSET 0x0201
-#define TAG_THUMBNAIL_LENGTH 0x0202
-#define TAG_EXPOSURETIME 0x829A
-#define TAG_FNUMBER 0x829D
-#define TAG_EXIF_OFFSET 0x8769
-#define TAG_EXPOSURE_PROGRAM 0x8822
-#define TAG_GPSINFO 0x8825
-#define TAG_ISO_EQUIVALENT 0x8827
-#define TAG_DATETIME_ORIGINAL 0x9003
-#define TAG_DATETIME_DIGITIZED 0x9004
-#define TAG_SHUTTERSPEED 0x9201
-#define TAG_APERTURE 0x9202
-#define TAG_EXPOSURE_BIAS 0x9204
-#define TAG_MAXAPERTURE 0x9205
-#define TAG_SUBJECT_DISTANCE 0x9206
-#define TAG_METERING_MODE 0x9207
-#define TAG_LIGHT_SOURCE 0x9208
-#define TAG_FLASH 0x9209
-#define TAG_FOCALLENGTH 0x920A
-#define TAG_MAKER_NOTE 0x927C
-#define TAG_USERCOMMENT 0x9286
-#define TAG_EXIF_IMAGEWIDTH 0xa002
-#define TAG_EXIF_IMAGELENGTH 0xa003
-#define TAG_INTEROP_OFFSET 0xa005
-#define TAG_FOCALPLANEXRES 0xa20E
-#define TAG_FOCALPLANEUNITS 0xa210
-#define TAG_EXPOSURE_INDEX 0xa215
-#define TAG_EXPOSURE_MODE 0xa402
-#define TAG_WHITEBALANCE 0xa403
-#define TAG_DIGITALZOOMRATIO 0xA404
-#define TAG_FOCALLENGTH_35MM 0xa405
+#define TAG_INTEROP_INDEX 0x0001
+#define TAG_INTEROP_VERSION 0x0002
+#define TAG_IMAGE_WIDTH 0x0100
+#define TAG_IMAGE_LENGTH 0x0101
+#define TAG_BITS_PER_SAMPLE 0x0102
+#define TAG_COMPRESSION 0x0103
+#define TAG_PHOTOMETRIC_INTERP 0x0106
+#define TAG_FILL_ORDER 0x010A
+#define TAG_DOCUMENT_NAME 0x010D
+#define TAG_IMAGE_DESCRIPTION 0x010E
+#define TAG_MAKE 0x010F
+#define TAG_MODEL 0x0110
+#define TAG_SRIP_OFFSET 0x0111
+#define TAG_ORIENTATION 0x0112
+#define TAG_SAMPLES_PER_PIXEL 0x0115
+#define TAG_ROWS_PER_STRIP 0x0116
+#define TAG_STRIP_BYTE_COUNTS 0x0117
+#define TAG_X_RESOLUTION 0x011A
+#define TAG_Y_RESOLUTION 0x011B
+#define TAG_PLANAR_CONFIGURATION 0x011C
+#define TAG_RESOLUTION_UNIT 0x0128
+#define TAG_TRANSFER_FUNCTION 0x012D
+#define TAG_SOFTWARE 0x0131
+#define TAG_DATETIME 0x0132
+#define TAG_ARTIST 0x013B
+#define TAG_WHITE_POINT 0x013E
+#define TAG_PRIMARY_CHROMATICITIES 0x013F
+#define TAG_TRANSFER_RANGE 0x0156
+#define TAG_JPEG_PROC 0x0200
+#define TAG_THUMBNAIL_OFFSET 0x0201
+#define TAG_THUMBNAIL_LENGTH 0x0202
+#define TAG_Y_CB_CR_COEFFICIENTS 0x0211
+#define TAG_Y_CB_CR_SUB_SAMPLING 0x0212
+#define TAG_Y_CB_CR_POSITIONING 0x0213
+#define TAG_REFERENCE_BLACK_WHITE 0x0214
+#define TAG_RELATED_IMAGE_WIDTH 0x1001
+#define TAG_RELATED_IMAGE_LENGTH 0x1002
+#define TAG_CFA_REPEAT_PATTERN_DIM 0x828D
+#define TAG_CFA_PATTERN1 0x828E
+#define TAG_BATTERY_LEVEL 0x828F
+#define TAG_COPYRIGHT 0x8298
+#define TAG_EXPOSURETIME 0x829A
+#define TAG_FNUMBER 0x829D
+#define TAG_IPTC_NAA 0x83BB
+#define TAG_EXIF_OFFSET 0x8769
+#define TAG_INTER_COLOR_PROFILE 0x8773
+#define TAG_EXPOSURE_PROGRAM 0x8822
+#define TAG_SPECTRAL_SENSITIVITY 0x8824
+#define TAG_GPSINFO 0x8825
+#define TAG_ISO_EQUIVALENT 0x8827
+#define TAG_OECF 0x8828
+#define TAG_EXIF_VERSION 0x9000
+#define TAG_DATETIME_ORIGINAL 0x9003
+#define TAG_DATETIME_DIGITIZED 0x9004
+#define TAG_COMPONENTS_CONFIG 0x9101
+#define TAG_CPRS_BITS_PER_PIXEL 0x9102
+#define TAG_SHUTTERSPEED 0x9201
+#define TAG_APERTURE 0x9202
+#define TAG_BRIGHTNESS_VALUE 0x9203
+#define TAG_EXPOSURE_BIAS 0x9204
+#define TAG_MAXAPERTURE 0x9205
+#define TAG_SUBJECT_DISTANCE 0x9206
+#define TAG_METERING_MODE 0x9207
+#define TAG_LIGHT_SOURCE 0x9208
+#define TAG_FLASH 0x9209
+#define TAG_FOCALLENGTH 0x920A
+#define TAG_MAKER_NOTE 0x927C
+#define TAG_USERCOMMENT 0x9286
+#define TAG_SUBSEC_TIME 0x9290
+#define TAG_SUBSEC_TIME_ORIG 0x9291
+#define TAG_SUBSEC_TIME_DIG 0x9292
+
+#define TAG_WINXP_TITLE 0x9c9b // Windows XP - not part of exif standard.
+#define TAG_WINXP_COMMENT 0x9c9c // Windows XP - not part of exif standard.
+#define TAG_WINXP_AUTHOR 0x9c9d // Windows XP - not part of exif standard.
+#define TAG_WINXP_KEYWORDS 0x9c9e // Windows XP - not part of exif standard.
+#define TAG_WINXP_SUBJECT 0x9c9f // Windows XP - not part of exif standard.
+
+#define TAG_FLASH_PIX_VERSION 0xA000
+#define TAG_COLOR_SPACE 0xA001
+#define TAG_EXIF_IMAGEWIDTH 0xA002
+#define TAG_EXIF_IMAGELENGTH 0xA003
+#define TAG_RELATED_AUDIO_FILE 0xA004
+#define TAG_INTEROP_OFFSET 0xA005
+#define TAG_FLASH_ENERGY 0xA20B
+#define TAG_SPATIAL_FREQ_RESP 0xA20C
+#define TAG_FOCAL_PLANE_XRES 0xA20E
+#define TAG_FOCAL_PLANE_YRES 0xA20F
+#define TAG_FOCAL_PLANE_UNITS 0xA210
+#define TAG_SUBJECT_LOCATION 0xA214
+#define TAG_EXPOSURE_INDEX 0xA215
+#define TAG_SENSING_METHOD 0xA217
+#define TAG_FILE_SOURCE 0xA300
+#define TAG_SCENE_TYPE 0xA301
+#define TAG_CFA_PATTERN 0xA302
+#define TAG_CUSTOM_RENDERED 0xA401
+#define TAG_EXPOSURE_MODE 0xA402
+#define TAG_WHITEBALANCE 0xA403
+#define TAG_DIGITALZOOMRATIO 0xA404
+#define TAG_FOCALLENGTH_35MM 0xA405
+#define TAG_SCENE_CAPTURE_TYPE 0xA406
+#define TAG_GAIN_CONTROL 0xA407
+#define TAG_CONTRAST 0xA408
+#define TAG_SATURATION 0xA409
+#define TAG_SHARPNESS 0xA40A
+#define TAG_DISTANCE_RANGE 0xA40C
// TODO: replace the ", 0" values in this table with the correct format, e.g. ", FMT_USHORT"
static const TagTable_t TagTable[] = {
- { 0x001, "InteropIndex", 0, 0},
- { 0x002, "InteropVersion", 0, 0},
- { 0x100, "ImageWidth", FMT_USHORT, 1},
- { 0x101, "ImageLength", FMT_USHORT, 1},
- { 0x102, "BitsPerSample", FMT_USHORT, 3},
- { 0x103, "Compression", FMT_USHORT, 1},
- { 0x106, "PhotometricInterpretation", FMT_USHORT, 1},
- { 0x10A, "FillOrder", 0, 0},
- { 0x10D, "DocumentName", 0, 0},
- { 0x10E, "ImageDescription", 0, 0 },
- { 0x10F, "Make", FMT_STRING, -1},
- { 0x110, "Model", FMT_STRING, -1},
- { 0x111, "StripOffsets", FMT_USHORT, 1},
- { 0x112, "Orientation", FMT_USHORT, 1},
- { 0x115, "SamplesPerPixel", FMT_USHORT, 3},
- { 0x116, "RowsPerStrip", FMT_USHORT, 1},
- { 0x117, "StripByteCounts", FMT_USHORT, 1},
- { 0x11A, "XResolution", FMT_URATIONAL, 1},
- { 0x11B, "YResolution", FMT_URATIONAL, 1},
- { 0x11C, "PlanarConfiguration", FMT_USHORT, 1},
- { 0x128, "ResolutionUnit", FMT_USHORT, 1},
- { 0x12D, "TransferFunction", FMT_USHORT, 768},
- { 0x131, "Software", FMT_STRING, -1},
- { 0x132, "DateTime", FMT_STRING, 20},
- { 0x13B, "Artist", FMT_STRING, -1},
- { 0x13E, "WhitePoint", FMT_SRATIONAL, 2},
- { 0x13F, "PrimaryChromaticities", FMT_SRATIONAL, 6},
- { 0x156, "TransferRange", 0, 0},
- { 0x200, "JPEGProc", 0, 0},
- { 0x201, "ThumbnailOffset", 0, 0},
- { 0x202, "ThumbnailLength", 0, 0},
- { 0x211, "YCbCrCoefficients", FMT_SRATIONAL, 3},
- { 0x212, "YCbCrSubSampling", FMT_USHORT, 2},
- { 0x213, "YCbCrPositioning", FMT_USHORT, 1},
- { 0x214, "ReferenceBlackWhite", FMT_SRATIONAL, 6},
- { 0x1001, "RelatedImageWidth", 0, 0},
- { 0x1002, "RelatedImageLength", 0, 0},
- { 0x828D, "CFARepeatPatternDim", 0, 0},
- { 0x828E, "CFAPattern", 0, 0},
- { 0x828F, "BatteryLevel", 0, 0},
- { 0x8298, "Copyright", FMT_STRING, -1},
- { 0x829A, "ExposureTime", FMT_USHORT, 1},
- { 0x829D, "FNumber", FMT_SRATIONAL, 1},
- { 0x83BB, "IPTC/NAA", 0, 0},
- { 0x8769, "ExifOffset", 0, 0},
- { 0x8773, "InterColorProfile", 0, 0},
- { 0x8822, "ExposureProgram", FMT_SSHORT, 1},
- { 0x8824, "SpectralSensitivity", FMT_STRING, -1},
- { 0x8825, "GPS Dir offset", 0, 0},
- { 0x8827, "ISOSpeedRatings", FMT_SSHORT, -1},
- { 0x8828, "OECF", 0, 0},
- { 0x9000, "ExifVersion", FMT_BYTE, 4},
- { 0x9003, "DateTimeOriginal", FMT_STRING, 20},
- { 0x9004, "DateTimeDigitized", FMT_STRING, 20},
- { 0x9101, "ComponentsConfiguration", FMT_BYTE, 4},
- { 0x9102, "CompressedBitsPerPixel", FMT_SRATIONAL, 1},
- { 0x9201, "ShutterSpeedValue", FMT_SRATIONAL, 1},
- { 0x9202, "ApertureValue", FMT_URATIONAL, 1},
- { 0x9203, "BrightnessValue", FMT_SRATIONAL, 1},
- { 0x9204, "ExposureBiasValue", FMT_SRATIONAL, 1},
- { 0x9205, "MaxApertureValue", FMT_URATIONAL, 1},
- { 0x9206, "SubjectDistance", FMT_URATIONAL, 1},
- { 0x9207, "MeteringMode", FMT_USHORT, 1},
- { 0x9208, "LightSource", FMT_USHORT, 1},
- { 0x9209, "Flash", FMT_USHORT, 1},
- { 0x920A, "FocalLength", FMT_URATIONAL, 1},
- { 0x927C, "MakerNote", FMT_STRING, -1},
- { 0x9286, "UserComment", FMT_STRING, -1},
- { 0x9290, "SubSecTime", FMT_STRING, -1},
- { 0x9291, "SubSecTimeOriginal", FMT_STRING, -1},
- { 0x9292, "SubSecTimeDigitized", FMT_STRING, -1},
- { 0xA000, "FlashPixVersion", FMT_BYTE, 4},
- { 0xA001, "ColorSpace", FMT_USHORT, 1},
- { 0xA002, "ExifImageWidth", 0, 0},
- { 0xA003, "ExifImageLength", 0, 0},
- { 0xA004, "RelatedAudioFile", 0, 0},
- { 0xA005, "InteroperabilityOffset", 0, 0},
- { 0xA20B, "FlashEnergy", FMT_URATIONAL, 1},
- { 0xA20C, "SpatialFrequencyResponse", FMT_STRING, -1},
- { 0xA20E, "FocalPlaneXResolution", FMT_URATIONAL, 1},
- { 0xA20F, "FocalPlaneYResolution", FMT_URATIONAL, 1},
- { 0xA210, "FocalPlaneResolutionUnit", FMT_USHORT, 1},
- { 0xA214, "SubjectLocation", FMT_USHORT, 2},
- { 0xA215, "ExposureIndex", FMT_URATIONAL, 1},
- { 0xA217, "SensingMethod", FMT_USHORT, 1},
- { 0xA300, "FileSource", 0, 1},
- { 0xA301, "SceneType", 0, 1},
- { 0xA301, "CFA Pattern", 0, -1},
- { 0xA401, "CustomRendered", FMT_USHORT, 1},
- { 0xA402, "ExposureMode", FMT_USHORT, 1},
- { 0xA403, "WhiteBalance", FMT_USHORT, 1},
- { 0xA404, "DigitalZoomRatio", FMT_URATIONAL, 1},
- { 0xA405, "FocalLengthIn35mmFilm", FMT_USHORT, 1},
- { 0xA406, "SceneCaptureType", FMT_USHORT, 1},
- { 0xA407, "GainControl", FMT_URATIONAL, 1},
- { 0xA408, "Contrast", FMT_USHORT, 1},
- { 0xA409, "Saturation", FMT_USHORT, 1},
- { 0xA40a, "Sharpness", FMT_USHORT, 1},
- { 0xA40c, "SubjectDistanceRange", FMT_USHORT, 1},
+ { TAG_INTEROP_INDEX, "InteropIndex", 0, 0},
+ { TAG_INTEROP_VERSION, "InteropVersion", 0, 0},
+ { TAG_IMAGE_WIDTH, "ImageWidth", FMT_USHORT, 1},
+ { TAG_IMAGE_LENGTH, "ImageLength", FMT_USHORT, 1},
+ { TAG_BITS_PER_SAMPLE, "BitsPerSample", FMT_USHORT, 3},
+ { TAG_COMPRESSION, "Compression", FMT_USHORT, 1},
+ { TAG_PHOTOMETRIC_INTERP, "PhotometricInterpretation", FMT_USHORT, 1},
+ { TAG_FILL_ORDER, "FillOrder", 0, 0},
+ { TAG_DOCUMENT_NAME, "DocumentName", 0, 0},
+ { TAG_IMAGE_DESCRIPTION, "ImageDescription", 0, 0 },
+ { TAG_MAKE, "Make", FMT_STRING, -1},
+ { TAG_MODEL, "Model", FMT_STRING, -1},
+ { TAG_SRIP_OFFSET, "StripOffsets", FMT_USHORT, 1},
+ { TAG_ORIENTATION, "Orientation", FMT_USHORT, 1},
+ { TAG_SAMPLES_PER_PIXEL, "SamplesPerPixel", FMT_USHORT, 3},
+ { TAG_ROWS_PER_STRIP, "RowsPerStrip", FMT_USHORT, 1},
+ { TAG_STRIP_BYTE_COUNTS, "StripByteCounts", FMT_USHORT, 1},
+ { TAG_X_RESOLUTION, "XResolution", FMT_URATIONAL, 1},
+ { TAG_Y_RESOLUTION, "YResolution", FMT_URATIONAL, 1},
+ { TAG_PLANAR_CONFIGURATION, "PlanarConfiguration", FMT_USHORT, 1},
+ { TAG_RESOLUTION_UNIT, "ResolutionUnit", FMT_USHORT, 1},
+ { TAG_TRANSFER_FUNCTION, "TransferFunction", FMT_USHORT, 768},
+ { TAG_SOFTWARE, "Software", FMT_STRING, -1},
+ { TAG_DATETIME, "DateTime", FMT_STRING, 20},
+ { TAG_ARTIST, "Artist", FMT_STRING, -1},
+ { TAG_WHITE_POINT, "WhitePoint", FMT_SRATIONAL, 2},
+ { TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities", FMT_SRATIONAL, 6},
+ { TAG_TRANSFER_RANGE, "TransferRange", 0, 0},
+ { TAG_JPEG_PROC, "JPEGProc", 0, 0},
+ { TAG_THUMBNAIL_OFFSET, "ThumbnailOffset", 0, 0},
+ { TAG_THUMBNAIL_LENGTH, "ThumbnailLength", 0, 0},
+ { TAG_Y_CB_CR_COEFFICIENTS, "YCbCrCoefficients", FMT_SRATIONAL, 3},
+ { TAG_Y_CB_CR_SUB_SAMPLING, "YCbCrSubSampling", FMT_USHORT, 2},
+ { TAG_Y_CB_CR_POSITIONING, "YCbCrPositioning", FMT_USHORT, 1},
+ { TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite", FMT_SRATIONAL, 6},
+ { TAG_RELATED_IMAGE_WIDTH, "RelatedImageWidth", 0, 0},
+ { TAG_RELATED_IMAGE_LENGTH, "RelatedImageLength", 0, 0},
+ { TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim", 0, 0},
+ { TAG_CFA_PATTERN1, "CFAPattern", 0, 0},
+ { TAG_BATTERY_LEVEL, "BatteryLevel", 0, 0},
+ { TAG_COPYRIGHT, "Copyright", FMT_STRING, -1},
+ { TAG_EXPOSURETIME, "ExposureTime", FMT_USHORT, 1},
+ { TAG_FNUMBER, "FNumber", FMT_SRATIONAL, 1},
+ { TAG_IPTC_NAA, "IPTC/NAA", 0, 0},
+ { TAG_EXIF_OFFSET, "ExifOffset", 0, 0},
+ { TAG_INTER_COLOR_PROFILE, "InterColorProfile", 0, 0},
+ { TAG_EXPOSURE_PROGRAM, "ExposureProgram", FMT_SSHORT, 1},
+ { TAG_SPECTRAL_SENSITIVITY, "SpectralSensitivity", FMT_STRING, -1},
+ { TAG_GPSINFO, "GPS Dir offset", 0, 0},
+ { TAG_ISO_EQUIVALENT, "ISOSpeedRatings", FMT_SSHORT, -1},
+ { TAG_OECF, "OECF", 0, 0},
+ { TAG_EXIF_VERSION, "ExifVersion", FMT_BYTE, 4},
+ { TAG_DATETIME_ORIGINAL, "DateTimeOriginal", FMT_STRING, 20},
+ { TAG_DATETIME_DIGITIZED, "DateTimeDigitized", FMT_STRING, 20},
+ { TAG_COMPONENTS_CONFIG, "ComponentsConfiguration", FMT_BYTE, 4},
+ { TAG_CPRS_BITS_PER_PIXEL, "CompressedBitsPerPixel", FMT_SRATIONAL, 1},
+ { TAG_SHUTTERSPEED, "ShutterSpeedValue", FMT_SRATIONAL, 1},
+ { TAG_APERTURE, "ApertureValue", FMT_URATIONAL, 1},
+ { TAG_BRIGHTNESS_VALUE, "BrightnessValue", FMT_SRATIONAL, 1},
+ { TAG_EXPOSURE_BIAS, "ExposureBiasValue", FMT_SRATIONAL, 1},
+ { TAG_MAXAPERTURE, "MaxApertureValue", FMT_URATIONAL, 1},
+ { TAG_SUBJECT_DISTANCE, "SubjectDistance", FMT_URATIONAL, 1},
+ { TAG_METERING_MODE, "MeteringMode", FMT_USHORT, 1},
+ { TAG_LIGHT_SOURCE, "LightSource", FMT_USHORT, 1},
+ { TAG_FLASH, "Flash", FMT_USHORT, 1},
+ { TAG_FOCALLENGTH, "FocalLength", FMT_URATIONAL, 1},
+ { TAG_MAKER_NOTE, "MakerNote", FMT_STRING, -1},
+ { TAG_USERCOMMENT, "UserComment", FMT_STRING, -1},
+ { TAG_SUBSEC_TIME, "SubSecTime", FMT_STRING, -1},
+ { TAG_SUBSEC_TIME_ORIG, "SubSecTimeOriginal", FMT_STRING, -1},
+ { TAG_SUBSEC_TIME_DIG, "SubSecTimeDigitized", FMT_STRING, -1},
+ { TAG_WINXP_TITLE, "Windows-XP Title", 0, 0},
+ { TAG_WINXP_COMMENT, "Windows-XP comment", 0, 0},
+ { TAG_WINXP_AUTHOR, "Windows-XP author", 0, 0},
+ { TAG_WINXP_KEYWORDS, "Windows-XP keywords", 0, 0},
+ { TAG_WINXP_SUBJECT, "Windows-XP subject", 0, 0},
+ { TAG_FLASH_PIX_VERSION, "FlashPixVersion", FMT_BYTE, 4},
+ { TAG_COLOR_SPACE, "ColorSpace", FMT_USHORT, 1},
+ { TAG_EXIF_IMAGEWIDTH, "ExifImageWidth", 0, 0},
+ { TAG_EXIF_IMAGELENGTH, "ExifImageLength", 0, 0},
+ { TAG_RELATED_AUDIO_FILE, "RelatedAudioFile", 0, 0},
+ { TAG_INTEROP_OFFSET, "InteroperabilityOffset", 0, 0},
+ { TAG_FLASH_ENERGY, "FlashEnergy", FMT_URATIONAL, 1},
+ { TAG_SPATIAL_FREQ_RESP, "SpatialFrequencyResponse", FMT_STRING, -1},
+ { TAG_FOCAL_PLANE_XRES, "FocalPlaneXResolution", FMT_URATIONAL, 1},
+ { TAG_FOCAL_PLANE_YRES, "FocalPlaneYResolution", FMT_URATIONAL, 1},
+ { TAG_FOCAL_PLANE_UNITS, "FocalPlaneResolutionUnit", FMT_USHORT, 1},
+ { TAG_SUBJECT_LOCATION, "SubjectLocation", FMT_USHORT, 2},
+ { TAG_EXPOSURE_INDEX, "ExposureIndex", FMT_URATIONAL, 1},
+ { TAG_SENSING_METHOD, "SensingMethod", FMT_USHORT, 1},
+ { TAG_FILE_SOURCE, "FileSource", 0, 1},
+ { TAG_SCENE_TYPE, "SceneType", 0, 1},
+ { TAG_CFA_PATTERN, "CFA Pattern", 0, -1},
+ { TAG_CUSTOM_RENDERED, "CustomRendered", FMT_USHORT, 1},
+ { TAG_EXPOSURE_MODE, "ExposureMode", FMT_USHORT, 1},
+ { TAG_WHITEBALANCE, "WhiteBalance", FMT_USHORT, 1},
+ { TAG_DIGITALZOOMRATIO, "DigitalZoomRatio", FMT_URATIONAL, 1},
+ { TAG_FOCALLENGTH_35MM, "FocalLengthIn35mmFilm", FMT_USHORT, 1},
+ { TAG_SCENE_CAPTURE_TYPE, "SceneCaptureType", FMT_USHORT, 1},
+ { TAG_GAIN_CONTROL, "GainControl", FMT_URATIONAL, 1},
+ { TAG_CONTRAST, "Contrast", FMT_USHORT, 1},
+ { TAG_SATURATION, "Saturation", FMT_USHORT, 1},
+ { TAG_SHARPNESS, "Sharpness", FMT_USHORT, 1},
+ { TAG_DISTANCE_RANGE, "SubjectDistanceRange", FMT_USHORT, 1},
} ;
#define TAG_TABLE_SIZE (sizeof(TagTable) / sizeof(TagTable_t))
@@ -308,7 +384,7 @@
{
int s,n;
- for(n=0;n<20;n++){
+ for(n=0;n<16;n++){
switch(Format){
case FMT_SBYTE:
case FMT_BYTE: printf("%02x",*(uchar *)ValuePtr); s=1; break;
@@ -334,7 +410,7 @@
ValuePtr = (void *)((char *)ValuePtr + s);
}
- if (n >= 20) printf("...");
+ if (n >= 16) printf("...");
}
@@ -414,12 +490,12 @@
// Version 1.3 of jhead would truncate a bit too much.
// This also caught later on as well.
}else{
- ErrNonfatal("Illegally sized directory",0,0);
+ ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0);
return;
}
}
if (DumpExifMap){
- printf("Map: %05d-%05d: Directory\n",DirStart-OffsetBase, DirEnd+4-OffsetBase);
+ printf("Map: %05d-%05d: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
}
@@ -575,9 +651,28 @@
(char *)ValuePtr - (char *)OffsetBase;
break;
+ case TAG_WINXP_COMMENT:
+ if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
+ // Already have a comment (probably windows comment), skip this one.
+ if (ShowTags) printf("Windows XP commend and other comment in header\n");
+ break; // Already have a windows comment, skip this one.
+ }
+
+ if (ByteCount > 1){
+ if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
+ memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
+ ImageInfo.CommentWidchars = ByteCount/2;
+ }
+ break;
case TAG_USERCOMMENT:
- // Olympus has this padded with trailing spaces. Remove these first.
+ if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
+ // Already have a comment (probably windows comment), skip this one.
+ if (ShowTags) printf("Multiple comments in exif header\n");
+ break; // Already have a windows comment, skip this one.
+ }
+
+ // Comment is often padded with trailing spaces. Remove these first.
for (a=ByteCount;;){
a--;
if ((ValuePtr)[a] == ' '){
@@ -598,9 +693,8 @@
break;
}
}
-
}else{
- strncpy(ImageInfo.Comments, (char *)ValuePtr, 199);
+ strncpy(ImageInfo.Comments, (char *)ValuePtr, MAX_COMMENT_SIZE-1);
}
break;
@@ -679,11 +773,11 @@
if (ExifImageWidth < a) ExifImageWidth = a;
break;
- case TAG_FOCALPLANEXRES:
+ case TAG_FOCAL_PLANE_XRES:
FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
break;
- case TAG_FOCALPLANEUNITS:
+ case TAG_FOCAL_PLANE_UNITS:
switch((int)ConvertAnyFormat(ValuePtr, Format)){
case 1: FocalplaneUnits = 25.4; break; // inch
case 2:
@@ -791,6 +885,12 @@
// computing it from sensor geometry and actual focal length.
ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
break;
+
+ case TAG_DISTANCE_RANGE:
+ // Three possible standard values:
+ // 1 = macro, 2 = close, 3 = distant
+ ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
+ break;
}
}
@@ -1073,12 +1173,12 @@
unsigned short NumEntries;
int DataWriteIndex;
int DirIndex;
- int ThumbnailOffsetDirIndex = 0;
+ int DirContinuation = 0;
#ifdef SUPERDEBUG
LOGE("create_EXIF %d exif elements, %d gps elements", exifTagCount, gpsTagCount);
#endif
-
+
MotorolaOrder = 0;
memcpy(Buffer+2, "Exif\0\0II",8);
@@ -1097,7 +1197,8 @@
Put16u(Buffer+DirIndex, NumEntries); // Number of entries
DirIndex += 2;
- // Entries go here....
+
+ // Entries go here...
{
// Date/time entry
char* dateTime = NULL;
@@ -1142,8 +1243,7 @@
&DirIndex,
&DataWriteIndex);
}
- }
- {
+
if (gpsTagCount) {
// Link to gps dir entry
writeExifTagAndData(TAG_GPSINFO,
@@ -1161,7 +1261,7 @@
if (gpsTagCount) {
exifDirPtr += 2 + gpsTagCount*12 + 4;
}
- ThumbnailOffsetDirIndex = DirIndex;
+ DirContinuation = DirIndex;
writeExifTagAndData(TAG_EXIF_OFFSET,
FMT_ULONG,
1,
@@ -1173,10 +1273,11 @@
}
// End of directory - contains optional link to continued directory.
- Put32u(Buffer+DirIndex, 0);
+ DirContinuation = DirIndex;
printf("Ending Exif section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
}
+
// GPS Section
if (gpsTagCount) {
DirIndex = DataWriteIndex;
@@ -1216,9 +1317,8 @@
}
{
- // Now that we know where the Thumbnail section is written, we have to go
- // back and "poke" the address to point here.
- Put32u(Buffer+ThumbnailOffsetDirIndex + 8, DataWriteIndex-8); // Pointer or value.
+ //Continuation which links to this directory;
+ Put32u(Buffer+DirContinuation, DataWriteIndex-8);
printf("Starting Thumbnail section DirIndex = %d", DirIndex);
DirIndex = DataWriteIndex;
@@ -1256,6 +1356,7 @@
printf("Ending Thumbnail section DirIndex = %d DataWriteIndex %d", DirIndex, DataWriteIndex);
}
+
Buffer[0] = (unsigned char)(DataWriteIndex >> 8);
Buffer[1] = (unsigned char)DataWriteIndex;
@@ -1594,6 +1695,23 @@
break;
}
+ if (ImageInfo.DistanceRange) {
+ printf("Focus range : ");
+ switch(ImageInfo.DistanceRange) {
+ case 1:
+ printf("macro");
+ break;
+ case 2:
+ printf("close");
+ break;
+ case 3:
+ printf("distant");
+ break;
+ }
+ printf("\n");
+ }
+
+
if (ImageInfo.Process != M_SOF0){
// don't show it if its the plain old boring 'baseline' process, but do
@@ -1622,21 +1740,25 @@
if (ImageInfo.Comments[0]){
int a,c;
printf("Comment : ");
- for (a=0;a<MAX_COMMENT;a++){
- c = ImageInfo.Comments[a];
- if (c == '\0') break;
- if (c == '\n'){
- // Do not start a new line if the string ends with a carriage return.
- if (ImageInfo.Comments[a+1] != '\0'){
- printf("\nComment : ");
+ if (!ImageInfo.CommentWidchars){
+ for (a=0;a<MAX_COMMENT_SIZE;a++){
+ c = ImageInfo.Comments[a];
+ if (c == '\0') break;
+ if (c == '\n'){
+ // Do not start a new line if the string ends with a carriage return.
+ if (ImageInfo.Comments[a+1] != '\0'){
+ printf("\nComment : ");
+ }else{
+ printf("\n");
+ }
}else{
- printf("\n");
+ putchar(c);
}
- }else{
- putchar(c);
}
+ printf("\n");
+ }else{
+ printf("%.*ls\n", ImageInfo.CommentWidchars, (wchar_t *)ImageInfo.Comments);
}
- printf("\n");
}
if (ImageInfo.ThumbnailOffset){
printf("Map: %05d-%05d: Thumbnail\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize);
diff --git a/gpsinfo.c b/gpsinfo.c
index 2a25368..18b9d07 100644
--- a/gpsinfo.c
+++ b/gpsinfo.c
@@ -205,21 +205,22 @@
ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0);
}
strcpy(FmtString, "%0.0fd %0.0fm %0.0fs");
-
for (a=0;a<3;a++){
int den, digits;
den = Get32s(ValuePtr+4+a*ComponentSize);
digits = 0;
- while (den > 1){
+ while (den > 1 && digits <= 6){
den = den / 10;
digits += 1;
}
+ if (digits > 6) digits = 6;
FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0));
FmtString[3+a*7] = (char)('0'+digits);
Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
}
+
sprintf(TempString, FmtString, Values[0], Values[1], Values[2]);
if (Tag == TAG_GPS_LAT){
@@ -244,7 +245,8 @@
break;
case TAG_GPS_ALT:
- sprintf(ImageInfo.GpsAlt + 1, "%dm", Get32s(ValuePtr));
+ sprintf(ImageInfo.GpsAlt + 1, "%.2fm",
+ ConvertAnyFormat(ValuePtr, Format));
break;
}
@@ -256,7 +258,7 @@
// Show unknown tag
printf(" Illegal GPS tag %04x=", Tag);
}
-
+
switch(Format){
case FMT_UNDEFINED:
// Undefined is typically an ascii string.
diff --git a/iptc.c b/iptc.c
index 37f7127..93d6f34 100644
--- a/iptc.c
+++ b/iptc.c
@@ -1,9 +1,10 @@
//--------------------------------------------------------------------------
-// Process IPTC data.
+// Process IPTC data and XMP data.
//--------------------------------------------------------------------------
#include "jhead.h"
-// Supported IPTC entry types
+// IPTC entry types known to Jhead (there's many more defined)
+#define IPTC_RECORD_VERSION 0x00
#define IPTC_SUPLEMENTAL_CATEGORIES 0x14
#define IPTC_KEYWORDS 0x19
#define IPTC_CAPTION 0x78
@@ -25,6 +26,9 @@
#define IPTC_COPYRIGHT 0x0A
#define IPTC_COUNTRY_CODE 0x64
#define IPTC_REFERENCE_SERVICE 0x2D
+#define IPTC_TIME_CREATED 0x3C
+#define IPTC_SUB_LOCATION 0x5C
+#define IPTC_IMAGE_TYPE 0x82
//--------------------------------------------------------------------------
// Process and display IPTC marker.
@@ -86,21 +90,21 @@
// Get length (from motorola format)
//length = (*pos << 24) | (*(pos+1) << 16) | (*(pos+2) << 8) | *(pos+3);
- pos += sizeof(long); // move data pointer to the next field
+ pos += 4; // move data pointer to the next field
printf("======= IPTC data: =======\n");
// Now read IPTC data
while (pos < (Data + itemlen-5)) {
short signature;
- char type = 0;
+ unsigned char type = 0;
short length = 0;
char * description = NULL;
if (pos+5 > maxpos) goto corrupt;
signature = (*pos << 8) + (*(pos+1));
- pos += sizeof(short);
+ pos += 2;
if (signature != 0x1C02){
break;
@@ -108,42 +112,50 @@
type = *pos++;
length = (*pos << 8) + (*(pos+1));
- pos += sizeof(short); // Skip tag length
+ pos += 2; // Skip tag length
if (pos+length > maxpos) goto corrupt;
// Process tag here
switch (type) {
+ case IPTC_RECORD_VERSION:
+ printf("Record vers. : %d\n", (*pos << 8) + (*(pos+1)));
+ break;
+
case IPTC_SUPLEMENTAL_CATEGORIES: description = "SuplementalCategories"; break;
case IPTC_KEYWORDS: description = "Keywords"; break;
case IPTC_CAPTION: description = "Caption"; break;
case IPTC_AUTHOR: description = "Author"; break;
case IPTC_HEADLINE: description = "Headline"; break;
- case IPTC_SPECIAL_INSTRUCTIONS: description = "Spec.Instr."; break;
+ case IPTC_SPECIAL_INSTRUCTIONS: description = "Spec. Instr."; break;
case IPTC_CATEGORY: description = "Category"; break;
case IPTC_BYLINE: description = "Byline"; break;
- case IPTC_BYLINE_TITLE: description = "BylineTitle"; break;
+ case IPTC_BYLINE_TITLE: description = "Byline Title"; break;
case IPTC_CREDIT: description = "Credit"; break;
case IPTC_SOURCE: description = "Source"; break;
case IPTC_COPYRIGHT_NOTICE: description = "(C)Notice"; break;
- case IPTC_OBJECT_NAME: description = "ObjectName"; break;
+ case IPTC_OBJECT_NAME: description = "Object Name"; break;
case IPTC_CITY: description = "City"; break;
case IPTC_STATE: description = "State"; break;
case IPTC_COUNTRY: description = "Country"; break;
case IPTC_TRANSMISSION_REFERENCE: description = "OriginalTransmissionReference"; break;
case IPTC_DATE: description = "DateCreated"; break;
case IPTC_COPYRIGHT: description = "(C)Flag"; break;
- case IPTC_REFERENCE_SERVICE: description = "CountryCode"; break;
- case IPTC_COUNTRY_CODE: description = "Ref.Service"; break;
+ case IPTC_REFERENCE_SERVICE: description = "Country Code"; break;
+ case IPTC_COUNTRY_CODE: description = "Ref. Service"; break;
+ case IPTC_TIME_CREATED: description = "Time Created"; break;
+ case IPTC_SUB_LOCATION: description = "Sub Location"; break;
+ case IPTC_IMAGE_TYPE: description = "Image type"; break;
+
default:
if (ShowTags){
- printf("Unrecognised IPTC tag: 0x%02x \n", type);
+ printf("Unrecognised IPTC tag: %d\n", type );
}
break;
}
if (description != NULL) {
char TempBuf[32];
memset(TempBuf, 0, sizeof(TempBuf));
- memset(TempBuf, ' ', 13);
+ memset(TempBuf, ' ', 14);
memcpy(TempBuf, description, strlen(description));
strcat(TempBuf, ":");
printf("%s %*.*s\n", TempBuf, length, length, pos);
@@ -154,3 +166,40 @@
corrupt:
ErrNonfatal("Pointer corruption in IPTC\n",0,0);
}
+
+
+
+//--------------------------------------------------------------------------
+// Dump contents of XMP section
+//--------------------------------------------------------------------------
+void ShowXmp(Section_t XmpSection)
+{
+ unsigned char * Data;
+ char OutLine[101];
+ int OutLineChars;
+ int NonBlank;
+ unsigned a;
+ NonBlank = 0;
+ Data = XmpSection.Data;
+ OutLineChars = 0;
+
+
+ for (a=0;a<XmpSection.Size;a++){
+ if (Data[a] >= 32 && Data[a] < 128){
+ OutLine[OutLineChars++] = Data[a];
+ if (Data[a] != ' ') NonBlank |= 1;
+ }else{
+ if (Data[a] != '\n'){
+ OutLine[OutLineChars++] = '?';
+ }
+ }
+ if (Data[a] == '\n' || OutLineChars >= 100){
+ OutLine[OutLineChars] = 0;
+ if (NonBlank){
+ puts(OutLine);
+ }
+ NonBlank = (NonBlank & 1) << 1;
+ OutLineChars = 0;
+ }
+ }
+}
diff --git a/jhead.c b/jhead.c
index 036eb69..88bd527 100644
--- a/jhead.c
+++ b/jhead.c
@@ -2,12 +2,12 @@
// Program to pull the information out of various types of EXIF digital
// camera files and show it in a reasonably consistent way
//
-// Version 2.71
+// Version 2.86
//
// Compiling under Windows:
// Make sure you have Microsoft's compiler on the path, then run make.bat
//
-// Dec 1999 - Feb 2007
+// Dec 1999 - Mar 2009
//
// by Matthias Wandel www.sentex.net/~mwandel
//--------------------------------------------------------------------------
@@ -16,11 +16,11 @@
#include <sys/stat.h>
#include <utils/Log.h>
-#define JHEAD_VERSION "2.71"
+#define JHEAD_VERSION "2.87"
// This #define turns on features that are too very specific to
// how I organize my photos. Best to ignore everything inside #ifdef MATTHIAS
-#define MATTHIAS
+//#define MATTHIAS
#ifdef _WIN32
#include <io.h>
@@ -36,8 +36,10 @@
//--------------------------------------------------------------------------
// Command line options flags
static int TrimExif = FALSE; // Cut off exif beyond interesting data.
-static int RenameToDate = FALSE;
+static int RenameToDate = 0; // 1=rename, 2=rename all.
+#ifdef _WIN32
static int RenameAssociatedFiles = FALSE;
+#endif
static char * strftime_args = NULL; // Format for new file name.
static int Exif2FileTime = FALSE;
static int DoModify = FALSE;
@@ -60,6 +62,7 @@
static int DeleteComments = FALSE;
static int DeleteExif = FALSE;
static int DeleteIptc = FALSE;
+static int DeleteXmp = FALSE;
static int DeleteUnknown = FALSE;
static char * ThumbSaveName = NULL; // If not NULL, use this string to make up
// the filename to store the thumbnail to.
@@ -86,7 +89,6 @@
// (file name, file size, file date)
-
#ifdef MATTHIAS
// This #ifdef to take out less than elegant stuff for editing
// the comments in a JPEG. The programs rdjpgcom and wrjpgcom
@@ -103,7 +105,6 @@
void ErrFatal(char * msg)
{
LOGE("Error : %s\n", msg);
- fprintf(stderr,"Error : %s\n", msg);
if (CurrentFile) fprintf(stderr,"in file '%s'\n",CurrentFile);
exit(EXIT_FAILURE);
}
@@ -118,7 +119,7 @@
LOGE(msg, a1, a2);
if (SupressNonFatalErrors) return;
- fprintf(stderr,"Nonfatal Error : ");
+ fprintf(stderr,"\nNonfatal Error : ");
if (CurrentFile) fprintf(stderr,"'%s' ",CurrentFile);
fprintf(stderr, msg, a1, a2);
fprintf(stderr, "\n");
@@ -142,7 +143,7 @@
{
FILE * file;
int a;
- char QuotedPath[PATH_MAX];
+ char QuotedPath[PATH_MAX+10];
file = fopen(TempFileName, "w");
if (file == NULL){
@@ -156,8 +157,8 @@
fflush(stdout); // So logs are contiguous.
{
- char * Editor;
- Editor = getenv("EDITOR");
+ char * Editor;
+ Editor = getenv("EDITOR");
if (Editor == NULL){
#ifdef _WIN32
Editor = "notepad";
@@ -165,11 +166,12 @@
Editor = "vi";
#endif
}
+ if (strlen(Editor) > PATH_MAX) ErrFatal("env too long");
sprintf(QuotedPath, "%s \"%s\"",Editor, TempFileName);
a = system(QuotedPath);
}
-
+
if (a != 0){
perror("Editor failed to launch");
exit(-1);
@@ -245,11 +247,11 @@
// Overwrite old comment of same tag with new one.
if (!memcmp(Line, AddComment, l+1)){
TagExists = TRUE;
- strcpy(Line, AddComment);
+ strncpy(Line, AddComment, sizeof(Line));
Modified = TRUE;
}
}
- strcat(OutComment, Line);
+ strncat(OutComment, Line, MAX_COMMENT_SIZE-5-strlen(OutComment));
strcat(OutComment, "\n");
break;
}
@@ -264,7 +266,7 @@
}
if (AddComment && TagExists == FALSE){
- strcat(OutComment, AddComment);
+ strncat(OutComment, AddComment, MAX_COMMENT_SIZE-5-strlen(OutComment));
strcat(OutComment, "\n");
Modified = TRUE;
}
@@ -273,7 +275,7 @@
// Scan date is not in the file yet, and it doesn't have one built in. Add it.
char Temp[30];
sprintf(Temp, "scan_date=%s", ctime(&ImageInfo.FileDateTime));
- strcat(OutComment, Temp);
+ strncat(OutComment, Temp, MAX_COMMENT_SIZE-5-strlen(OutComment));
Modified = TRUE;
}
return Modified;
@@ -283,7 +285,7 @@
//--------------------------------------------------------------------------
static int AutoResizeCmdStuff(void)
{
- static char CommandString[500];
+ static char CommandString[PATH_MAX+1];
double scale;
ApplyCommand = CommandString;
@@ -307,37 +309,82 @@
//--------------------------------------------------------------------------
+// Escape an argument such that it is interpreted literally by the shell
+// (returns the number of written characters)
+//--------------------------------------------------------------------------
+static int shellescape(char* to, const char* from)
+{
+ int i, j;
+ i = j = 0;
+
+ // Enclosing characters in double quotes preserves the literal value of
+ // all characters within the quotes, with the exception of $, `, and \.
+ to[j++] = '"';
+ while(from[i])
+ {
+#ifdef _WIN32
+ // Under WIN32, there isn't really anything dangerous you can do with
+ // escape characters, plus windows users aren't as sercurity paranoid.
+ // Hence, no need to do fancy escaping.
+ to[j++] = from[i++];
+#else
+ switch(from[i]) {
+ case '"':
+ case '$':
+ case '`':
+ case '\\':
+ to[j++] = '\\';
+ // Fallthru...
+ default:
+ to[j++] = from[i++];
+ }
+#endif
+ if (j >= PATH_MAX) ErrFatal("max path exceeded");
+ }
+ to[j++] = '"';
+ return j;
+}
+
+
+//--------------------------------------------------------------------------
// Apply the specified command to the JPEG file.
//--------------------------------------------------------------------------
static void DoCommand(const char * FileName, int ShowIt)
{
int a,e;
- char ExecString[400];
- char TempName[200];
+ char ExecString[PATH_MAX*3];
+ char TempName[PATH_MAX+10];
int TempUsed = FALSE;
e = 0;
- // Make a temporary file in the destination directory by changing last char.
- strcpy(TempName, FileName);
- a = strlen(TempName)-1;
- TempName[a] = (char)(TempName[a] == 't' ? 'z' : 't');
+ // Generate an unused temporary file name in the destination directory
+ // (a is the number of characters to copy from FileName)
+ a = strlen(FileName)-1;
+ while(a > 0 && FileName[a-1] != SLASH) a--;
+ memcpy(TempName, FileName, a);
+ strcpy(TempName+a, "XXXXXX");
+ mktemp(TempName);
+ if(!TempName[0]) {
+ ErrFatal("Cannot find available temporary file name");
+ }
+
+
// Build the exec string. &i and &o in the exec string get replaced by input and output files.
for (a=0;;a++){
if (ApplyCommand[a] == '&'){
if (ApplyCommand[a+1] == 'i'){
// Input file.
- e += sprintf(ExecString+e, "\"%s\"",FileName);
+ e += shellescape(ExecString+e, FileName);
a += 1;
continue;
}
if (ApplyCommand[a+1] == 'o'){
// Needs an output file distinct from the input file.
- e += sprintf(ExecString+e, "\"%s\"",TempName);
+ e += shellescape(ExecString+e, TempName);
a += 1;
TempUsed = TRUE;
- unlink(TempName);// Remove any pre-existing temp file
continue;
}
}
@@ -421,7 +468,7 @@
strncat(OutFileName, OrigName, PATH_MAX);
strncat(OutFileName, Subst+2, PATH_MAX);
}else{
- strcpy(OutFileName, NamePattern);
+ strncpy(OutFileName, NamePattern, PATH_MAX);
}
}
@@ -435,8 +482,8 @@
int a;
int PathLen;
int ExtPos;
- char FilePattern[_MAX_PATH];
- char NewName[_MAX_PATH];
+ char FilePattern[_MAX_PATH+1];
+ char NewName[_MAX_PATH+1];
struct _finddata_t finddata;
long find_handle;
@@ -448,7 +495,7 @@
FilePattern[ExtPos] = '*';
FilePattern[ExtPos+1] = '\0';
- for(PathLen = strlen(FileName);FileName[PathLen-1] != '\\';){
+ for(PathLen = strlen(FileName);FileName[PathLen-1] != SLASH;){
if (--PathLen == 0) break;
}
@@ -462,13 +509,13 @@
if (!memcmp(finddata.name, "..",3)) goto next_file;
if (finddata.attrib & _A_SUBDIR) goto next_file;
- strcpy(FilePattern+PathLen, finddata.name); // full name with path
+ strncpy(FilePattern+PathLen, finddata.name, PATH_MAX-PathLen); // full name with path
strcpy(NewName, NewBaseName);
for(a = strlen(finddata.name);finddata.name[a] != '.';){
if (--a == 0) goto next_file;
}
- strcat(NewName, finddata.name+a); // add extension to new name
+ strncat(NewName, finddata.name+a, _MAX_PATH-strlen(NewName)); // add extension to new name
if (rename(FilePattern, NewName) == 0){
if (!Quiet){
@@ -490,14 +537,17 @@
{
int NumAlpha = 0;
int NumDigit = 0;
- int PrefixPart = 0;
- int ExtensionPart = strlen(FileName);
+ int PrefixPart = 0; // Where the actual filename starts.
+ int ExtensionPart; // Where the file extension starts.
int a;
struct tm tm;
char NewBaseName[PATH_MAX*2];
+ int AddLetter = 0;
+ char NewName[PATH_MAX+2];
+ ExtensionPart = strlen(FileName);
for (a=0;FileName[a];a++){
- if (FileName[a] == '/' || FileName[a] == '\\'){
+ if (FileName[a] == SLASH){
// Don't count path component.
NumAlpha = 0;
NumDigit = 0;
@@ -524,7 +574,7 @@
}
- strcpy(NewBaseName, FileName); // Get path component of name.
+ strncpy(NewBaseName, FileName, PATH_MAX); // Get path component of name.
if (strftime_args){
// Complicated scheme for flexibility. Just pass the args to strftime.
@@ -559,14 +609,17 @@
}else if (pattern[a] == 'i'){
if (ppos >= 0 && a<ppos+4){
// Replace this part with a number.
- char pat[8];
- char num[16];
+ char pat[8], num[16];
+ int l,nl;
memcpy(pat, pattern+ppos, 4);
pat[a-ppos] = 'd'; // Replace 'i' with 'd' for '%d'
pat[a-ppos+1] = '\0';
sprintf(num, pat, FileSequence); // let printf do the number formatting.
- memmove(pattern+ppos+strlen(num), pattern+a+1, strlen(pattern+a+1)+1);
- memcpy(pattern+ppos, num, strlen(num));
+ nl = strlen(num);
+ l = strlen(pattern+a+1);
+ if (ppos+nl+l+1 >= PATH_MAX) ErrFatal("str overflow");
+ memmove(pattern+ppos+nl, pattern+a+1, l+1);
+ memcpy(pattern+ppos, num, nl);
break;
}
}else if (!isdigit(pattern[a])){
@@ -574,16 +627,19 @@
}
}
}
-
- strftime(NewBaseName+PrefixPart, PATH_MAX, pattern, &tm);
+ strftime(NewName, PATH_MAX, pattern, &tm);
}else{
// My favourite scheme.
- sprintf(NewBaseName+PrefixPart, "%02d%02d-%02d%02d%02d",
+ sprintf(NewName, "%02d%02d-%02d%02d%02d",
tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
+ NewBaseName[PrefixPart] = 0;
+ CatPath(NewBaseName, NewName);
+
+ AddLetter = isdigit(NewBaseName[strlen(NewBaseName)-1]);
for (a=0;;a++){
- char NewName[PATH_MAX];
+ char NewName[PATH_MAX+10];
char NameExtra[3];
struct stat dummy;
@@ -593,10 +649,10 @@
// it. This to avoid using a separator character - this because any good separator
// is before the '.' in ascii, and so sorting the names would put the later name before
// the name without suffix, causing the pictures to more likely be out of order.
- if (isdigit(NewBaseName[strlen(NewBaseName)-1])){
- NameExtra[0] = (char)('a'-1+a); // Try a,b,c,d... for suffix if it ends in a letter.
+ if (AddLetter){
+ NameExtra[0] = (char)('a'-1+a); // Try a,b,c,d... for suffix if it ends in a number.
}else{
- NameExtra[0] = (char)('0'-1+a); // Try 1,2,3,4... for suffix if it ends in a char.
+ NameExtra[0] = (char)('0'-1+a); // Try 0,1,2,3... for suffix if it ends in a latter.
}
NameExtra[1] = 0;
}else{
@@ -607,6 +663,11 @@
if (!strcmp(FileName, NewName)) break; // Skip if its already this name.
+ if (!EnsurePathExists(NewBaseName)){
+ break;
+ }
+
+
if (stat(NewName, &dummy)){
// This name does not pre-exist.
if (rename(FileName, NewName) == 0){
@@ -621,9 +682,10 @@
printf("Error: Couldn't rename '%s' to '%s'\n",FileName, NewName);
}
break;
+
}
- if (a >= 9){
+ if (a > 25 || (!AddLetter && a > 9)){
printf("Possible new names for for '%s' already exist\n",FileName);
break;
}
@@ -645,7 +707,7 @@
ErrFatal("Orientation screwup");
}
- sprintf(RotateCommand, "jpegtran -%s -outfile &o &i", Argument);
+ sprintf(RotateCommand, "jpegtran -trim -%s -outfile &o &i", Argument);
ApplyCommand = RotateCommand;
DoCommand(FileName, FALSE);
ApplyCommand = NULL;
@@ -656,15 +718,15 @@
ImageInfo.ThumbnailAtEnd){
// Must have a thumbnail that exists and is modifieable.
- char ThumbTempName_in[PATH_MAX+4];
- char ThumbTempName_out[PATH_MAX+4];
+ char ThumbTempName_in[PATH_MAX+5];
+ char ThumbTempName_out[PATH_MAX+5];
strcpy(ThumbTempName_in, FileName);
strcat(ThumbTempName_in, ".thi");
strcpy(ThumbTempName_out, FileName);
strcat(ThumbTempName_out, ".tho");
SaveThumbnail(ThumbTempName_in);
- sprintf(RotateCommand,"jpegtran -%s -outfile \"%s\" \"%s\"",
+ sprintf(RotateCommand,"jpegtran -trim -%s -outfile \"%s\" \"%s\"",
Argument, ThumbTempName_out, ThumbTempName_in);
if (system(RotateCommand) == 0){
@@ -710,7 +772,14 @@
void ProcessFile(const char * FileName)
{
int Modified = FALSE;
- ReadMode_t ReadMode = READ_METADATA;
+ ReadMode_t ReadMode;
+
+ if (strlen(FileName) >= PATH_MAX-1){
+ // Protect against buffer overruns in strcpy / strcat's on filename
+ ErrFatal("filename too long");
+ }
+
+ ReadMode = READ_METADATA;
CurrentFile = FileName;
FilesMatched = 1;
@@ -874,7 +943,7 @@
EditComment || CommentInsertfileName || CommentInsertLiteral){
Section_t * CommentSec;
- char Comment[1001];
+ char Comment[MAX_COMMENT_SIZE+1];
int CommentSize;
CommentSec = FindSection(M_COM);
@@ -890,9 +959,9 @@
}
CommentSize = CommentSec->Size-2;
- if (CommentSize > 1000){
- fprintf(stderr, "Truncating comment at 1000 chars\n");
- CommentSize = 1000;
+ if (CommentSize > MAX_COMMENT_SIZE){
+ fprintf(stderr, "Truncating comment at %d chars\n",MAX_COMMENT_SIZE);
+ CommentSize = MAX_COMMENT_SIZE;
}
if (CommentInsertfileName){
@@ -914,11 +983,11 @@
if (CommentSize < 0) CommentSize = 0;
}
}else if (CommentInsertLiteral){
- strncpy(Comment, CommentInsertLiteral, 1000);
+ strncpy(Comment, CommentInsertLiteral, MAX_COMMENT_SIZE);
CommentSize = strlen(Comment);
}else{
#ifdef MATTHIAS
- char CommentZt[1001];
+ char CommentZt[MAX_COMMENT_SIZE+1];
memcpy(CommentZt, (char *)CommentSec->Data+2, CommentSize);
CommentZt[CommentSize] = '\0';
if (ModifyDescriptComment(Comment, CommentZt)){
@@ -930,7 +999,7 @@
memcpy(Comment, (char *)CommentSec->Data+2, CommentSize);
#endif
{
- char EditFileName[PATH_MAX+4];
+ char EditFileName[PATH_MAX+5];
strcpy(EditFileName, FileName);
strcat(EditFileName, ".txt");
@@ -1027,6 +1096,7 @@
Pointer = ExifSection->Data+ImageInfo.DateTimeOffsets[a]+8;
memcpy(Pointer, TempBuf, 19);
}
+ memcpy(ImageInfo.DateTime, TempBuf, 19);
Modified = TRUE;
}else{
@@ -1043,13 +1113,16 @@
if (DeleteIptc){
if (RemoveSectionType(M_IPTC)) Modified = TRUE;
}
+ if (DeleteXmp){
+ if (RemoveSectionType(M_XMP)) Modified = TRUE;
+ }
if (DeleteUnknown){
if (RemoveUnknownSections()) Modified = TRUE;
}
if (Modified){
- char BackupName[400];
+ char BackupName[PATH_MAX+5];
struct stat buf;
if (!Quiet) printf("Modified: %s\n",FileName);
@@ -1133,7 +1206,7 @@
static void Usage (void)
{
printf("Jhead is a program for manipulating settings and thumnails in Exif jpeg headers\n"
- "used by most Digital Cameras. v"JHEAD_VERSION" Matthias Wandel, April 29 2006.\n"
+ "used by most Digital Cameras. v"JHEAD_VERSION" Matthias Wandel, Mar 02 2009.\n"
"http://www.sentex.net/~mwandel/jhead\n"
"\n");
@@ -1148,6 +1221,7 @@
" -dc Delete comment field (as left by progs like Photoshop & Compupic)\n"
" -de Strip Exif section (smaller JPEG file, but lose digicam info)\n"
" -di Delete IPTC section (from Photoshop, or Picasa)\n"
+ " -dx Deletex XMP section\n"
" -du Delete non image sections except for Exif and comment sections\n"
" -purejpg Strip all unnecessary data from jpeg (combines -dc -de and -du)\n"
" -mkexif Create new minimal exif section (overwrites pre-existing exif)\n"
@@ -1177,6 +1251,8 @@
" The '.jpg' is automatically added to the end of the name. If the\n"
" destination name already exists, a letter or digit is added to \n"
" the end of the name to make it unique.\n"
+ " The new name may include a path as part of the name. If this path\n"
+ " does not exist, it will be created\n"
" -nf[format-string]\n"
" Same as -n, but rename regardless of original name\n"
" -a (Windows only) Rename files with same name but different extension\n"
@@ -1195,8 +1271,8 @@
" To deal with different months and years having different numbers of\n"
" days, a simple date-month-year offset would result in unexpected\n"
" results. Instead, the difference is specified as desired date\n"
- " minus original date. Date is specified as yyyy:mmm:dd or as date\n"
- " and time in the format yyyy:mmm:dd/hh:mm:ss\n"
+ " minus original date. Date is specified as yyyy:mm:dd or as date\n"
+ " and time in the format yyyy:mm:dd/hh:mm:ss\n"
" -ts<time> Set the Exif internal time to <time>. <time> is in the format\n"
" yyyy:mm:dd-hh:mm:ss\n"
" -ds<date> Set the Exif internal date. <date> is in the format YYYY:MM:DD\n"
@@ -1340,6 +1416,9 @@
}else if (!strcmp(arg,"-di")){
DeleteIptc = TRUE;
DoModify = TRUE;
+ }else if (!strcmp(arg,"-dx")){
+ DeleteXmp = TRUE;
+ DoModify = TRUE;
}else if (!strcmp(arg, "-du")){
DeleteUnknown = TRUE;
DoModify = TRUE;
@@ -1348,6 +1427,7 @@
DeleteComments = TRUE;
DeleteIptc = TRUE;
DeleteUnknown = TRUE;
+ DeleteXmp = TRUE;
DoModify = TRUE;
}else if (!strcmp(arg,"-ce")){
EditComment = TRUE;
@@ -1422,13 +1502,17 @@
if (*arg){
// A strftime format string is supplied.
strftime_args = arg;
+ #ifdef _WIN32
+ SlashToNative(strftime_args);
+ #endif
//printf("strftime_args = %s\n",arg);
}
}else if (!strcmp(arg,"-a")){
- RenameAssociatedFiles = TRUE;
#ifndef _WIN32
ErrFatal("Error: -a only supported in Windows version");
- #endif
+ #else
+ RenameAssociatedFiles = TRUE;
+ #endif
}else if (!strcmp(arg,"-ft")){
Exif2FileTime = TRUE;
DoReadAction = TRUE;
@@ -1588,13 +1672,7 @@
FilesMatched = FALSE;
#ifdef _WIN32
- {
- int a;
- for (a=0;;a++){
- if (argv[argn][a] == '\0') break;
- if (argv[argn][a] == '/') argv[argn][a] = '\\';
- }
- }
+ SlashToNative(argv[argn]);
// Use my globbing module to do fancier wildcard expansion with recursive
// subdirectories under Windows.
MyGlob(argv[argn], ProcessFile);
diff --git a/jhead.h b/jhead.h
index 2eca8b8..1540d94 100644
--- a/jhead.h
+++ b/jhead.h
@@ -35,12 +35,16 @@
#define FALSE 0
#endif
-#define MAX_COMMENT 2000
+#define MAX_COMMENT_SIZE 2000
#ifdef _WIN32
#define PATH_MAX _MAX_PATH
+ #define SLASH '\\'
+#else
+ #define SLASH '/'
#endif
+
//--------------------------------------------------------------------------
// This structure is used to store jpeg file sections in memory.
typedef struct {
@@ -85,7 +89,10 @@
int ExposureMode;
int ISOequivalent;
int LightSource;
- char Comments[MAX_COMMENT];
+ int DistanceRange;
+
+ char Comments[MAX_COMMENT_SIZE];
+ int CommentWidchars; // If nonzer, widechar comment, indicates number of chars.
unsigned ThumbnailOffset; // Exif offset to thumbnail
unsigned ThumbnailSize; // Size of thumbnail.
@@ -191,10 +198,18 @@
TagTable_t* GpsTagToTagTableEntry(unsigned short tag);
// iptc.c prototpyes
-void show_IPTC (unsigned char * CharBuf, unsigned int length);
+void show_IPTC (unsigned char * CharBuf, unsigned int length);
+void ShowXmp(Section_t XmpSection);
// Prototypes for myglob.c module
-extern void MyGlob(const char * Pattern , void (*FileFuncParm)(const char * FileName));
+#ifdef _WIN32
+void MyGlob(const char * Pattern , void (*FileFuncParm)(const char * FileName));
+void SlashToNative(char * Path);
+#endif
+
+// Prototypes for paths.c module
+int EnsurePathExists(const char * FileName);
+void CatPath(char * BasePath, const char * FilePath);
// Prototypes from jpgfile.c
int ReadJpegSections (FILE * infile, ReadMode_t ReadMode);
@@ -221,11 +236,11 @@
// in this program. (See jdmarker.c for a more complete list.)
//--------------------------------------------------------------------------
-#define M_SOF0 0xC0 // Start Of Frame N
-#define M_SOF1 0xC1 // N indicates which compression process
-#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
+#define M_SOF0 0xC0 // Start Of Frame N
+#define M_SOF1 0xC1 // N indicates which compression process
+#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
#define M_SOF3 0xC3
-#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
+#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
#define M_SOF6 0xC6
#define M_SOF7 0xC7
#define M_SOF9 0xC9
@@ -234,15 +249,16 @@
#define M_SOF13 0xCD
#define M_SOF14 0xCE
#define M_SOF15 0xCF
-#define M_SOI 0xD8 // Start Of Image (beginning of datastream)
-#define M_EOI 0xD9 // End Of Image (end of datastream)
-#define M_SOS 0xDA // Start Of Scan (begins compressed data)
-#define M_JFIF 0xE0 // Jfif marker
-#define M_EXIF 0xE1 // Exif marker
-#define M_COM 0xFE // COMment
+#define M_SOI 0xD8 // Start Of Image (beginning of datastream)
+#define M_EOI 0xD9 // End Of Image (end of datastream)
+#define M_SOS 0xDA // Start Of Scan (begins compressed data)
+#define M_JFIF 0xE0 // Jfif marker
+#define M_EXIF 0xE1 // Exif marker. Also used for XMP data!
+#define M_XMP 0x10E1 // Not a real tag (same value in file as Exif!)
+#define M_COM 0xFE // COMment
#define M_DQT 0xDB
#define M_DHT 0xC4
#define M_DRI 0xDD
-#define M_IPTC 0xED // IPTC marker
+#define M_IPTC 0xED // IPTC marker
diff --git a/jpgfile.c b/jpgfile.c
index 6f5cf72..cf81039 100644
--- a/jpgfile.c
+++ b/jpgfile.c
@@ -45,13 +45,13 @@
static void process_COM (const uchar * Data, int length)
{
int ch;
- char Comment[MAX_COMMENT+1];
+ char Comment[MAX_COMMENT_SIZE+1];
int nch;
int a;
nch = 0;
- if (length > MAX_COMMENT) length = MAX_COMMENT; // Truncate if it won't fit in our structure.
+ if (length > MAX_COMMENT_SIZE) length = MAX_COMMENT_SIZE; // Truncate if it won't fit in our structure.
for (a=2;a<length;a++){
ch = Data[a];
@@ -72,6 +72,7 @@
}
strcpy(ImageInfo.Comments,Comment);
+ ImageInfo.CommentWidchars = 0;
}
@@ -141,22 +142,16 @@
CheckSectionsAllocated();
- for (a=0;a<7;a++){
+ for (a=0;a<=16;a++){
marker = fgetc(infile);
if (marker != 0xff) break;
- if (a >= 6){
+ if (a >= 16){
fprintf(stderr,"too many padding bytes\n");
return FALSE;
}
}
- if (marker == 0xff){
- // 0xff is legal padding, but if we get that many, something's wrong.
- //ErrFatal("too many padding bytes!");
- LOGE("too many padding bytes!");
- return FALSE;
- }
Sections[SectionsRead].Type = marker;
@@ -254,15 +249,24 @@
break;
case M_EXIF:
- // Seen files from some 'U-lead' software with Vivitar scanner
- // that uses marker 31 for non exif stuff. Thus make sure
- // it says 'Exif' in the section before treating it as exif.
- if ((ReadMode & READ_METADATA) && memcmp(Data+2, "Exif", 4) == 0){
- process_EXIF(Data, itemlen);
- }else{
- // Discard this section.
- free(Sections[--SectionsRead].Data);
+ // There can be different section using the same marker.
+ if (ReadMode & READ_METADATA){
+ if (memcmp(Data+2, "Exif", 4) == 0){
+ process_EXIF(Data, itemlen);
+ break;
+ }else if (memcmp(Data+2, "http:", 5) == 0){
+ Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes.
+ if (ShowTags){
+ printf("Image cotains XMP section, %d bytes long\n", itemlen);
+ if (ShowTags){
+ ShowXmp(Sections[SectionsRead-1]);
+ }
+ }
+ break;
+ }
}
+ // Oterwise, discard this section.
+ free(Sections[--SectionsRead].Data);
break;
case M_IPTC:
@@ -399,9 +403,14 @@
uchar * ThumbnailPointer;
if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){
+ if (ThumbFileName == NULL){
+ // Delete of nonexistent thumbnail (not even pointers present)
+ // No action, no error.
+ return FALSE;
+ }
+
// Adding or removing of thumbnail is not possible - that would require rearranging
// of the exif header, which is risky, and jhad doesn't know how to do.
-
fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n");
#ifdef SUPERDEBUG
LOGE("Image contains no thumbnail to replace - add is not possible\n");
@@ -430,6 +439,10 @@
return FALSE;
}
}else{
+ if (ImageInfo.ThumbnailSize == 0){
+ return FALSE;
+ }
+
ThumbLen = 0;
ThumbnailFile = NULL;
}
@@ -469,15 +482,19 @@
Section_t ExifKeeper;
Section_t CommentKeeper;
Section_t IptcKeeper;
+ Section_t XmpKeeper;
int a;
memset(&ExifKeeper, 0, sizeof(ExifKeeper));
memset(&CommentKeeper, 0, sizeof(CommentKeeper));
memset(&IptcKeeper, 0, sizeof(IptcKeeper));
+ memset(&XmpKeeper, 0, sizeof(IptcKeeper));
for (a=0;a<SectionsRead;a++){
if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){
- ExifKeeper = Sections[a];
+ ExifKeeper = Sections[a];
+ }else if (Sections[a].Type == M_XMP && XmpKeeper.Type == 0){
+ XmpKeeper = Sections[a];
}else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){
CommentKeeper = Sections[a];
}else if (Sections[a].Type == M_IPTC && IptcKeeper.Type == 0){
@@ -499,6 +516,11 @@
CheckSectionsAllocated();
Sections[SectionsRead++] = IptcKeeper;
}
+
+ if (XmpKeeper.Type){
+ CheckSectionsAllocated();
+ Sections[SectionsRead++] = XmpKeeper;
+ }
}
//--------------------------------------------------------------------------
@@ -537,7 +559,7 @@
// Write all the misc sections
for (a=0;a<SectionsRead-1;a++){
fputc(0xff,outfile);
- fputc(Sections[a].Type, outfile);
+ fputc((unsigned char)Sections[a].Type, outfile);
fwrite(Sections[a].Data, Sections[a].Size, 1, outfile);
}
@@ -611,6 +633,7 @@
case M_SOS:
case M_JFIF:
case M_EXIF:
+ case M_XMP:
case M_COM:
case M_DQT:
case M_DHT: