rjhead 0.2.88

Sign up to get free protection for your applications and to get access to all the features.
data/ext/jhead.h ADDED
@@ -0,0 +1,251 @@
1
+ //--------------------------------------------------------------------------
2
+ // Include file for jhead program.
3
+ //
4
+ // This include file only defines stuff that goes across modules.
5
+ // I like to keep the definitions for macros and structures as close to
6
+ // where they get used as possible, so include files only get stuff that
7
+ // gets used in more than one file.
8
+ //--------------------------------------------------------------------------
9
+ #define _CRT_SECURE_NO_DEPRECATE 1
10
+
11
+ #include <stdio.h>
12
+ #include <stdlib.h>
13
+ #include <string.h>
14
+ #include <time.h>
15
+ #include <errno.h>
16
+ #include <ctype.h>
17
+
18
+ //--------------------------------------------------------------------------
19
+
20
+ #ifdef _WIN32
21
+ #include <sys/utime.h>
22
+ #else
23
+ #include <utime.h>
24
+ #include <sys/types.h>
25
+ #include <unistd.h>
26
+ #include <errno.h>
27
+ #include <limits.h>
28
+ #endif
29
+
30
+
31
+ typedef unsigned char uchar;
32
+
33
+ #ifndef TRUE
34
+ #define TRUE 1
35
+ #define FALSE 0
36
+ #endif
37
+
38
+ #define MAX_COMMENT_SIZE 2000
39
+
40
+ #ifdef _WIN32
41
+ #define PATH_MAX _MAX_PATH
42
+ #define SLASH '\\'
43
+ #else
44
+ #define SLASH '/'
45
+ #endif
46
+
47
+
48
+ //--------------------------------------------------------------------------
49
+ // This structure is used to store jpeg file sections in memory.
50
+ typedef struct {
51
+ uchar * Data;
52
+ int Type;
53
+ unsigned Size;
54
+ }Section_t;
55
+
56
+ extern int ExifSectionIndex;
57
+
58
+ extern int DumpExifMap;
59
+
60
+ #define MAX_DATE_COPIES 10
61
+
62
+ //--------------------------------------------------------------------------
63
+ // This structure stores Exif header image elements in a simple manner
64
+ // Used to store camera data as extracted from the various ways that it can be
65
+ // stored in an exif header
66
+ typedef struct {
67
+ char FileName [PATH_MAX+1];
68
+ time_t FileDateTime;
69
+
70
+ struct {
71
+ // Info in the jfif header.
72
+ // This info is not used much - jhead used to just replace it with default
73
+ // values, and over 10 years, only two people pointed this out.
74
+ char Present;
75
+ char ResolutionUnits;
76
+ short XDensity;
77
+ short YDensity;
78
+ }JfifHeader;
79
+
80
+ unsigned FileSize;
81
+ char CameraMake [32];
82
+ char CameraModel [40];
83
+ char DateTime [20];
84
+ int Height, Width;
85
+ int Orientation;
86
+ int IsColor;
87
+ int Process;
88
+ int FlashUsed;
89
+ float FocalLength;
90
+ float ExposureTime;
91
+ float ApertureFNumber;
92
+ float Distance;
93
+ float CCDWidth;
94
+ float ExposureBias;
95
+ float DigitalZoomRatio;
96
+ int FocalLength35mmEquiv; // Exif 2.2 tag - usually not present.
97
+ int Whitebalance;
98
+ int MeteringMode;
99
+ int ExposureProgram;
100
+ int ExposureMode;
101
+ int ISOequivalent;
102
+ int LightSource;
103
+ int DistanceRange;
104
+
105
+ float xResolution;
106
+ float yResolution;
107
+ int ResolutionUnit;
108
+
109
+ char Comments[MAX_COMMENT_SIZE];
110
+ int CommentWidthchars; // If nonzero, widechar comment, indicates number of chars.
111
+
112
+ unsigned ThumbnailOffset; // Exif offset to thumbnail
113
+ unsigned ThumbnailSize; // Size of thumbnail.
114
+ unsigned LargestExifOffset; // Last exif data referenced (to check if thumbnail is at end)
115
+
116
+ char ThumbnailAtEnd; // Exif header ends with the thumbnail
117
+ // (we can only modify the thumbnail if its at the end)
118
+ int ThumbnailSizeOffset;
119
+
120
+ int DateTimeOffsets[MAX_DATE_COPIES];
121
+ int numDateTimeTags;
122
+
123
+ int GpsInfoPresent;
124
+ char GpsLat[31];
125
+ char GpsLong[31];
126
+ char GpsAlt[20];
127
+ }ImageInfo_t;
128
+
129
+
130
+
131
+ #define EXIT_FAILURE 1
132
+ #define EXIT_SUCCESS 0
133
+
134
+ // jpgfile.c functions
135
+ typedef enum {
136
+ READ_METADATA = 1,
137
+ READ_IMAGE = 2,
138
+ READ_ALL = 3
139
+ }ReadMode_t;
140
+
141
+
142
+ // prototypes for jhead.c functions
143
+ void ErrFatal(char * msg);
144
+ void ErrNonfatal(char * msg, int a1, int a2);
145
+ void FileTimeAsString(char * TimeStr);
146
+
147
+ // Prototypes for exif.c functions.
148
+ int Exif2tm(struct tm * timeptr, char * ExifTime);
149
+ void process_EXIF (unsigned char * CharBuf, unsigned int length);
150
+ int RemoveThumbnail(unsigned char * ExifSection);
151
+ void ShowImageInfo(int ShowFileInfo);
152
+ void ShowConciseImageInfo(void);
153
+ const char * ClearOrientation(void);
154
+ void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount);
155
+ double ConvertAnyFormat(void * ValuePtr, int Format);
156
+ int Get16u(void * Short);
157
+ unsigned Get32u(void * Long);
158
+ int Get32s(void * Long);
159
+ void Put32u(void * Value, unsigned PutValue);
160
+ void create_EXIF(void);
161
+
162
+ //--------------------------------------------------------------------------
163
+ // Exif format descriptor stuff
164
+ extern const int BytesPerFormat[];
165
+ #define NUM_FORMATS 12
166
+
167
+ #define FMT_BYTE 1
168
+ #define FMT_STRING 2
169
+ #define FMT_USHORT 3
170
+ #define FMT_ULONG 4
171
+ #define FMT_URATIONAL 5
172
+ #define FMT_SBYTE 6
173
+ #define FMT_UNDEFINED 7
174
+ #define FMT_SSHORT 8
175
+ #define FMT_SLONG 9
176
+ #define FMT_SRATIONAL 10
177
+ #define FMT_SINGLE 11
178
+ #define FMT_DOUBLE 12
179
+
180
+
181
+ // makernote.c prototypes
182
+ extern void ProcessMakerNote(unsigned char * DirStart, int ByteCount,
183
+ unsigned char * OffsetBase, unsigned ExifLength);
184
+
185
+ // gpsinfo.c prototypes
186
+ void ProcessGpsInfo(unsigned char * ValuePtr, int ByteCount,
187
+ unsigned char * OffsetBase, unsigned ExifLength);
188
+
189
+ // iptc.c prototpyes
190
+ void show_IPTC (unsigned char * CharBuf, unsigned int length);
191
+ void ShowXmp(Section_t XmpSection);
192
+
193
+ // Prototypes for myglob.c module
194
+ #ifdef _WIN32
195
+ void MyGlob(const char * Pattern , void (*FileFuncParm)(const char * FileName));
196
+ void SlashToNative(char * Path);
197
+ #endif
198
+
199
+ // Prototypes for paths.c module
200
+ int EnsurePathExists(const char * FileName);
201
+ void CatPath(char * BasePath, const char * FilePath);
202
+
203
+ // Prototypes from jpgfile.c
204
+ int ReadJpegSections (FILE * infile, ReadMode_t ReadMode);
205
+ void DiscardData(void);
206
+ void DiscardAllButExif(void);
207
+ int ReadJpegFile(const char * FileName, ReadMode_t ReadMode);
208
+ int ReplaceThumbnail(const char * ThumbFileName);
209
+ int SaveThumbnail(char * ThumbFileName);
210
+ int RemoveSectionType(int SectionType);
211
+ int RemoveUnknownSections(void);
212
+ void WriteJpegFile(const char * FileName);
213
+ Section_t * FindSection(int SectionType);
214
+ Section_t * CreateSection(int SectionType, unsigned char * Data, int size);
215
+ void ResetJpgfile(void);
216
+
217
+
218
+ // Variables from jhead.c used by exif.c
219
+ extern ImageInfo_t ImageInfo;
220
+ extern int ShowTags;
221
+
222
+ //--------------------------------------------------------------------------
223
+ // JPEG markers consist of one or more 0xFF bytes, followed by a marker
224
+ // code byte (which is not an FF). Here are the marker codes of interest
225
+ // in this program. (See jdmarker.c for a more complete list.)
226
+ //--------------------------------------------------------------------------
227
+
228
+ #define M_SOF0 0xC0 // Start Of Frame N
229
+ #define M_SOF1 0xC1 // N indicates which compression process
230
+ #define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
231
+ #define M_SOF3 0xC3
232
+ #define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
233
+ #define M_SOF6 0xC6
234
+ #define M_SOF7 0xC7
235
+ #define M_SOF9 0xC9
236
+ #define M_SOF10 0xCA
237
+ #define M_SOF11 0xCB
238
+ #define M_SOF13 0xCD
239
+ #define M_SOF14 0xCE
240
+ #define M_SOF15 0xCF
241
+ #define M_SOI 0xD8 // Start Of Image (beginning of datastream)
242
+ #define M_EOI 0xD9 // End Of Image (end of datastream)
243
+ #define M_SOS 0xDA // Start Of Scan (begins compressed data)
244
+ #define M_JFIF 0xE0 // Jfif marker
245
+ #define M_EXIF 0xE1 // Exif marker. Also used for XMP data!
246
+ #define M_XMP 0x10E1 // Not a real tag (same value in file as Exif!)
247
+ #define M_COM 0xFE // COMment
248
+ #define M_DQT 0xDB
249
+ #define M_DHT 0xC4
250
+ #define M_DRI 0xDD
251
+ #define M_IPTC 0xED // IPTC marker
data/ext/jhead.spec ADDED
@@ -0,0 +1,149 @@
1
+ # -----------------------------------------------------
2
+ # Define static values
3
+ # -----------------------------------------------------
4
+
5
+ # -----------------------------------------------------
6
+ # Define requirements for configure, BuildRequire,
7
+ # Require and so on (depending on macros)
8
+ # -----------------------------------------------------
9
+
10
+ # -----------------------------------------------------
11
+ # RPM-Information
12
+ # -----------------------------------------------------
13
+ Name: jhead
14
+ Summary: Tool for handling EXIF metadata in JPEG image files
15
+
16
+ Version: 2.88
17
+ Release: 0
18
+
19
+ Group: Libraries
20
+ License: Public Domain
21
+ URL: http://www.sentex.net/~mwandel/jhead/
22
+
23
+ Source: http://www.sentex.net/~mwandel/jhead/%{name}-%{version}.tar.gz
24
+
25
+ BuildRoot: %{_tmppath}/%{name}-%{version}-root
26
+ BuildRequires: glibc-devel
27
+
28
+ # -----------------------------------------------------
29
+ # Package-Information (for main package)
30
+ # -----------------------------------------------------
31
+ %description
32
+ This package provides a tool for displaying and manipulating non-image
33
+ portions of EXIF format JPEG image files, as produced by most digital cameras.
34
+
35
+ # -----------------------------------------------------
36
+ # Prepare-Section
37
+ # -----------------------------------------------------
38
+ %prep
39
+ %setup -q
40
+
41
+ # -----------------------------------------------------
42
+ # Build-Section
43
+ # -----------------------------------------------------
44
+ %build
45
+ make %{?_smp_mflags}
46
+
47
+ # -----------------------------------------------------
48
+ # Install-Section
49
+ # -----------------------------------------------------
50
+ %install
51
+ mkdir -p %{buildroot}%{_bindir}
52
+ cp jhead %{buildroot}%{_bindir}
53
+ mkdir -p %{buildroot}%{_mandir}/man1/
54
+ cp jhead.1.gz %{buildroot}%{_mandir}/man1/
55
+
56
+ # -----------------------------------------------------
57
+ # Clean-Section
58
+ # -----------------------------------------------------
59
+ %clean
60
+ rm -rf %{buildroot}
61
+
62
+ # -----------------------------------------------------
63
+ # Files-Section (for main package)
64
+ # -----------------------------------------------------
65
+ %files
66
+ %defattr(0644,root,root,0755)
67
+ %attr(0755,root,root) %{_bindir}/*
68
+ %doc readme.txt usage.html
69
+ %{_mandir}/man1/jhead.1.gz
70
+
71
+ # -----------------------------------------------------
72
+ # Changelog-Section
73
+ # -----------------------------------------------------
74
+ %changelog
75
+
76
+ * Fri Nov 6 2009 - 2.88
77
+ - Added ability to move files around with the -n command
78
+ - Various minor bugfixes and maintenance changes
79
+
80
+ * Thu Mar 3 2009 - 2.87
81
+ - Added the ability to move files with the -n option
82
+ - Fix a bunch of breakages from security fixes asked
83
+ for by debian people
84
+
85
+ * Sat Feb 14 2009 - 2.86
86
+ - Added ability to move files around with the -n command
87
+ - Various minor bugfixes and maintenance changes
88
+
89
+ * Mon Oct 4 2008 - 2.84
90
+ - decode more exif tags in -v mode
91
+ - Fix gpsinfo altitude bug
92
+ - Fix several potential stirng overflows
93
+
94
+ * Thu Apr 03 2008 - 2.82
95
+ - No longer delete XMP section on some operations
96
+ - Improve IPTC display
97
+ - Minor tweaks and cleanups
98
+
99
+ * Tue Nov 13 2007 - 2.8
100
+ - Limited IPTC handling
101
+ - Added -q option
102
+ - Fix handling of corrupted GPS directory.
103
+ - Extract focus distance from canon makernote.
104
+ - Extract subject range (pentax and fuji cameras)
105
+
106
+ * Thu Jan 11 2007 - 2.7
107
+ - Release version 2.7
108
+
109
+ * Sat Apr 29 2006 - 2.6
110
+ - Release version 2.6
111
+
112
+ * Sun Jan 08 2006 - 2.5
113
+ - Release version 2.5
114
+
115
+ * Thu Jun 10 2005 - 2.4-2
116
+ - Minor fixups.
117
+
118
+ * Sun May 29 2005 - 2.4
119
+ - Display gps info
120
+ - Added -da optino for large date adjust
121
+ - Fix time reference bug for -ta option
122
+ - Fix crashes with some corrupt jpeg files
123
+
124
+ * Mon Jan 03 2005 - 2.4
125
+ - Handle readonly files better
126
+ - Handle more strange jpegs
127
+ - added 'purejpg' option
128
+ - Display digital zoom ratio
129
+
130
+ * Sun Jun 20 2004 - 2.2-0
131
+ - Various bug and spelling fixes.
132
+ - added ability to do sequential renaming
133
+
134
+ * Tue Jan 08 2004 Matthias Wandel <mwandel[at]sentex.net> - 2.1-0
135
+ - Bumped version number to 2.1 for new jhead release.
136
+ - Added -cl feature
137
+ - added -noroot feature
138
+
139
+ * Tue Jun 03 2003 Oliver Pitzeier <oliver@linux-kernel.at> - 2.0-3
140
+ - Specfile cleanup/beautifying
141
+ - Use _smp_mflags within make
142
+ - Add versions to the changelog entries
143
+
144
+ * Mon Apr 14 2003 Matthias Wandel <mwandel[at]sentex.net> - 2.0-2
145
+ - First jhead 2.0 RPM built by me.
146
+ - Finally wrote a nice man page for jhead
147
+ - Using jhead 1.9 RPM from connectiva linux as starting point (left in the portugese tags)
148
+
149
+
data/ext/jpgfile.c ADDED
@@ -0,0 +1,738 @@
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 handles basic Jpeg file handling
6
+ //
7
+ // Matthias Wandel
8
+ //--------------------------------------------------------------------------
9
+ #include "jhead.h"
10
+
11
+ // Storage for simplified info extracted from file.
12
+ ImageInfo_t ImageInfo;
13
+
14
+
15
+ static Section_t * Sections = NULL;
16
+ static int SectionsAllocated;
17
+ static int SectionsRead;
18
+ static int HaveAll;
19
+
20
+
21
+
22
+ #define PSEUDO_IMAGE_MARKER 0x123; // Extra value.
23
+ //--------------------------------------------------------------------------
24
+ // Get 16 bits motorola order (always) for jpeg header stuff.
25
+ //--------------------------------------------------------------------------
26
+ static int Get16m(const void * Short)
27
+ {
28
+ return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
29
+ }
30
+
31
+
32
+ //--------------------------------------------------------------------------
33
+ // Process a COM marker.
34
+ // We want to print out the marker contents as legible text;
35
+ // we must guard against random junk and varying newline representations.
36
+ //--------------------------------------------------------------------------
37
+ static void process_COM (const uchar * Data, int length)
38
+ {
39
+ int ch;
40
+ char Comment[MAX_COMMENT_SIZE+1];
41
+ int nch;
42
+ int a;
43
+
44
+ nch = 0;
45
+
46
+ if (length > MAX_COMMENT_SIZE) length = MAX_COMMENT_SIZE; // Truncate if it won't fit in our structure.
47
+
48
+ for (a=2;a<length;a++){
49
+ ch = Data[a];
50
+
51
+ if (ch == '\r' && Data[a+1] == '\n') continue; // Remove cr followed by lf.
52
+
53
+ if (ch >= 32 || ch == '\n' || ch == '\t'){
54
+ Comment[nch++] = (char)ch;
55
+ }else{
56
+ Comment[nch++] = '?';
57
+ }
58
+ }
59
+
60
+ Comment[nch] = '\0'; // Null terminate
61
+
62
+ if (ShowTags){
63
+ printf("COM marker comment: %s\n",Comment);
64
+ }
65
+
66
+ strcpy(ImageInfo.Comments,Comment);
67
+ ImageInfo.CommentWidthchars = 0;
68
+ }
69
+
70
+
71
+ //--------------------------------------------------------------------------
72
+ // Process a SOFn marker. This is useful for the image dimensions
73
+ //--------------------------------------------------------------------------
74
+ static void process_SOFn (const uchar * Data, int marker)
75
+ {
76
+ int data_precision, num_components;
77
+
78
+ data_precision = Data[2];
79
+ ImageInfo.Height = Get16m(Data+3);
80
+ ImageInfo.Width = Get16m(Data+5);
81
+ num_components = Data[7];
82
+
83
+ if (num_components == 3){
84
+ ImageInfo.IsColor = 1;
85
+ }else{
86
+ ImageInfo.IsColor = 0;
87
+ }
88
+
89
+ ImageInfo.Process = marker;
90
+
91
+ if (ShowTags){
92
+ printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
93
+ ImageInfo.Width, ImageInfo.Height, num_components, data_precision);
94
+ }
95
+ }
96
+
97
+
98
+ //--------------------------------------------------------------------------
99
+ // Check sections array to see if it needs to be increased in size.
100
+ //--------------------------------------------------------------------------
101
+ void CheckSectionsAllocated(void)
102
+ {
103
+ if (SectionsRead > SectionsAllocated){
104
+ ErrFatal("allocation screwup");
105
+ }
106
+ if (SectionsRead >= SectionsAllocated){
107
+ SectionsAllocated += SectionsAllocated/2;
108
+ Sections = (Section_t *)realloc(Sections, sizeof(Section_t)*SectionsAllocated);
109
+ if (Sections == NULL){
110
+ ErrFatal("could not allocate data for entire image");
111
+ }
112
+ }
113
+ }
114
+
115
+
116
+ //--------------------------------------------------------------------------
117
+ // Parse the marker stream until SOS or EOI is seen;
118
+ //--------------------------------------------------------------------------
119
+ int ReadJpegSections (FILE * infile, ReadMode_t ReadMode)
120
+ {
121
+ int a;
122
+ int HaveCom = FALSE;
123
+
124
+ a = fgetc(infile);
125
+
126
+ if (a != 0xff || fgetc(infile) != M_SOI){
127
+ return FALSE;
128
+ }
129
+
130
+ ImageInfo.JfifHeader.XDensity = ImageInfo.JfifHeader.YDensity = 300;
131
+ ImageInfo.JfifHeader.ResolutionUnits = 1;
132
+
133
+ for(;;){
134
+ int itemlen;
135
+ int prev;
136
+ int marker = 0;
137
+ int ll,lh, got;
138
+ uchar * Data;
139
+
140
+ CheckSectionsAllocated();
141
+
142
+ prev = 0;
143
+ for (a=0;;a++){
144
+ marker = fgetc(infile);
145
+ if (marker != 0xff && prev == 0xff) break;
146
+ prev = marker;
147
+ }
148
+
149
+ if (a > 10){
150
+ ErrNonfatal("Extraneous %d padding bytes before section %02X",a-1,marker);
151
+ }
152
+
153
+ Sections[SectionsRead].Type = marker;
154
+
155
+ // Read the length of the section.
156
+ lh = fgetc(infile);
157
+ ll = fgetc(infile);
158
+
159
+ itemlen = (lh << 8) | ll;
160
+
161
+ if (itemlen < 2){
162
+ ErrFatal("invalid marker");
163
+ }
164
+
165
+ Sections[SectionsRead].Size = itemlen;
166
+
167
+ Data = (uchar *)malloc(itemlen);
168
+ if (Data == NULL){
169
+ ErrFatal("Could not allocate memory");
170
+ }
171
+ Sections[SectionsRead].Data = Data;
172
+
173
+ // Store first two pre-read bytes.
174
+ Data[0] = (uchar)lh;
175
+ Data[1] = (uchar)ll;
176
+
177
+ got = fread(Data+2, 1, itemlen-2, infile); // Read the whole section.
178
+ if (got != itemlen-2){
179
+ ErrFatal("Premature end of file?");
180
+ }
181
+ SectionsRead += 1;
182
+
183
+ switch(marker){
184
+
185
+ case M_SOS: // stop before hitting compressed data
186
+ // If reading entire image is requested, read the rest of the data.
187
+ if (ReadMode & READ_IMAGE){
188
+ int cp, ep, size;
189
+ // Determine how much file is left.
190
+ cp = ftell(infile);
191
+ fseek(infile, 0, SEEK_END);
192
+ ep = ftell(infile);
193
+ fseek(infile, cp, SEEK_SET);
194
+
195
+ size = ep-cp;
196
+ Data = (uchar *)malloc(size);
197
+ if (Data == NULL){
198
+ ErrFatal("could not allocate data for entire image");
199
+ }
200
+
201
+ got = fread(Data, 1, size, infile);
202
+ if (got != size){
203
+ ErrFatal("could not read the rest of the image");
204
+ }
205
+
206
+ CheckSectionsAllocated();
207
+ Sections[SectionsRead].Data = Data;
208
+ Sections[SectionsRead].Size = size;
209
+ Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
210
+ SectionsRead ++;
211
+ HaveAll = 1;
212
+ }
213
+ return TRUE;
214
+
215
+ case M_EOI: // in case it's a tables-only JPEG stream
216
+ fprintf(stderr,"No image in jpeg!\n");
217
+ return FALSE;
218
+
219
+ case M_COM: // Comment section
220
+ if (HaveCom || ((ReadMode & READ_METADATA) == 0)){
221
+ // Discard this section.
222
+ free(Sections[--SectionsRead].Data);
223
+ }else{
224
+ process_COM(Data, itemlen);
225
+ HaveCom = TRUE;
226
+ }
227
+ break;
228
+
229
+ case M_JFIF:
230
+ // Regular jpegs always have this tag, exif images have the exif
231
+ // marker instead, althogh ACDsee will write images with both markers.
232
+ // this program will re-create this marker on absence of exif marker.
233
+ // hence no need to keep the copy from the file.
234
+ if (memcmp(Data+2, "JFIF\0",5)){
235
+ fprintf(stderr,"Header missing JFIF marker\n");
236
+ }
237
+ if (itemlen < 16){
238
+ fprintf(stderr,"Jfif header too short\n");
239
+ goto ignore;
240
+ }
241
+
242
+ ImageInfo.JfifHeader.Present = TRUE;
243
+ ImageInfo.JfifHeader.ResolutionUnits = Data[9];
244
+ ImageInfo.JfifHeader.XDensity = (Data[10]<<8) | Data[11];
245
+ ImageInfo.JfifHeader.YDensity = (Data[12]<<8) | Data[13];
246
+ if (ShowTags){
247
+ printf("JFIF SOI marker: Units: %d ",ImageInfo.JfifHeader.ResolutionUnits);
248
+ switch(ImageInfo.JfifHeader.ResolutionUnits){
249
+ case 0: printf("(aspect ratio)"); break;
250
+ case 1: printf("(dots per inch)"); break;
251
+ case 2: printf("(dots per cm)"); break;
252
+ default: printf("(unknown)"); break;
253
+ }
254
+ printf(" X-density=%d Y-density=%d\n",ImageInfo.JfifHeader.XDensity, ImageInfo.JfifHeader.YDensity);
255
+
256
+ if (Data[14] || Data[15]){
257
+ fprintf(stderr,"Ignoring jfif header thumbnail\n");
258
+ }
259
+ }
260
+
261
+ ignore:
262
+
263
+ free(Sections[--SectionsRead].Data);
264
+ break;
265
+
266
+ case M_EXIF:
267
+ // There can be different section using the same marker.
268
+ if (ReadMode & READ_METADATA){
269
+ if (memcmp(Data+2, "Exif", 4) == 0){
270
+ process_EXIF(Data, itemlen);
271
+ break;
272
+ }else if (memcmp(Data+2, "http:", 5) == 0){
273
+ Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes.
274
+ if (ShowTags){
275
+ printf("Image cotains XMP section, %d bytes long\n", itemlen);
276
+ if (ShowTags){
277
+ ShowXmp(Sections[SectionsRead-1]);
278
+ }
279
+ }
280
+ break;
281
+ }
282
+ }
283
+ // Oterwise, discard this section.
284
+ free(Sections[--SectionsRead].Data);
285
+ break;
286
+
287
+ case M_IPTC:
288
+ if (ReadMode & READ_METADATA){
289
+ if (ShowTags){
290
+ printf("Image cotains IPTC section, %d bytes long\n", itemlen);
291
+ }
292
+ // Note: We just store the IPTC section. Its relatively straightforward
293
+ // and we don't act on any part of it, so just display it at parse time.
294
+ }else{
295
+ free(Sections[--SectionsRead].Data);
296
+ }
297
+ break;
298
+
299
+ case M_SOF0:
300
+ case M_SOF1:
301
+ case M_SOF2:
302
+ case M_SOF3:
303
+ case M_SOF5:
304
+ case M_SOF6:
305
+ case M_SOF7:
306
+ case M_SOF9:
307
+ case M_SOF10:
308
+ case M_SOF11:
309
+ case M_SOF13:
310
+ case M_SOF14:
311
+ case M_SOF15:
312
+ process_SOFn(Data, marker);
313
+ break;
314
+ default:
315
+ // Skip any other sections.
316
+ if (ShowTags){
317
+ printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen);
318
+ }
319
+ break;
320
+ }
321
+ }
322
+ return TRUE;
323
+ }
324
+
325
+ //--------------------------------------------------------------------------
326
+ // Discard read data.
327
+ //--------------------------------------------------------------------------
328
+ void DiscardData(void)
329
+ {
330
+ int a;
331
+
332
+ for (a=0;a<SectionsRead;a++){
333
+ free(Sections[a].Data);
334
+ }
335
+
336
+ memset(&ImageInfo, 0, sizeof(ImageInfo));
337
+ SectionsRead = 0;
338
+ HaveAll = 0;
339
+ }
340
+
341
+ //--------------------------------------------------------------------------
342
+ // Read image data.
343
+ //--------------------------------------------------------------------------
344
+ int ReadJpegFile(const char * FileName, ReadMode_t ReadMode)
345
+ {
346
+ FILE * infile;
347
+ int ret;
348
+
349
+ infile = fopen(FileName, "rb"); // Unix ignores 'b', windows needs it.
350
+
351
+ if (infile == NULL) {
352
+ fprintf(stderr, "can't open '%s'\n", FileName);
353
+ return FALSE;
354
+ }
355
+
356
+
357
+ // Scan the JPEG headers.
358
+ ret = ReadJpegSections(infile, ReadMode);
359
+ if (!ret){
360
+ fprintf(stderr,"Not JPEG: %s\n",FileName);
361
+ }
362
+
363
+ fclose(infile);
364
+
365
+ if (ret == FALSE){
366
+ DiscardData();
367
+ }
368
+ return ret;
369
+ }
370
+
371
+
372
+ //--------------------------------------------------------------------------
373
+ // Replace or remove exif thumbnail
374
+ //--------------------------------------------------------------------------
375
+ int SaveThumbnail(char * ThumbFileName)
376
+ {
377
+ FILE * ThumbnailFile;
378
+
379
+ if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailSize == 0){
380
+ fprintf(stderr,"Image contains no thumbnail\n");
381
+ return FALSE;
382
+ }
383
+
384
+ if (strcmp(ThumbFileName, "-") == 0){
385
+ // A filename of '-' indicates thumbnail goes to stdout.
386
+ // This doesn't make much sense under Windows, so this feature is unix only.
387
+ ThumbnailFile = stdout;
388
+ }else{
389
+ ThumbnailFile = fopen(ThumbFileName,"wb");
390
+ }
391
+
392
+ if (ThumbnailFile){
393
+ uchar * ThumbnailPointer;
394
+ Section_t * ExifSection;
395
+ ExifSection = FindSection(M_EXIF);
396
+ ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
397
+
398
+ fwrite(ThumbnailPointer, ImageInfo.ThumbnailSize ,1, ThumbnailFile);
399
+ fclose(ThumbnailFile);
400
+ return TRUE;
401
+ }else{
402
+ ErrFatal("Could not write thumbnail file");
403
+ return FALSE;
404
+ }
405
+ }
406
+
407
+ //--------------------------------------------------------------------------
408
+ // Replace or remove exif thumbnail
409
+ //--------------------------------------------------------------------------
410
+ int ReplaceThumbnail(const char * ThumbFileName)
411
+ {
412
+ FILE * ThumbnailFile;
413
+ int ThumbLen, NewExifSize;
414
+ Section_t * ExifSection;
415
+ uchar * ThumbnailPointer;
416
+
417
+ if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){
418
+ if (ThumbFileName == NULL){
419
+ // Delete of nonexistent thumbnail (not even pointers present)
420
+ // No action, no error.
421
+ return FALSE;
422
+ }
423
+
424
+ // Adding or removing of thumbnail is not possible - that would require rearranging
425
+ // of the exif header, which is risky, and jhad doesn't know how to do.
426
+ fprintf(stderr,"Image contains no thumbnail to replace - add is not possible\n");
427
+ return FALSE;
428
+ }
429
+
430
+ if (ThumbFileName){
431
+ ThumbnailFile = fopen(ThumbFileName,"rb");
432
+
433
+ if (ThumbnailFile == NULL){
434
+ ErrFatal("Could not read thumbnail file");
435
+ return FALSE;
436
+ }
437
+
438
+ // get length
439
+ fseek(ThumbnailFile, 0, SEEK_END);
440
+
441
+ ThumbLen = ftell(ThumbnailFile);
442
+ fseek(ThumbnailFile, 0, SEEK_SET);
443
+
444
+ if (ThumbLen + ImageInfo.ThumbnailOffset > 0x10000-20){
445
+ ErrFatal("Thumbnail is too large to insert into exif header");
446
+ }
447
+ }else{
448
+ if (ImageInfo.ThumbnailSize == 0){
449
+ return FALSE;
450
+ }
451
+
452
+ ThumbLen = 0;
453
+ ThumbnailFile = NULL;
454
+ }
455
+
456
+ ExifSection = FindSection(M_EXIF);
457
+
458
+ NewExifSize = ImageInfo.ThumbnailOffset+8+ThumbLen;
459
+ ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize);
460
+
461
+ ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
462
+
463
+ if (ThumbnailFile){
464
+ fread(ThumbnailPointer, ThumbLen, 1, ThumbnailFile);
465
+ fclose(ThumbnailFile);
466
+ }
467
+
468
+ ImageInfo.ThumbnailSize = ThumbLen;
469
+
470
+ Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, ThumbLen);
471
+
472
+ ExifSection->Data[0] = (uchar)(NewExifSize >> 8);
473
+ ExifSection->Data[1] = (uchar)NewExifSize;
474
+ ExifSection->Size = NewExifSize;
475
+
476
+ return TRUE;
477
+ }
478
+
479
+
480
+ //--------------------------------------------------------------------------
481
+ // Discard everything but the exif and comment sections.
482
+ //--------------------------------------------------------------------------
483
+ void DiscardAllButExif(void)
484
+ {
485
+ Section_t ExifKeeper;
486
+ Section_t CommentKeeper;
487
+ Section_t IptcKeeper;
488
+ Section_t XmpKeeper;
489
+ int a;
490
+
491
+ memset(&ExifKeeper, 0, sizeof(ExifKeeper));
492
+ memset(&CommentKeeper, 0, sizeof(CommentKeeper));
493
+ memset(&IptcKeeper, 0, sizeof(IptcKeeper));
494
+ memset(&XmpKeeper, 0, sizeof(IptcKeeper));
495
+
496
+ for (a=0;a<SectionsRead;a++){
497
+ if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){
498
+ ExifKeeper = Sections[a];
499
+ }else if (Sections[a].Type == M_XMP && XmpKeeper.Type == 0){
500
+ XmpKeeper = Sections[a];
501
+ }else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){
502
+ CommentKeeper = Sections[a];
503
+ }else if (Sections[a].Type == M_IPTC && IptcKeeper.Type == 0){
504
+ IptcKeeper = Sections[a];
505
+ }else{
506
+ free(Sections[a].Data);
507
+ }
508
+ }
509
+ SectionsRead = 0;
510
+ if (ExifKeeper.Type){
511
+ CheckSectionsAllocated();
512
+ Sections[SectionsRead++] = ExifKeeper;
513
+ }
514
+ if (CommentKeeper.Type){
515
+ CheckSectionsAllocated();
516
+ Sections[SectionsRead++] = CommentKeeper;
517
+ }
518
+ if (IptcKeeper.Type){
519
+ CheckSectionsAllocated();
520
+ Sections[SectionsRead++] = IptcKeeper;
521
+ }
522
+
523
+ if (XmpKeeper.Type){
524
+ CheckSectionsAllocated();
525
+ Sections[SectionsRead++] = XmpKeeper;
526
+ }
527
+ }
528
+
529
+ //--------------------------------------------------------------------------
530
+ // Write image data back to disk.
531
+ //--------------------------------------------------------------------------
532
+ void WriteJpegFile(const char * FileName)
533
+ {
534
+ FILE * outfile;
535
+ int a;
536
+
537
+ if (!HaveAll){
538
+ ErrFatal("Can't write back - didn't read all");
539
+ }
540
+
541
+ outfile = fopen(FileName,"wb");
542
+ if (outfile == NULL){
543
+ ErrFatal("Could not open file for write");
544
+ }
545
+
546
+ // Initial static jpeg marker.
547
+ fputc(0xff,outfile);
548
+ fputc(0xd8,outfile);
549
+
550
+ if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){
551
+ // The image must start with an exif or jfif marker. If we threw those away, create one.
552
+ static uchar JfifHead[18] = {
553
+ 0xff, M_JFIF,
554
+ 0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01,
555
+ 0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00
556
+ };
557
+
558
+ if (ImageInfo.ResolutionUnit == 2 || ImageInfo.ResolutionUnit == 3){
559
+ // Use the exif resolution info to fill out the jfif header.
560
+ // Usually, for exif images, there's no jfif header, so if wediscard
561
+ // the exif header, use info from the exif header for the jfif header.
562
+
563
+ ImageInfo.JfifHeader.ResolutionUnits = (char)(ImageInfo.ResolutionUnit-1);
564
+ // Jfif is 1 and 2, Exif is 2 and 3 for In and cm respecively
565
+ ImageInfo.JfifHeader.XDensity = (int)ImageInfo.xResolution;
566
+ ImageInfo.JfifHeader.YDensity = (int)ImageInfo.yResolution;
567
+ }
568
+
569
+ JfifHead[11] = ImageInfo.JfifHeader.ResolutionUnits;
570
+ JfifHead[12] = (uchar)(ImageInfo.JfifHeader.XDensity >> 8);
571
+ JfifHead[13] = (uchar)ImageInfo.JfifHeader.XDensity;
572
+ JfifHead[14] = (uchar)(ImageInfo.JfifHeader.YDensity >> 8);
573
+ JfifHead[15] = (uchar)ImageInfo.JfifHeader.YDensity;
574
+
575
+
576
+ fwrite(JfifHead, 18, 1, outfile);
577
+
578
+ // use the values from the exif data for the jfif header, if we have found values
579
+ if (ImageInfo.ResolutionUnit != 0) {
580
+ // JFIF.ResolutionUnit is {1,2}, EXIF.ResolutionUnit is {2,3}
581
+ JfifHead[11] = (uchar)ImageInfo.ResolutionUnit - 1;
582
+ }
583
+ if (ImageInfo.xResolution > 0.0 && ImageInfo.yResolution > 0.0) {
584
+ JfifHead[12] = (uchar)((int)ImageInfo.xResolution>>8);
585
+ JfifHead[13] = (uchar)((int)ImageInfo.xResolution);
586
+
587
+ JfifHead[14] = (uchar)((int)ImageInfo.yResolution>>8);
588
+ JfifHead[15] = (uchar)((int)ImageInfo.yResolution);
589
+ }
590
+ }
591
+
592
+
593
+ // Write all the misc sections
594
+ for (a=0;a<SectionsRead-1;a++){
595
+ fputc(0xff,outfile);
596
+ fputc((unsigned char)Sections[a].Type, outfile);
597
+ fwrite(Sections[a].Data, Sections[a].Size, 1, outfile);
598
+ }
599
+
600
+ // Write the remaining image data.
601
+ fwrite(Sections[a].Data, Sections[a].Size, 1, outfile);
602
+
603
+ fclose(outfile);
604
+ }
605
+
606
+
607
+ //--------------------------------------------------------------------------
608
+ // Check if image has exif header.
609
+ //--------------------------------------------------------------------------
610
+ Section_t * FindSection(int SectionType)
611
+ {
612
+ int a;
613
+
614
+ for (a=0;a<SectionsRead;a++){
615
+ if (Sections[a].Type == SectionType){
616
+ return &Sections[a];
617
+ }
618
+ }
619
+ // Could not be found.
620
+ return NULL;
621
+ }
622
+
623
+ //--------------------------------------------------------------------------
624
+ // Remove a certain type of section.
625
+ //--------------------------------------------------------------------------
626
+ int RemoveSectionType(int SectionType)
627
+ {
628
+ int a;
629
+ for (a=0;a<SectionsRead-1;a++){
630
+ if (Sections[a].Type == SectionType){
631
+ // Free up this section
632
+ free (Sections[a].Data);
633
+ // Move succeding sections back by one to close space in array.
634
+ memmove(Sections+a, Sections+a+1, sizeof(Section_t) * (SectionsRead-a));
635
+ SectionsRead -= 1;
636
+ return TRUE;
637
+ }
638
+ }
639
+ return FALSE;
640
+ }
641
+
642
+ //--------------------------------------------------------------------------
643
+ // Remove sectons not part of image and not exif or comment sections.
644
+ //--------------------------------------------------------------------------
645
+ int RemoveUnknownSections(void)
646
+ {
647
+ int a;
648
+ int Modified = FALSE;
649
+ for (a=0;a<SectionsRead-1;){
650
+ switch(Sections[a].Type){
651
+ case M_SOF0:
652
+ case M_SOF1:
653
+ case M_SOF2:
654
+ case M_SOF3:
655
+ case M_SOF5:
656
+ case M_SOF6:
657
+ case M_SOF7:
658
+ case M_SOF9:
659
+ case M_SOF10:
660
+ case M_SOF11:
661
+ case M_SOF13:
662
+ case M_SOF14:
663
+ case M_SOF15:
664
+ case M_SOI:
665
+ case M_EOI:
666
+ case M_SOS:
667
+ case M_JFIF:
668
+ case M_EXIF:
669
+ case M_XMP:
670
+ case M_COM:
671
+ case M_DQT:
672
+ case M_DHT:
673
+ case M_DRI:
674
+ case M_IPTC:
675
+ // keep.
676
+ a++;
677
+ break;
678
+ default:
679
+ // Unknown. Delete.
680
+ free (Sections[a].Data);
681
+ // Move succeding sections back by one to close space in array.
682
+ memmove(Sections+a, Sections+a+1, sizeof(Section_t) * (SectionsRead-a));
683
+ SectionsRead -= 1;
684
+ Modified = TRUE;
685
+ }
686
+ }
687
+ return Modified;
688
+ }
689
+
690
+ //--------------------------------------------------------------------------
691
+ // Add a section (assume it doesn't already exist) - used for
692
+ // adding comment sections and exif sections
693
+ //--------------------------------------------------------------------------
694
+ Section_t * CreateSection(int SectionType, unsigned char * Data, int Size)
695
+ {
696
+ Section_t * NewSection;
697
+ int a;
698
+ int NewIndex;
699
+ NewIndex = 2;
700
+
701
+ if (SectionType == M_EXIF) NewIndex = 0; // Exif alwas goes first!
702
+
703
+ // Insert it in third position - seems like a safe place to put
704
+ // things like comments.
705
+
706
+ if (SectionsRead < NewIndex){
707
+ ErrFatal("Too few sections!");
708
+ }
709
+
710
+ CheckSectionsAllocated();
711
+ for (a=SectionsRead;a>NewIndex;a--){
712
+ Sections[a] = Sections[a-1];
713
+ }
714
+ SectionsRead += 1;
715
+
716
+ NewSection = Sections+NewIndex;
717
+
718
+ NewSection->Type = SectionType;
719
+ NewSection->Size = Size;
720
+ NewSection->Data = Data;
721
+
722
+ return NewSection;
723
+ }
724
+
725
+
726
+ //--------------------------------------------------------------------------
727
+ // Initialisation.
728
+ //--------------------------------------------------------------------------
729
+ void ResetJpgfile(void)
730
+ {
731
+ if (Sections == NULL){
732
+ Sections = (Section_t *)malloc(sizeof(Section_t)*5);
733
+ SectionsAllocated = 5;
734
+ }
735
+
736
+ SectionsRead = 0;
737
+ HaveAll = 0;
738
+ }