4 * This is a utility program to get and set the Exif Orientation Tag.
\r
5 * It can be used together with jpegtran in scripts for automatic
\r
6 * orientation correction of digital camera pictures.
\r
8 * The Exif orientation value gives the orientation of the camera
\r
9 * relative to the scene when the image was captured. The relation
\r
10 * of the '0th row' and '0th column' to visual position is shown as
\r
13 * Value | 0th Row | 0th Column
\r
14 * ------+-------------+-----------
\r
15 * 1 | top | left side
\r
16 * 2 | top | rigth side
\r
17 * 3 | bottom | rigth side
\r
18 * 4 | bottom | left side
\r
19 * 5 | left side | top
\r
20 * 6 | right side | top
\r
21 * 7 | right side | bottom
\r
22 * 8 | left side | bottom
\r
24 * For convenience, here is what the letter F would look like if it were
\r
25 * tagged correctly and displayed by a program that ignores the orientation
\r
30 * 888888 888888 88 88 8888888888 88 88 8888888888
\r
31 * 88 88 88 88 88 88 88 88 88 88 88 88
\r
32 * 8888 8888 8888 8888 88 8888888888 8888888888 88
\r
34 * 88 88 888888 888888
\r
41 static FILE * myfile; /* My JPEG file */
\r
43 static unsigned char exif_data[65536L];
\r
45 /* Return next input byte, or EOF if no more */
\r
46 #define NEXTBYTE() getc(myfile)
\r
48 /* Error exit handler */
\r
49 #define ERREXIT(msg) (exit(0))
\r
51 /* Read one byte, testing for EOF */
\r
59 ERREXIT("Premature EOF in JPEG file");
\r
63 /* Read 2 bytes, convert to unsigned int */
\r
64 /* All 2-byte quantities in JPEG markers are MSB first */
\r
72 ERREXIT("Premature EOF in JPEG file");
\r
75 ERREXIT("Premature EOF in JPEG file");
\r
76 return (((unsigned int) c1) << 8) + ((unsigned int) c2);
\r
79 static const char * progname; /* program name for error messages */
\r
83 /* complain about bad command line */
\r
85 fprintf(out, "jpegexiforient reads or writes the Exif Orientation Tag ");
\r
86 fprintf(out, "in a JPEG Exif file.\n");
\r
88 fprintf(out, "Usage: %s [switches] jpegfile\n", progname);
\r
90 fprintf(out, "Switches:\n");
\r
91 fprintf(out, " --help display this help and exit\n");
\r
92 fprintf(out, " --version output version information and exit\n");
\r
93 fprintf(out, " -n Do not output the trailing newline\n");
\r
94 fprintf(out, " -1 .. -8 Set orientation value 1 .. 8\n");
\r
102 main (int argc, char **argv)
\r
104 int n_flag, set_flag;
\r
105 unsigned int length, i;
\r
106 int is_motorola; /* Flag for byte order */
\r
107 unsigned int offset, number_of_tags, tagnum;
\r
109 progname = argv[0];
\r
110 if (progname == NULL || progname[0] == 0)
\r
111 progname = "jpegexiforient"; /* in case C library doesn't provide it */
\r
113 if (argc < 2) { usage(stderr); return 1; }
\r
115 n_flag = 0; set_flag = 0;
\r
118 while (argv[i][0] == '-') {
\r
119 switch (argv[i][1]) {
\r
121 switch (argv[i][2]) {
\r
122 case 'h': usage(stdout); return 0;
\r
123 case 'v': fprintf(stdout,"jpegexiforient\n"); return 0;
\r
136 set_flag = argv[i][1] - '0';
\r
139 usage(stderr); return 1;
\r
141 if (++i >= argc) { usage(stderr); return 1; }
\r
145 if ((myfile = fopen(argv[i], "rb+")) == NULL) {
\r
146 fprintf(stderr, "%s: can't open %s\n", progname, argv[i]);
\r
150 if ((myfile = fopen(argv[i], "rb")) == NULL) {
\r
151 fprintf(stderr, "%s: can't open %s\n", progname, argv[i]);
\r
156 /* Read File head, check for JPEG SOI + Exif APP1 */
\r
157 for (i = 0; i < 4; i++)
\r
158 exif_data[i] = (unsigned char) read_1_byte();
\r
159 if (exif_data[0] != 0xFF ||
\r
160 exif_data[1] != 0xD8 ||
\r
161 exif_data[2] != 0xFF ||
\r
162 exif_data[3] != 0xE1)
\r
165 /* Get the marker parameter length count */
\r
166 length = read_2_bytes();
\r
167 /* Length includes itself, so must be at least 2 */
\r
168 /* Following Exif data length must be at least 6 */
\r
172 /* Read Exif head, check for "Exif" */
\r
173 for (i = 0; i < 6; i++)
\r
174 exif_data[i] = (unsigned char) read_1_byte();
\r
175 if (exif_data[0] != 0x45 ||
\r
176 exif_data[1] != 0x78 ||
\r
177 exif_data[2] != 0x69 ||
\r
178 exif_data[3] != 0x66 ||
\r
179 exif_data[4] != 0 ||
\r
182 /* Read Exif body */
\r
183 for (i = 0; i < length; i++)
\r
184 exif_data[i] = (unsigned char) read_1_byte();
\r
186 if (length < 12) return 0; /* Length of an IFD entry */
\r
188 /* Discover byte order */
\r
189 if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
\r
191 else if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
\r
196 /* Check Tag Mark */
\r
198 if (exif_data[2] != 0) return 0;
\r
199 if (exif_data[3] != 0x2A) return 0;
\r
201 if (exif_data[3] != 0) return 0;
\r
202 if (exif_data[2] != 0x2A) return 0;
\r
205 /* Get first IFD offset (offset to IFD0) */
\r
207 if (exif_data[4] != 0) return 0;
\r
208 if (exif_data[5] != 0) return 0;
\r
209 offset = exif_data[6];
\r
211 offset += exif_data[7];
\r
213 if (exif_data[7] != 0) return 0;
\r
214 if (exif_data[6] != 0) return 0;
\r
215 offset = exif_data[5];
\r
217 offset += exif_data[4];
\r
219 if (offset > length - 2) return 0; /* check end of data segment */
\r
221 /* Get the number of directory entries contained in this IFD */
\r
223 number_of_tags = exif_data[offset];
\r
224 number_of_tags <<= 8;
\r
225 number_of_tags += exif_data[offset+1];
\r
227 number_of_tags = exif_data[offset+1];
\r
228 number_of_tags <<= 8;
\r
229 number_of_tags += exif_data[offset];
\r
231 if (number_of_tags == 0) return 0;
\r
234 /* Search for Orientation Tag in IFD0 */
\r
236 if (offset > length - 12) return 0; /* check end of data segment */
\r
237 /* Get Tag number */
\r
239 tagnum = exif_data[offset];
\r
241 tagnum += exif_data[offset+1];
\r
243 tagnum = exif_data[offset+1];
\r
245 tagnum += exif_data[offset];
\r
247 if (tagnum == 0x0112) break; /* found Orientation Tag */
\r
248 if (--number_of_tags == 0) return 0;
\r
253 /* Set the Orientation value */
\r
255 exif_data[offset+2] = 0; /* Format = unsigned short (2 octets) */
\r
256 exif_data[offset+3] = 3;
\r
257 exif_data[offset+4] = 0; /* Number Of Components = 1 */
\r
258 exif_data[offset+5] = 0;
\r
259 exif_data[offset+6] = 0;
\r
260 exif_data[offset+7] = 1;
\r
261 exif_data[offset+8] = 0;
\r
262 exif_data[offset+9] = (unsigned char)set_flag;
\r
263 exif_data[offset+10] = 0;
\r
264 exif_data[offset+11] = 0;
\r
266 exif_data[offset+2] = 3; /* Format = unsigned short (2 octets) */
\r
267 exif_data[offset+3] = 0;
\r
268 exif_data[offset+4] = 1; /* Number Of Components = 1 */
\r
269 exif_data[offset+5] = 0;
\r
270 exif_data[offset+6] = 0;
\r
271 exif_data[offset+7] = 0;
\r
272 exif_data[offset+8] = (unsigned char)set_flag;
\r
273 exif_data[offset+9] = 0;
\r
274 exif_data[offset+10] = 0;
\r
275 exif_data[offset+11] = 0;
\r
277 fseek(myfile, (4 + 2 + 6 + 2) + offset, SEEK_SET);
\r
278 fwrite(exif_data + 2 + offset, 1, 10, myfile);
\r
280 /* Get the Orientation value */
\r
282 if (exif_data[offset+8] != 0) return 0;
\r
283 set_flag = exif_data[offset+9];
\r
285 if (exif_data[offset+9] != 0) return 0;
\r
286 set_flag = exif_data[offset+8];
\r
288 if (set_flag > 8) return 0;
\r
291 /* Write out Orientation value */
\r
293 printf("%c", '0' + set_flag);
\r
295 printf("%c\n", '0' + set_flag);
\r