rjhead 0.2.88
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/ext/changes.txt +351 -0
- data/ext/exif.c +1629 -0
- data/ext/extconf.rb +0 -0
- data/ext/gpsinfo.c +217 -0
- data/ext/iptc.c +205 -0
- data/ext/jhead.1 +409 -0
- data/ext/jhead.c +1697 -0
- data/ext/jhead.h +251 -0
- data/ext/jhead.spec +149 -0
- data/ext/jpgfile.c +738 -0
- data/ext/make.bat +1 -0
- data/ext/makefile +23 -0
- data/ext/makefile-win32 +27 -0
- data/ext/makernote.c +184 -0
- data/ext/myglob.c +305 -0
- data/ext/paths.c +140 -0
- data/ext/readme.txt +60 -0
- data/ext/usage.html +469 -0
- data/lib/rjhead.rb +0 -0
- data/rjhead.gemspec +73 -0
- data/setup.rake +1 -0
- data/test/rjhead_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +94 -0
data/ext/extconf.rb
ADDED
File without changes
|
data/ext/gpsinfo.c
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
//--------------------------------------------------------------------------
|
2
|
+
// Parsing of GPS info from exif header.
|
3
|
+
//
|
4
|
+
// Matthias Wandel, Dec 1999 - Dec 2002
|
5
|
+
//--------------------------------------------------------------------------
|
6
|
+
#include "jhead.h"
|
7
|
+
|
8
|
+
#define MAX_GPS_TAG 0x1e
|
9
|
+
|
10
|
+
|
11
|
+
#define TAG_GPS_LAT_REF 1
|
12
|
+
#define TAG_GPS_LAT 2
|
13
|
+
#define TAG_GPS_LONG_REF 3
|
14
|
+
#define TAG_GPS_LONG 4
|
15
|
+
#define TAG_GPS_ALT_REF 5
|
16
|
+
#define TAG_GPS_ALT 6
|
17
|
+
|
18
|
+
|
19
|
+
static const char * GpsTags[MAX_GPS_TAG+1]= {
|
20
|
+
"VersionID ",//0x00
|
21
|
+
"LatitudeRef ",//0x01
|
22
|
+
"Latitude ",//0x02
|
23
|
+
"LongitudeRef ",//0x03
|
24
|
+
"Longitude ",//0x04
|
25
|
+
"AltitudeRef ",//0x05
|
26
|
+
"Altitude ",//0x06
|
27
|
+
"TimeStamp ",//0x07
|
28
|
+
"Satellites ",//0x08
|
29
|
+
"Status ",//0x09
|
30
|
+
"MeasureMode ",//0x0A
|
31
|
+
"DOP ",//0x0B
|
32
|
+
"SpeedRef ",//0x0C
|
33
|
+
"Speed ",//0x0D
|
34
|
+
"TrackRef ",//0x0E
|
35
|
+
"Track ",//0x0F
|
36
|
+
"ImgDirectionRef ",//0x10
|
37
|
+
"ImgDirection ",//0x11
|
38
|
+
"MapDatum ",//0x12
|
39
|
+
"DestLatitudeRef ",//0x13
|
40
|
+
"DestLatitude ",//0x14
|
41
|
+
"DestLongitudeRef",//0x15
|
42
|
+
"DestLongitude ",//0x16
|
43
|
+
"DestBearingRef ",//0x17
|
44
|
+
"DestBearing ",//0x18
|
45
|
+
"DestDistanceRef ",//0x19
|
46
|
+
"DestDistance ",//0x1A
|
47
|
+
"ProcessingMethod",//0x1B
|
48
|
+
"AreaInformation ",//0x1C
|
49
|
+
"DateStamp ",//0x1D
|
50
|
+
"Differential ",//0x1E
|
51
|
+
};
|
52
|
+
|
53
|
+
//--------------------------------------------------------------------------
|
54
|
+
// Process GPS info directory
|
55
|
+
//--------------------------------------------------------------------------
|
56
|
+
void ProcessGpsInfo(unsigned char * DirStart, int ByteCountUnused, unsigned char * OffsetBase, unsigned ExifLength)
|
57
|
+
{
|
58
|
+
int de;
|
59
|
+
unsigned a;
|
60
|
+
int NumDirEntries;
|
61
|
+
|
62
|
+
NumDirEntries = Get16u(DirStart);
|
63
|
+
#define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
|
64
|
+
|
65
|
+
if (ShowTags){
|
66
|
+
printf("(dir has %d entries)\n",NumDirEntries);
|
67
|
+
}
|
68
|
+
|
69
|
+
ImageInfo.GpsInfoPresent = TRUE;
|
70
|
+
strcpy(ImageInfo.GpsLat, "? ?");
|
71
|
+
strcpy(ImageInfo.GpsLong, "? ?");
|
72
|
+
ImageInfo.GpsAlt[0] = 0;
|
73
|
+
|
74
|
+
for (de=0;de<NumDirEntries;de++){
|
75
|
+
unsigned Tag, Format, Components;
|
76
|
+
unsigned char * ValuePtr;
|
77
|
+
int ComponentSize;
|
78
|
+
unsigned ByteCount;
|
79
|
+
unsigned char * DirEntry;
|
80
|
+
DirEntry = DIR_ENTRY_ADDR(DirStart, de);
|
81
|
+
|
82
|
+
if (DirEntry+12 > OffsetBase+ExifLength){
|
83
|
+
ErrNonfatal("GPS info directory goes past end of exif",0,0);
|
84
|
+
return;
|
85
|
+
}
|
86
|
+
|
87
|
+
Tag = Get16u(DirEntry);
|
88
|
+
Format = Get16u(DirEntry+2);
|
89
|
+
Components = Get32u(DirEntry+4);
|
90
|
+
|
91
|
+
if ((Format-1) >= NUM_FORMATS) {
|
92
|
+
// (-1) catches illegal zero case as unsigned underflows to positive large.
|
93
|
+
ErrNonfatal("Illegal number format %d for Exif gps tag %04x", Format, Tag);
|
94
|
+
continue;
|
95
|
+
}
|
96
|
+
|
97
|
+
ComponentSize = BytesPerFormat[Format];
|
98
|
+
ByteCount = Components * ComponentSize;
|
99
|
+
|
100
|
+
if (ByteCount > 4){
|
101
|
+
unsigned OffsetVal;
|
102
|
+
OffsetVal = Get32u(DirEntry+8);
|
103
|
+
// If its bigger than 4 bytes, the dir entry contains an offset.
|
104
|
+
if (OffsetVal+ByteCount > ExifLength){
|
105
|
+
// Bogus pointer offset and / or bytecount value
|
106
|
+
ErrNonfatal("Illegal value pointer for Exif gps tag %04x", Tag,0);
|
107
|
+
continue;
|
108
|
+
}
|
109
|
+
ValuePtr = OffsetBase+OffsetVal;
|
110
|
+
}else{
|
111
|
+
// 4 bytes or less and value is in the dir entry itself
|
112
|
+
ValuePtr = DirEntry+8;
|
113
|
+
}
|
114
|
+
|
115
|
+
switch(Tag){
|
116
|
+
char FmtString[21];
|
117
|
+
char TempString[50];
|
118
|
+
double Values[3];
|
119
|
+
|
120
|
+
case TAG_GPS_LAT_REF:
|
121
|
+
ImageInfo.GpsLat[0] = ValuePtr[0];
|
122
|
+
break;
|
123
|
+
|
124
|
+
case TAG_GPS_LONG_REF:
|
125
|
+
ImageInfo.GpsLong[0] = ValuePtr[0];
|
126
|
+
break;
|
127
|
+
|
128
|
+
case TAG_GPS_LAT:
|
129
|
+
case TAG_GPS_LONG:
|
130
|
+
if (Format != FMT_URATIONAL){
|
131
|
+
ErrNonfatal("Inappropriate format (%d) for Exif GPS coordinates!", Format, 0);
|
132
|
+
}
|
133
|
+
strcpy(FmtString, "%0.0fd %0.0fm %0.0fs");
|
134
|
+
for (a=0;a<3;a++){
|
135
|
+
int den, digits;
|
136
|
+
|
137
|
+
den = Get32s(ValuePtr+4+a*ComponentSize);
|
138
|
+
digits = 0;
|
139
|
+
while (den > 1 && digits <= 6){
|
140
|
+
den = den / 10;
|
141
|
+
digits += 1;
|
142
|
+
}
|
143
|
+
if (digits > 6) digits = 6;
|
144
|
+
FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0));
|
145
|
+
FmtString[3+a*7] = (char)('0'+digits);
|
146
|
+
|
147
|
+
Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
|
148
|
+
}
|
149
|
+
|
150
|
+
sprintf(TempString, FmtString, Values[0], Values[1], Values[2]);
|
151
|
+
|
152
|
+
if (Tag == TAG_GPS_LAT){
|
153
|
+
strncpy(ImageInfo.GpsLat+2, TempString, 29);
|
154
|
+
}else{
|
155
|
+
strncpy(ImageInfo.GpsLong+2, TempString, 29);
|
156
|
+
}
|
157
|
+
break;
|
158
|
+
|
159
|
+
case TAG_GPS_ALT_REF:
|
160
|
+
ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' ');
|
161
|
+
break;
|
162
|
+
|
163
|
+
case TAG_GPS_ALT:
|
164
|
+
sprintf(ImageInfo.GpsAlt + 1, "%.2fm",
|
165
|
+
ConvertAnyFormat(ValuePtr, Format));
|
166
|
+
break;
|
167
|
+
}
|
168
|
+
|
169
|
+
if (ShowTags){
|
170
|
+
// Show tag value.
|
171
|
+
if (Tag < MAX_GPS_TAG){
|
172
|
+
printf(" GPS%s =", GpsTags[Tag]);
|
173
|
+
}else{
|
174
|
+
// Show unknown tag
|
175
|
+
printf(" Illegal GPS tag %04x=", Tag);
|
176
|
+
}
|
177
|
+
|
178
|
+
switch(Format){
|
179
|
+
case FMT_UNDEFINED:
|
180
|
+
// Undefined is typically an ascii string.
|
181
|
+
|
182
|
+
case FMT_STRING:
|
183
|
+
// String arrays printed without function call (different from int arrays)
|
184
|
+
{
|
185
|
+
printf("\"");
|
186
|
+
for (a=0;a<ByteCount;a++){
|
187
|
+
int ZeroSkipped = 0;
|
188
|
+
if (ValuePtr[a] >= 32){
|
189
|
+
if (ZeroSkipped){
|
190
|
+
printf("?");
|
191
|
+
ZeroSkipped = 0;
|
192
|
+
}
|
193
|
+
putchar(ValuePtr[a]);
|
194
|
+
}else{
|
195
|
+
if (ValuePtr[a] == 0){
|
196
|
+
ZeroSkipped = 1;
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
printf("\"\n");
|
201
|
+
}
|
202
|
+
break;
|
203
|
+
|
204
|
+
default:
|
205
|
+
// Handle arrays of numbers later (will there ever be?)
|
206
|
+
for (a=0;;){
|
207
|
+
PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount);
|
208
|
+
if (++a >= Components) break;
|
209
|
+
printf(", ");
|
210
|
+
}
|
211
|
+
printf("\n");
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
|
data/ext/iptc.c
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
//--------------------------------------------------------------------------
|
2
|
+
// Process IPTC data and XMP data.
|
3
|
+
//--------------------------------------------------------------------------
|
4
|
+
#include "jhead.h"
|
5
|
+
|
6
|
+
// IPTC entry types known to Jhead (there's many more defined)
|
7
|
+
#define IPTC_RECORD_VERSION 0x00
|
8
|
+
#define IPTC_SUPLEMENTAL_CATEGORIES 0x14
|
9
|
+
#define IPTC_KEYWORDS 0x19
|
10
|
+
#define IPTC_CAPTION 0x78
|
11
|
+
#define IPTC_AUTHOR 0x7A
|
12
|
+
#define IPTC_HEADLINE 0x69
|
13
|
+
#define IPTC_SPECIAL_INSTRUCTIONS 0x28
|
14
|
+
#define IPTC_CATEGORY 0x0F
|
15
|
+
#define IPTC_BYLINE 0x50
|
16
|
+
#define IPTC_BYLINE_TITLE 0x55
|
17
|
+
#define IPTC_CREDIT 0x6E
|
18
|
+
#define IPTC_SOURCE 0x73
|
19
|
+
#define IPTC_COPYRIGHT_NOTICE 0x74
|
20
|
+
#define IPTC_OBJECT_NAME 0x05
|
21
|
+
#define IPTC_CITY 0x5A
|
22
|
+
#define IPTC_STATE 0x5F
|
23
|
+
#define IPTC_COUNTRY 0x65
|
24
|
+
#define IPTC_TRANSMISSION_REFERENCE 0x67
|
25
|
+
#define IPTC_DATE 0x37
|
26
|
+
#define IPTC_COPYRIGHT 0x0A
|
27
|
+
#define IPTC_COUNTRY_CODE 0x64
|
28
|
+
#define IPTC_REFERENCE_SERVICE 0x2D
|
29
|
+
#define IPTC_TIME_CREATED 0x3C
|
30
|
+
#define IPTC_SUB_LOCATION 0x5C
|
31
|
+
#define IPTC_IMAGE_TYPE 0x82
|
32
|
+
|
33
|
+
//--------------------------------------------------------------------------
|
34
|
+
// Process and display IPTC marker.
|
35
|
+
//
|
36
|
+
// IPTC block consists of:
|
37
|
+
// - Marker: 1 byte (0xED)
|
38
|
+
// - Block length: 2 bytes
|
39
|
+
// - IPTC Signature: 14 bytes ("Photoshop 3.0\0")
|
40
|
+
// - 8BIM Signature 4 bytes ("8BIM")
|
41
|
+
// - IPTC Block start 2 bytes (0x04, 0x04)
|
42
|
+
// - IPTC Header length 1 byte
|
43
|
+
// - IPTC header Header is padded to even length, counting the length byte
|
44
|
+
// - Length 4 bytes
|
45
|
+
// - IPTC Data which consists of a number of entries, each of which has the following format:
|
46
|
+
// - Signature 2 bytes (0x1C02)
|
47
|
+
// - Entry type 1 byte (for defined entry types, see #defines above)
|
48
|
+
// - entry length 2 bytes
|
49
|
+
// - entry data 'entry length' bytes
|
50
|
+
//
|
51
|
+
//--------------------------------------------------------------------------
|
52
|
+
void show_IPTC (unsigned char* Data, unsigned int itemlen)
|
53
|
+
{
|
54
|
+
const char IptcSig1[] = "Photoshop 3.0";
|
55
|
+
const char IptcSig2[] = "8BIM";
|
56
|
+
const char IptcSig3[] = {0x04, 0x04};
|
57
|
+
|
58
|
+
unsigned char * pos = Data + sizeof(short); // position data pointer after length field
|
59
|
+
unsigned char * maxpos = Data+itemlen;
|
60
|
+
char headerLen = 0;
|
61
|
+
|
62
|
+
if (itemlen < 25) goto corrupt;
|
63
|
+
|
64
|
+
// Check IPTC signatures
|
65
|
+
if (memcmp(pos, IptcSig1, sizeof(IptcSig1)-1) != 0) goto badsig;
|
66
|
+
pos += sizeof(IptcSig1); // move data pointer to the next field
|
67
|
+
|
68
|
+
if (memcmp(pos, IptcSig2, sizeof(IptcSig2)-1) != 0) goto badsig;
|
69
|
+
pos += sizeof(IptcSig2)-1; // move data pointer to the next field
|
70
|
+
|
71
|
+
if (memcmp(pos, IptcSig3, sizeof(IptcSig3)) != 0){
|
72
|
+
badsig:
|
73
|
+
if (ShowTags){
|
74
|
+
ErrNonfatal("IPTC type signature mismatch\n",0,0);
|
75
|
+
}
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
pos += sizeof(IptcSig3); // move data pointer to the next field
|
79
|
+
|
80
|
+
if (pos >= maxpos) goto corrupt;
|
81
|
+
|
82
|
+
// IPTC section found
|
83
|
+
|
84
|
+
// Skip header
|
85
|
+
headerLen = *pos++; // get header length and move data pointer to the next field
|
86
|
+
pos += headerLen + 1 - (headerLen % 2); // move data pointer to the next field (Header is padded to even length, counting the length byte)
|
87
|
+
|
88
|
+
if (pos+4 >= maxpos) goto corrupt;
|
89
|
+
|
90
|
+
// Get length (from motorola format)
|
91
|
+
//length = (*pos << 24) | (*(pos+1) << 16) | (*(pos+2) << 8) | *(pos+3);
|
92
|
+
|
93
|
+
pos += 4; // move data pointer to the next field
|
94
|
+
|
95
|
+
printf("======= IPTC data: =======\n");
|
96
|
+
|
97
|
+
// Now read IPTC data
|
98
|
+
while (pos < (Data + itemlen-5)) {
|
99
|
+
short signature;
|
100
|
+
unsigned char type = 0;
|
101
|
+
short length = 0;
|
102
|
+
char * description = NULL;
|
103
|
+
|
104
|
+
if (pos+5 > maxpos) goto corrupt;
|
105
|
+
|
106
|
+
signature = (*pos << 8) + (*(pos+1));
|
107
|
+
pos += 2;
|
108
|
+
|
109
|
+
if (signature != 0x1C02){
|
110
|
+
break;
|
111
|
+
}
|
112
|
+
|
113
|
+
type = *pos++;
|
114
|
+
length = (*pos << 8) + (*(pos+1));
|
115
|
+
pos += 2; // Skip tag length
|
116
|
+
|
117
|
+
if (pos+length > maxpos) goto corrupt;
|
118
|
+
// Process tag here
|
119
|
+
switch (type) {
|
120
|
+
case IPTC_RECORD_VERSION:
|
121
|
+
printf("Record vers. : %d\n", (*pos << 8) + (*(pos+1)));
|
122
|
+
break;
|
123
|
+
|
124
|
+
case IPTC_SUPLEMENTAL_CATEGORIES: description = "SuplementalCategories"; break;
|
125
|
+
case IPTC_KEYWORDS: description = "Keywords"; break;
|
126
|
+
case IPTC_CAPTION: description = "Caption"; break;
|
127
|
+
case IPTC_AUTHOR: description = "Author"; break;
|
128
|
+
case IPTC_HEADLINE: description = "Headline"; break;
|
129
|
+
case IPTC_SPECIAL_INSTRUCTIONS: description = "Spec. Instr."; break;
|
130
|
+
case IPTC_CATEGORY: description = "Category"; break;
|
131
|
+
case IPTC_BYLINE: description = "Byline"; break;
|
132
|
+
case IPTC_BYLINE_TITLE: description = "Byline Title"; break;
|
133
|
+
case IPTC_CREDIT: description = "Credit"; break;
|
134
|
+
case IPTC_SOURCE: description = "Source"; break;
|
135
|
+
case IPTC_COPYRIGHT_NOTICE: description = "(C)Notice"; break;
|
136
|
+
case IPTC_OBJECT_NAME: description = "Object Name"; break;
|
137
|
+
case IPTC_CITY: description = "City"; break;
|
138
|
+
case IPTC_STATE: description = "State"; break;
|
139
|
+
case IPTC_COUNTRY: description = "Country"; break;
|
140
|
+
case IPTC_TRANSMISSION_REFERENCE: description = "OriginalTransmissionReference"; break;
|
141
|
+
case IPTC_DATE: description = "DateCreated"; break;
|
142
|
+
case IPTC_COPYRIGHT: description = "(C)Flag"; break;
|
143
|
+
case IPTC_REFERENCE_SERVICE: description = "Country Code"; break;
|
144
|
+
case IPTC_COUNTRY_CODE: description = "Ref. Service"; break;
|
145
|
+
case IPTC_TIME_CREATED: description = "Time Created"; break;
|
146
|
+
case IPTC_SUB_LOCATION: description = "Sub Location"; break;
|
147
|
+
case IPTC_IMAGE_TYPE: description = "Image type"; break;
|
148
|
+
|
149
|
+
default:
|
150
|
+
if (ShowTags){
|
151
|
+
printf("Unrecognised IPTC tag: %d\n", type );
|
152
|
+
}
|
153
|
+
break;
|
154
|
+
}
|
155
|
+
if (description != NULL) {
|
156
|
+
char TempBuf[32];
|
157
|
+
memset(TempBuf, 0, sizeof(TempBuf));
|
158
|
+
memset(TempBuf, ' ', 14);
|
159
|
+
memcpy(TempBuf, description, strlen(description));
|
160
|
+
strcat(TempBuf, ":");
|
161
|
+
printf("%s %*.*s\n", TempBuf, length, length, pos);
|
162
|
+
}
|
163
|
+
pos += length;
|
164
|
+
}
|
165
|
+
return;
|
166
|
+
corrupt:
|
167
|
+
ErrNonfatal("Pointer corruption in IPTC\n",0,0);
|
168
|
+
}
|
169
|
+
|
170
|
+
|
171
|
+
|
172
|
+
//--------------------------------------------------------------------------
|
173
|
+
// Dump contents of XMP section
|
174
|
+
//--------------------------------------------------------------------------
|
175
|
+
void ShowXmp(Section_t XmpSection)
|
176
|
+
{
|
177
|
+
unsigned char * Data;
|
178
|
+
char OutLine[101];
|
179
|
+
int OutLineChars;
|
180
|
+
int NonBlank;
|
181
|
+
unsigned a;
|
182
|
+
NonBlank = 0;
|
183
|
+
Data = XmpSection.Data;
|
184
|
+
OutLineChars = 0;
|
185
|
+
|
186
|
+
|
187
|
+
for (a=0;a<XmpSection.Size;a++){
|
188
|
+
if (Data[a] >= 32 && Data[a] < 128){
|
189
|
+
OutLine[OutLineChars++] = Data[a];
|
190
|
+
if (Data[a] != ' ') NonBlank |= 1;
|
191
|
+
}else{
|
192
|
+
if (Data[a] != '\n'){
|
193
|
+
OutLine[OutLineChars++] = '?';
|
194
|
+
}
|
195
|
+
}
|
196
|
+
if (Data[a] == '\n' || OutLineChars >= 100){
|
197
|
+
OutLine[OutLineChars] = 0;
|
198
|
+
if (NonBlank){
|
199
|
+
puts(OutLine);
|
200
|
+
}
|
201
|
+
NonBlank = (NonBlank & 1) << 1;
|
202
|
+
OutLineChars = 0;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
data/ext/jhead.1
ADDED
@@ -0,0 +1,409 @@
|
|
1
|
+
.TH JHEAD 1 "06 Nov 2009" "jhead 2.88"
|
2
|
+
.SH NAME
|
3
|
+
jhead \- Digicam JPEG Exif header manipulation tool
|
4
|
+
.SH SYNOPSIS
|
5
|
+
.B jhead
|
6
|
+
[
|
7
|
+
.I options
|
8
|
+
]
|
9
|
+
[
|
10
|
+
.I file\.\.\.
|
11
|
+
]
|
12
|
+
|
13
|
+
.LP
|
14
|
+
.SH DESCRIPTION
|
15
|
+
.LP
|
16
|
+
.B jhead
|
17
|
+
is used to display and manipulate data contained in the Exif header of JPEG
|
18
|
+
images from digital cameras. By default, jhead displays the more useful
|
19
|
+
camera settings from the file in a user-friendly format.
|
20
|
+
.PP
|
21
|
+
.B jhead
|
22
|
+
can also be used to manipulate some aspects of the image relating to JPEG and
|
23
|
+
Exif headers, such as changing the internal timestamps, removing the thumbnail,
|
24
|
+
or transferring Exif headers back into edited images after graphical editors
|
25
|
+
deleted the Exif header.
|
26
|
+
.B jhead
|
27
|
+
can also be used to launch other programs, similar in style to the UNIX
|
28
|
+
.B find
|
29
|
+
command, but much simpler.
|
30
|
+
|
31
|
+
|
32
|
+
.SH GENERAL METADATA OPTIONS
|
33
|
+
.TP
|
34
|
+
.BI \-\^te \ file
|
35
|
+
Transplant Exif header from a JPEG (with Exif header) in
|
36
|
+
.I file
|
37
|
+
into the image that is manipulated. This option is
|
38
|
+
useful if you like to edit the photos but still want the Exif header on your photos.
|
39
|
+
As most photo editing programs will wipe out the Exif header, this option can be used
|
40
|
+
to re-copy them back from original copies after editing the photos.
|
41
|
+
|
42
|
+
|
43
|
+
This feature has an interesting 'relative path' option for specifying the
|
44
|
+
thumbnail name. Whenever the <name> contains the characters '&i',
|
45
|
+
will substitute the original filename for this name. This allows creating a
|
46
|
+
jhead 'relative name' when doing a whole batch of files. For example, the
|
47
|
+
incantation:
|
48
|
+
|
49
|
+
.B jhead -te "originals\&i" *.jpg
|
50
|
+
|
51
|
+
would transfer the exif header for each .jpg file in the originals directory by
|
52
|
+
the same name, Both Win32 and most Unix shells treat the '&' character in a
|
53
|
+
special way, so you have to put quotes around that command line option for
|
54
|
+
the '&' to even be passed to the program.
|
55
|
+
|
56
|
+
.TP
|
57
|
+
.B \-dc
|
58
|
+
Delete comment field from the JPEG header. Note that the comment
|
59
|
+
is not part of the Exif header.
|
60
|
+
.TP
|
61
|
+
.B \-de
|
62
|
+
Delete the Exif header entirely. Leaves other metadata sections intact.
|
63
|
+
.TP
|
64
|
+
.B \-di
|
65
|
+
Delete the IPTC section, if present. Leaves other metadata sections intact.
|
66
|
+
.TP
|
67
|
+
.B \-dx
|
68
|
+
Delete the XMP section, if present. Leaves other metadata sections intact.
|
69
|
+
.TP
|
70
|
+
.B \-du
|
71
|
+
Delete sections of jpeg that are not Exif, not comment, and otherwise not
|
72
|
+
contributing to the image either - such as data that photoshop might leave in the image.
|
73
|
+
.TP
|
74
|
+
.B \-purejpg
|
75
|
+
Delete all JPEG sections that aren't necessary for rendering the image. Strips any
|
76
|
+
metadata that various applications may have left in the image. A combination of
|
77
|
+
the -de -dc and -du options.
|
78
|
+
.TP
|
79
|
+
.B \-mkexif
|
80
|
+
Creates minimal exif header. Exif header contains date/time, and empty thumbnail
|
81
|
+
fields only. Date/time set to file time by default. Use with -rgt option if you
|
82
|
+
want the exif header to contain a thumbnail. Note that exif header creation is
|
83
|
+
very limited at this time, and no other fields can be added to the exif header
|
84
|
+
this way.
|
85
|
+
.TP
|
86
|
+
.B \-ce
|
87
|
+
Edit the JPEG header comment field (note, this comment field is outside the Exif structure
|
88
|
+
and can be part of Exif and non Exif style JPEG images).
|
89
|
+
|
90
|
+
A temporary file containing the comment is created and a text editor is launched to edit
|
91
|
+
the file. The editor is specified in the EDITOR environment variable. If none is specified
|
92
|
+
notepad or vi are used under Windows and Unix respectively. After the editor exits,
|
93
|
+
the data is transferred back into the image, and the temporary file deleted.
|
94
|
+
.TP
|
95
|
+
.BI \-\^cs \ file
|
96
|
+
Save comment section to a
|
97
|
+
.I file
|
98
|
+
.TP
|
99
|
+
.BI \-\^ci \ file
|
100
|
+
Replace comment with text from
|
101
|
+
.I file
|
102
|
+
.TP
|
103
|
+
.BI \-\^cl \ string
|
104
|
+
Replace comment with specified string from command line
|
105
|
+
.IR file
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
.SH DATE / TIME MANIPULATION OPTIONS
|
110
|
+
|
111
|
+
.TP
|
112
|
+
.B \-ft
|
113
|
+
Sets the file's system time stamp to what is stored in the Exif header.
|
114
|
+
.TP
|
115
|
+
.B \-dsft
|
116
|
+
Sets the Exif timestamp to the file's timestamp. Requires an Exif header to
|
117
|
+
pre-exist. Use -mkexif option to create one if needed.
|
118
|
+
.TP
|
119
|
+
.BI \-\^n [format_string]
|
120
|
+
This option causes files to be renamed and/ or mmoved using the date information from the Exif header
|
121
|
+
"DateTimeOriginal" field.
|
122
|
+
If the file is not an Exif file, or the DateTimeOriginal does not contain a valid value,
|
123
|
+
the file date is used.
|
124
|
+
Renaming is by default restricted to files whose names consist largely of digits.
|
125
|
+
This effectively restricts renaming to files that have not already been manually renamed, as
|
126
|
+
the default sequential names from digital cameras consist largely of digits.
|
127
|
+
Use the
|
128
|
+
.B -nf
|
129
|
+
option to force renaming of all files.
|
130
|
+
If the new name contains a '/', this will be interpreted as a new path, and the file will be
|
131
|
+
moved accordingly.
|
132
|
+
|
133
|
+
|
134
|
+
If the
|
135
|
+
.I format_string
|
136
|
+
is omitted, the file will be renamed to MMDD-HHMMSS. Note that
|
137
|
+
this scheme doesn't include the year (I never have photos from different years together anyway).
|
138
|
+
|
139
|
+
If a
|
140
|
+
.I format_string
|
141
|
+
is provided, it will be passed to the strftime function as the format string.
|
142
|
+
In addition, if the format string contains '%f', this will
|
143
|
+
substitute the original name of the file (minus extension). '%i' will substitute a sequence
|
144
|
+
number. Leading zeros can be specified like with printf - i.e. '%04i' pads the number to 4
|
145
|
+
digits using leading zeros.
|
146
|
+
|
147
|
+
If the name includes '/', this is interpreted as a new path for the file.
|
148
|
+
If the new path does not exist, the path will be created.
|
149
|
+
|
150
|
+
If the target name already exists, the name will be appended with "a", "b", "c", etc,
|
151
|
+
unless the name ends with a letter, in which case it will be appended with "0", "1", "2", etc.
|
152
|
+
|
153
|
+
This feature is especially useful if more than one digital camera was used to take pictures
|
154
|
+
of an event. By renaming them to a scheme according to date, they will automatically
|
155
|
+
appear in order of taking in most directory listings and image browsers. Alternatively,
|
156
|
+
if your image browser supports listing by file time, you can use the -ft option to set
|
157
|
+
the file time to the time the photo was taken.
|
158
|
+
|
159
|
+
Some of the more useful arguments for strftime are:
|
160
|
+
|
161
|
+
.BR %H \ Hour\ in\ 24-hour\ format\ (00\ -\ 23)
|
162
|
+
.br
|
163
|
+
.BR %j \ Day\ of\ year\ as\ decimal\ number\ (001\ -\ 366)
|
164
|
+
.br
|
165
|
+
.BR %m \ Month\ as\ decimal\ number\ (01\ -\ 12)
|
166
|
+
.br
|
167
|
+
.BR %M \ Minute\ as\ decimal\ number\ (00\ -\ 59)
|
168
|
+
.br
|
169
|
+
.BR %S \ Second\ as\ decimal\ number\ (00\ -\ 59)
|
170
|
+
.br
|
171
|
+
.BR %w \ Weekday\ as\ decimal\ number\ (0\ -\ 6;\ Sunday\ is\ 0)
|
172
|
+
.br
|
173
|
+
.BR %y \ Year\ without\ century,\ as\ decimal\ number\ (00\ -\ 99)
|
174
|
+
.br
|
175
|
+
.BR %Y \ Year\ with\ century,\ as\ decimal\ number
|
176
|
+
|
177
|
+
Example:
|
178
|
+
|
179
|
+
.B jhead -n%Y%m%d-%H%M%S *.jpg
|
180
|
+
This will rename files matched by *.jpg in the format YYYYMMDD-HHMMSS
|
181
|
+
|
182
|
+
For a full listing of strftime arguments, look up the strftime in them man pages.
|
183
|
+
Note that some arguments
|
184
|
+
to the strftime function (not listed here) produce strings with characters such as ':' that
|
185
|
+
may not be valid as part of a filename on some systems.
|
186
|
+
|
187
|
+
.TP
|
188
|
+
.B \-nf
|
189
|
+
Same as '-n' but renames files regardless of original file name.
|
190
|
+
|
191
|
+
|
192
|
+
.TP
|
193
|
+
.B \-ta<+|-><timediff>
|
194
|
+
Adjust time stored in the Exif header by h:mm backwards or forwards. Useful when having
|
195
|
+
taken pictures with the wrong time set on the camera, such as after travelling across
|
196
|
+
time zones, or when daylight savings time has changed.
|
197
|
+
|
198
|
+
This option changes all Date/time fields in the exif header, including "DateTimeOriginal" (tag 0x9003)
|
199
|
+
and "DateTimeDigitized" (tag 0x9004).
|
200
|
+
.TP
|
201
|
+
.B \-da<newdate>-<olddate>
|
202
|
+
|
203
|
+
Works like -ta, but for specifying large date offsets, to be used when fixing dates from
|
204
|
+
cameras where the date was set incorrectly, such as having date and time reset by battery
|
205
|
+
removal on some cameras
|
206
|
+
|
207
|
+
Because different months and years have different numbers of days in them, a simple offset
|
208
|
+
for months, days, years would lead to unexpected results at times. The time offset is
|
209
|
+
thus specified as a difference between two dates, so that jhead can figure out exactly
|
210
|
+
how many days the timestamp needs to be adjusted by, including leap years and daylight
|
211
|
+
savings time changes.
|
212
|
+
The dates are specified as yyyy:mm:dd. For sub-day adjustments, a time of day can also
|
213
|
+
be included, by specifying yyyy:nn:dd/hh:mm or yyyy:mm:dd/hh:mm:ss
|
214
|
+
|
215
|
+
Examples:
|
216
|
+
|
217
|
+
Year on camera was set to 2005 instead of 2004 for pictures taken in April
|
218
|
+
.br
|
219
|
+
jhead -da2004:03:01-2005:03:01
|
220
|
+
|
221
|
+
Default camera date is 2002:01:01, and date was reset on 2005:05:29 at 11:21 am
|
222
|
+
.br
|
223
|
+
jhead -da2005:05:29/11:21-2002:01:01
|
224
|
+
.TP
|
225
|
+
.B \-ts
|
226
|
+
Sets the time stored in the Exif header to what is specified on the command line.
|
227
|
+
Time must be specified as:
|
228
|
+
.I yyyy:mm:dd-hh:mm:ss
|
229
|
+
.TP
|
230
|
+
.B \-ds
|
231
|
+
Sets the date stored in the Exif header to what is specified on the command line.
|
232
|
+
Can be used to set date, just year and month, or just year.
|
233
|
+
Date is specified as:
|
234
|
+
.I yyyy:mm:dd, yyyy:mm, or yyyy
|
235
|
+
|
236
|
+
|
237
|
+
.SH THUMBNAIL MANIPULATION OPTIONS
|
238
|
+
|
239
|
+
.TP
|
240
|
+
.B \-dt
|
241
|
+
Delete thumbnails from the Exif header, but leave the
|
242
|
+
interesting parts intact. This option truncates the thumbnail from the Exif header, provided
|
243
|
+
that the thumbnail is the last part of the Exif header (which so far as I know is always the case).
|
244
|
+
Exif headers have a built-in thumbnail, which typically
|
245
|
+
occupies around 10k of space. This thumbnail is used by digital cameras. Windows XP may also
|
246
|
+
use this thumbnail if present (but it doesn't need it). The
|
247
|
+
thumbnails are too small to use even full screen on the digicam's LCD.
|
248
|
+
I have not encountered any adverse side effects of deleting the thumbnails, even from the
|
249
|
+
software provided with my old Olympus digicam. Use with caution.
|
250
|
+
|
251
|
+
.TP
|
252
|
+
.BI \-\^st \ file
|
253
|
+
Save the integral thumbnail to
|
254
|
+
.I file
|
255
|
+
The thumbnail lives
|
256
|
+
inside the Exif header, and is a very low-res JPEG image. Note that making
|
257
|
+
any changes to a photo, except for with some programs, generally wipes out the Exif header
|
258
|
+
and with it the thumbnail.
|
259
|
+
|
260
|
+
The thumbnail is too low res to really use for very much.
|
261
|
+
|
262
|
+
This feature has an interesting 'relative path' option for specifying the thumbnail name.
|
263
|
+
Whenever the name for
|
264
|
+
.I file
|
265
|
+
contains the characters '&i',
|
266
|
+
.B jhead
|
267
|
+
will substitute the original
|
268
|
+
filename for this name. This allows creating a 'relative name' when doing a whole
|
269
|
+
batch of files. For example, the incantation:
|
270
|
+
|
271
|
+
.B jhead -st "thumbnails/&i" *.jpg
|
272
|
+
|
273
|
+
would create a thumbnail for each .jpg file in the thumbnails directory by the same name,
|
274
|
+
(provided that the thumbnails directory exists, of course).
|
275
|
+
Both Win32 and UNIX shells treat the '&'character in a special way, so you have to
|
276
|
+
put quotes around that command line option for the '&' to even be passed to the program.
|
277
|
+
|
278
|
+
If a '-' is specified for the output file, the thumbnail is sent to stdout. (UNIX build only)
|
279
|
+
|
280
|
+
.TP
|
281
|
+
.B \-rt
|
282
|
+
Replace thumbnails from the Exif header.
|
283
|
+
This only works if the exif header already contains a thumbnail, and the thumbnail is at the
|
284
|
+
end of the header (both always the case if the photo came from a digital camera)
|
285
|
+
.TP
|
286
|
+
.BI \-\^rgt \ size
|
287
|
+
Regenerate exif thumbnail. 'size' specifies maximum height or width of thumbnail.
|
288
|
+
Relies on 'mogrify' program (from ImageMagick) to regenerate the thumbnail.
|
289
|
+
This only works if the image already contains a thumbnail.
|
290
|
+
|
291
|
+
.SH ROTATION OPTIONS
|
292
|
+
.TP
|
293
|
+
.B \-autorot
|
294
|
+
Using the 'Orientation' tag of the Exif header, rotate the image so that it is upright.
|
295
|
+
The program
|
296
|
+
.B jpegtran
|
297
|
+
is used to perform the rotation. This program is present in most
|
298
|
+
Linux distributions. For windows, you need to get a copy of it. After rotation, the
|
299
|
+
orientation tag of the Exif header is set to '1' (normal orientation). The thumbnail
|
300
|
+
is also rotated. Other fields of the Exif header, including
|
301
|
+
dimensions are untouched, but the JPEG height/width are adjusted.
|
302
|
+
This feature is especially useful with newer Canon cameras, that set the orientation
|
303
|
+
tag automatically using a gravity sensor.
|
304
|
+
.TP
|
305
|
+
.B \-norot
|
306
|
+
Clears the rotation field in the Exif header without altering the image. Useful if
|
307
|
+
the images were previously rotated without clearing the Exif rotation tag, as some
|
308
|
+
image browsers will auto rotate images when the rotation tag is set.
|
309
|
+
Sometimes, thumbnails and rotation tags can get very out of sync from manipulation
|
310
|
+
with various tools. To reset it all use -norot with -rgt to clear this out.
|
311
|
+
|
312
|
+
.SH OUTPUT VERBOSITY CONTROL
|
313
|
+
.TP
|
314
|
+
.B \-h
|
315
|
+
Displays summary of command line options.
|
316
|
+
.TP
|
317
|
+
.B \-v
|
318
|
+
Makes the program even more verbose than it already is. Like DOS programs, and unlike
|
319
|
+
UNIX programs, Jhead gives feedback as to what it is doing, even when nothing goes wrong.
|
320
|
+
Windows user that I am, when something doesn't give me feedback for 20 seconds, I assume
|
321
|
+
its crashed.
|
322
|
+
.TP
|
323
|
+
.B \-q
|
324
|
+
No output on success, more like Unix programs.
|
325
|
+
.TP
|
326
|
+
.B \-V
|
327
|
+
Print version info and compilation date.
|
328
|
+
.B \-exifmap
|
329
|
+
Show a map of the bytes in the exif header. Useful when analyzing strange exif headers,
|
330
|
+
not of much use to non software developers.
|
331
|
+
.TP
|
332
|
+
.B \-se
|
333
|
+
Suppress error messages relating to corrupt Exif header structure.
|
334
|
+
.TP
|
335
|
+
.B \-c
|
336
|
+
Concise output. This causes picture info to be summarized on one line instead of several.
|
337
|
+
Useful for grep-ing through images, as well as importing into spread sheets (data is space
|
338
|
+
delimited with quotes as text qualifier).
|
339
|
+
|
340
|
+
.SH FILE MATCHING OPTIONS
|
341
|
+
.TP
|
342
|
+
.B \-model
|
343
|
+
Restricts processing of files to those whose camera model, as indicated by the Exif image
|
344
|
+
information, contains the substring specified in the argument after '-model'.
|
345
|
+
For example, the following command will list only images that are from an S100 camera:
|
346
|
+
|
347
|
+
.B jhead -model S100 *.jpg
|
348
|
+
|
349
|
+
.B jhead -model S100 *.jpg
|
350
|
+
|
351
|
+
I use this option to restrict my JPEG recompensing to those images that came from my
|
352
|
+
Canon S100 digicam, (see the -cmd option).
|
353
|
+
.TP
|
354
|
+
.B \-exonly
|
355
|
+
Skip all files that don't have an Exif header. Photos straight from a digital camera
|
356
|
+
have an Exif header, whereas many photo manipulation tools discard the Exif header.
|
357
|
+
.TP
|
358
|
+
.B \-cmd
|
359
|
+
Executes the specified command on each JPEG file to be processed.
|
360
|
+
|
361
|
+
The Exif section of each file is read before running the command, and reinserted
|
362
|
+
after the command finishes.
|
363
|
+
|
364
|
+
The specified command invoked separately for each JPEG that is processed, even if
|
365
|
+
multiple files are specified (explicitly or by wild card).
|
366
|
+
|
367
|
+
Example use:
|
368
|
+
|
369
|
+
Having a whole directory of photos from my S100, I run the following commands:
|
370
|
+
|
371
|
+
.B jhead -cmd "mogrify -quality 80 &i" -model S100 -r *.jpg
|
372
|
+
.br
|
373
|
+
.B jhead -cmd "jpegtran -progressive &i > &o" -r *.jpg
|
374
|
+
|
375
|
+
The first command mogrifies all JPEGs in the tree that indicate that they are from a
|
376
|
+
Canon S100 in their Exif header to 80% quality at the same resolution. This is a 'lossy'
|
377
|
+
process, so I only run it on files that are from the Canon, and only run it once.
|
378
|
+
The next command then takes a JPEGs and converts them to progressive JPEGs. The result
|
379
|
+
is the same images, with no discernible differences, stored in half the space. This
|
380
|
+
produces substantial savings on some cameras.
|
381
|
+
|
382
|
+
.SH SEE ALSO
|
383
|
+
.BR jpegtran (1),
|
384
|
+
.BR mogrify (1),
|
385
|
+
.BR rdjpgcom (1),
|
386
|
+
.BR wrjpgcom (1)
|
387
|
+
.SH AUTHOR
|
388
|
+
Matthias Wandel
|
389
|
+
.SH BUGS
|
390
|
+
After jhead runs a program to rotate or resize an image, the image dimensions and thumbnail
|
391
|
+
in the Exif header are not adjusted.
|
392
|
+
.PP
|
393
|
+
Modifying of Exif header data is very limited, as Jhead internally only has a read only
|
394
|
+
implementation of the file system contained in the Exif header. For example, there is no way
|
395
|
+
to replace the thumbnail or edit the Exif comment in the Exif header. There is also no way
|
396
|
+
to create minimal exif headers.
|
397
|
+
.PP
|
398
|
+
Some Canon digital SLR cameras fail to adjust the effective sensor resolution when shooting at less
|
399
|
+
than full resolution, causing jhead to incorrectly miscalculate the sensor width and 35mm equivalent
|
400
|
+
focal length. The same can result from resizing photos with Photoshop, which will manipulate
|
401
|
+
parts of the exif header.
|
402
|
+
This is often reported as a bug in Jhead, but Jhead can't do much about incorrect data.
|
403
|
+
.PP
|
404
|
+
Send bug reports to mwandel at sentex dot net.
|
405
|
+
|
406
|
+
.SH COPYING PERMISSIONS
|
407
|
+
Jhead is 'public domain'. You may freely copy jhead, and reuse part or all of its code
|
408
|
+
in free or proprietary programs. I do however request that you do not post my e-mail
|
409
|
+
address in ways that spam robots can harvest it.
|