xref: /haiku/src/tools/translation/tiffinfo/tiffinfo.cpp (revision c90684742e7361651849be4116d0e5de3a817194)
1 /*****************************************************************************/
2 // tiffinfo
3 // Written by Michael Wilber, OBOS Translation Kit Team
4 //
5 // Version:
6 //
7 // tiffinfo is a command line program for displaying text information about
8 // TIFF images. This information includes a listing of every field (tag) in
9 // the TIFF file, for every image in the file.  Also, for some fields,
10 // the numerical value for the field is converted to descriptive text.
11 //
12 // This application and all source files used in its construction, except
13 // where noted, are licensed under the MIT License, and have been written
14 // and are:
15 //
16 // Copyright (c) 2003 OpenBeOS Project
17 //
18 // Permission is hereby granted, free of charge, to any person obtaining a
19 // copy of this software and associated documentation files (the "Software"),
20 // to deal in the Software without restriction, including without limitation
21 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
22 // and/or sell copies of the Software, and to permit persons to whom the
23 // Software is furnished to do so, subject to the following conditions:
24 //
25 // The above copyright notice and this permission notice shall be included
26 // in all copies or substantial portions of the Software.
27 //
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
31 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34 // DEALINGS IN THE SOFTWARE.
35 /*****************************************************************************/
36 #include <ByteOrder.h>
37 #include <File.h>
38 #include <StorageDefs.h>
39 #include <iostream.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 struct IFDEntry {
45 	uint16 tag;
46 		// uniquely identifies the field
47 	uint16 fieldType;
48 		// number, string, float, etc.
49 	uint32 count;
50 		// length / number of values
51 
52 	// The actual value or the file offset
53 	// where the actual value is located
54 	union {
55 		float 	floatval;
56 		uint32	longval;
57 		uint16	shortvals[2];
58 		uint8	bytevals[4];
59 	};
60 };
61 
62 enum ENTRY_TYPE {
63 	TIFF_BYTE = 1,
64 	TIFF_ASCII,
65 	TIFF_SHORT,
66 	TIFF_LONG,
67 	TIFF_RATIONAL,
68 	TIFF_SBYTE,
69 	TIFF_UNDEFINED,
70 	TIFF_SSHORT,
71 	TIFF_SLONG,
72 	TIFF_SRATIONAL,
73 	TIFF_FLOAT,
74 	TIFF_DOUBLE
75 };
76 
77 const char *
78 get_type_string(uint16 type)
79 {
80 	const char *kstrTypes[] = {
81 		"Byte",
82 		"ASCII",
83 		"Short",
84 		"Long",
85 		"Rational",
86 		"Signed Byte",
87 		"Undefined",
88 		"Signed Short",
89 		"Signed Long",
90 		"Signed Rational",
91 		"Float",
92 		"Double"
93 	};
94 
95 	if (type >= 1 && type <= 12)
96 		return kstrTypes[type - 1];
97 	else
98 		return "?";
99 }
100 
101 const char *
102 get_tag_string(uint16 tag)
103 {
104 	switch (tag) {
105 		case 254: return "New Subfile Type";
106 		case 255: return "Subfile Type";
107 		case 256: return "Image Width";
108 		case 257: return "Image Height";
109 		case 258: return "Bits Per Sample";
110 		case 259: return "Compression";
111 		case 262: return "Photometric Interpretation";
112 		case 263: return "Thresholding";
113 		case 264: return "CellWidth";
114 		case 265: return "CellLength";
115 		case 266: return "Fill Order";
116 		case 269: return "Document Name";
117 		case 270: return "Image Description";
118 		case 271: return "Make";
119 		case 272: return "Model";
120 		case 273: return "Strip Offsets";
121 		case 274: return "Orientation";
122 		case 277: return "Samples Per Pixel";
123 		case 278: return "Rows Per Strip";
124 		case 279: return "Strip Byte Counts";
125 		case 280: return "Min Sample Value";
126 		case 281: return "Max Sample Value";
127 		case 282: return "X Resolution";
128 		case 283: return "Y Resolution";
129 		case 284: return "Planar Configuration";
130 		case 285: return "Page Name";
131 		case 286: return "X Position";
132 		case 287: return "Y Position";
133 		case 288: return "Free Offsets";
134 		case 289: return "Free Byte Counts";
135 		case 290: return "Gray Response Unit";
136 		case 291: return "Gray Response Curve";
137 		case 292: return "T4 Options";
138 		case 293: return "T6 Options";
139 		case 296: return "Resolution Unit";
140 		case 297: return "Page Number";
141 		case 305: return "Software";
142 		case 306: return "DateTime";
143 		case 315: return "Artist";
144 		case 316: return "Host Computer";
145 		case 320: return "Color Map";
146 		case 322: return "Tile Width";
147 		case 323: return "Tile Height";
148 		case 324: return "Tile Offsets";
149 		case 325: return "Tile Byte Counts";
150 		case 338: return "Extra Samples";
151 		case 339: return "Sample Format";
152 		case 529: return "YCbCr Coefficients";
153 		case 530: return "YCbCr Subsampling";
154 		case 531: return "YCbCr Positioning";
155 		case 532: return "Reference Black White";
156 		case 32995: return "Matteing";
157 		case 32996: return "Data Type"; // obseleted by SampleFormat tag
158 		case 32997: return "Image Depth"; // tile / strip calculations
159 		case 32998: return "Tile Depth"; // tile / strip calculations
160 		case 33432: return "Copyright";
161 		case 37439: return "StoNits?";
162 
163 		default:
164 			return "?";
165 	}
166 }
167 
168 void
169 print_ifd_value(IFDEntry &entry, BFile &file, swap_action swp)
170 {
171 	switch (entry.tag) {
172 		case 254: // NewSubfileType
173 			if (entry.count == 1 && entry.fieldType == TIFF_LONG) {
174 				if (entry.longval & 1)
175 					printf("Low Res (1) ");
176 				if (entry.longval & 2)
177 					printf("Page (2) ");
178 				if (entry.longval & 4)
179 					printf("Mask (4) ");
180 
181 				printf("(0x%.8lx)", entry.longval);
182 				return;
183 			}
184 			break;
185 
186 		case 256: // ImageWidth
187 		case 257: // ImageHeight
188 			if (entry.count == 1) {
189 				printf("%d",
190 					((entry.fieldType == TIFF_SHORT) ?
191 						entry.shortvals[0] : static_cast<unsigned int>(entry.longval)));
192 				return;
193 			}
194 			break;
195 
196 		case 259:
197 			if (entry.count == 1 && entry.fieldType == TIFF_SHORT) {
198 				switch (entry.shortvals[0]) {
199 					case 1:
200 						printf("No Compression (1)");
201 						return;
202 					case 2:
203 						printf("CCITT Group 3 1-Dimensional Modified Huffman run-length encoding (2)");
204 						return;
205 					case 3:
206 						printf("Fax Group 3 (3)");
207 						return;
208 					case 4:
209 						printf("Fax Group 4 (4)");
210 						return;
211 					case 5:
212 						printf("LZW (5)");
213 						return;
214 					case 32773:
215 						printf("PackBits (32773)");
216 						return;
217 				}
218 			}
219 			break;
220 
221 		case 262: // PhotometricInterpretation
222 			if (entry.count == 1 && entry.fieldType == TIFF_SHORT) {
223 				switch (entry.shortvals[0]) {
224 					case 0:
225 						printf("White is Zero (%d)", entry.shortvals[0]);
226 						return;
227 					case 1:
228 						printf("Black is Zero (%d)", entry.shortvals[0]);
229 						return;
230 					case 2:
231 						printf("RGB (%d)", entry.shortvals[0]);
232 						return;
233 					case 3:
234 						printf("Palette Color (%d)", entry.shortvals[0]);
235 						return;
236 					case 4:
237 						printf("Transparency Mask (%d)", entry.shortvals[0]);
238 						return;
239 				}
240 			}
241 			break;
242 
243 		case 274: // Orientation
244 			if (entry.count == 1 && entry.fieldType == TIFF_SHORT) {
245 				switch (entry.shortvals[0]) {
246 					case 1:
247 						printf("top to bottom, left to right (1)");
248 						return;
249 					case 2:
250 						printf("top to bottom, right to left (2)");
251 						return;
252 					case 3:
253 						printf("bottom to top, right to left (3)");
254 						return;
255 					case 4:
256 						printf("bottom to top, left to right (4)");
257 						return;
258 					case 5:
259 						printf("left to right, top to bottom (5)");
260 						return;
261 					case 6:
262 						printf("right to left, top to bottom (6)");
263 						return;
264 					case 7:
265 						printf("right to left, bottom to top (7)");
266 						return;
267 					case 8:
268 						printf("left to right, bottom to top (8)");
269 						return;
270 				}
271 			}
272 			break;
273 
274 		case 278: // RowsPerStrip
275 		{
276 			const uint32 ksinglestrip = 0xffffffffUL;
277 			printf("%u",
278 					static_cast<unsigned int>(entry.longval));
279 			if (entry.longval == ksinglestrip)
280 				printf(" (All rows in first strip)");
281 			return;
282 		}
283 
284 		case 284: // PlanarConfiguration
285 			if (entry.count == 1 && entry.fieldType == TIFF_SHORT) {
286 				if (entry.shortvals[0] == 1) {
287 					printf("Chunky (%d)", entry.shortvals[0]);
288 					return;
289 				}
290 				else if (entry.shortvals[0] == 2) {
291 					printf("Planar (%d)", entry.shortvals[0]);
292 					return;
293 				}
294 			}
295 			break;
296 
297 		case 296: // ResolutionUnit
298 			if (entry.count == 1 && entry.fieldType == TIFF_SHORT) {
299 				switch (entry.shortvals[0]) {
300 					case 1:
301 						printf("None (%d)", entry.shortvals[0]);
302 						return;
303 					case 2:
304 						printf("Inch (%d)", entry.shortvals[0]);
305 						return;
306 					case 3:
307 						printf("Cenimeter (%d)", entry.shortvals[0]);
308 						return;
309 				}
310 			}
311 			break;
312 
313 		default:
314 			if (entry.fieldType == TIFF_ASCII) {
315 				char ascfield[256] = { 0 };
316 
317 				if (entry.count <= 4)
318 					memcpy(ascfield, &entry.longval, entry.count);
319 				else if (entry.count > 4 && entry.count < 256) {
320 					ssize_t nread = file.ReadAt(entry.longval, ascfield, entry.count);
321 					if (nread != static_cast<ssize_t>(entry.count))
322 						ascfield[0] = '\0';
323 				}
324 
325 				if (ascfield[0] != '\0') {
326 					printf("%s", ascfield);
327 					return;
328 				}
329 			} else if (entry.fieldType == TIFF_RATIONAL && entry.count == 1) {
330 				struct { uint32 numerator; uint32 denominator; } rational;
331 
332 				ssize_t	nread = file.ReadAt(entry.longval, &rational, 8);
333 				if (nread == 8 &&
334 					swap_data(B_UINT32_TYPE, &rational, 8, swp) == B_OK) {
335 
336 					printf("%u / %u (offset: 0x%.8lx)",
337 						static_cast<unsigned int>(rational.numerator),
338 						static_cast<unsigned int>(rational.denominator),
339 						entry.longval);
340 					return;
341 				}
342 			} else if (entry.fieldType == TIFF_SRATIONAL && entry.count == 1) {
343 				struct { int32 numerator; int32 denominator; } srational;
344 
345 				ssize_t	nread = file.ReadAt(entry.longval, &srational, 8);
346 				if (nread == 8 &&
347 					swap_data(B_INT32_TYPE, &srational, 8, swp) == B_OK) {
348 
349 					printf("%d / %d (offset: 0x%.8lx)",
350 						static_cast<int>(srational.numerator),
351 						static_cast<int>(srational.denominator),
352 						entry.longval);
353 					return;
354 				}
355 			} else if (entry.fieldType == TIFF_LONG && entry.count == 1) {
356 				printf("%u",
357 					static_cast<unsigned int>(entry.longval));
358 				return;
359 			} else if (entry.fieldType == TIFF_SLONG && entry.count == 1) {
360 				printf("%d",
361 					static_cast<int>(entry.longval));
362 				return;
363 			} else if (entry.fieldType == TIFF_SHORT && entry.count <= 2) {
364 				for (uint32 i = 0; i < entry.count; i++) {
365 					if (i > 0)
366 						printf(", ");
367 					printf("%u", entry.shortvals[i]);
368 				}
369 				return;
370 			} else if (entry.fieldType == TIFF_SSHORT && entry.count <= 2) {
371 				for (uint32 i = 0; i < entry.count; i++) {
372 					if (i > 0)
373 						printf(", ");
374 					printf("%d", entry.shortvals[i]);
375 				}
376 				return;
377 			} else if (entry.fieldType == TIFF_BYTE && entry.count <= 4) {
378 				for (uint32 i = 0; i < entry.count; i++) {
379 					if (i > 0)
380 						printf(", ");
381 					printf("%u", entry.bytevals[i]);
382 				}
383 				return;
384 			} else if (entry.fieldType == TIFF_SBYTE && entry.count <= 4) {
385 				for (uint32 i = 0; i < entry.count; i++) {
386 					if (i > 0)
387 						printf(", ");
388 					printf("%d", entry.bytevals[i]);
389 				}
390 				return;
391 			} else if (entry.fieldType == TIFF_UNDEFINED && entry.count <= 4) {
392 				for (uint32 i = 0; i < entry.count; i++) {
393 					if (i > 0)
394 						printf(", ");
395 					printf("0x%.2lx",
396 						static_cast<unsigned long>(entry.bytevals[i]));
397 				}
398 				return;
399 			}
400 			break;
401 	}
402 	printf("0x%.8lx", entry.longval);
403 }
404 
405 int swap_value_field(IFDEntry &entry, swap_action swp)
406 {
407 	switch (entry.fieldType) {
408 		case TIFF_BYTE:
409 		case TIFF_ASCII:
410 		case TIFF_SBYTE:
411 		case TIFF_UNDEFINED:
412 			if (entry.count > 4) {
413 				if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK)
414 					return 0;
415 			}
416 			return 1;
417 
418 		case TIFF_LONG:
419 		case TIFF_SLONG:
420 		case TIFF_RATIONAL:
421 		case TIFF_SRATIONAL:
422 		case TIFF_DOUBLE:
423 			if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK)
424 				return 0;
425 			return 1;
426 
427 		case TIFF_FLOAT:
428 			if (swap_data(B_FLOAT_TYPE, &entry.floatval, 4, swp) != B_OK)
429 				return 0;
430 			return 1;
431 
432 		case TIFF_SHORT:
433 		case TIFF_SSHORT:
434 			if (entry.count <= 2) {
435 				if (swap_data(B_UINT16_TYPE, &entry.shortvals,
436 					entry.count * 2, swp) != B_OK)
437 					return 0;
438 			} else {
439 				if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK)
440 					return 0;
441 			}
442 
443 			return 1;
444 	}
445 
446 	// no error, but unknown type
447 	return 2;
448 }
449 
450 int
451 report_ifd_entries(BFile &file, uint16 entrycount, swap_action swp)
452 {
453 	IFDEntry entry;
454 
455 	if (sizeof(IFDEntry) != 12) {
456 		printf("IFDEntry size must be 12\n");
457 		return 0;
458 	}
459 
460 	off_t offset = file.Position();
461 	for (uint16 i = 0; i < entrycount; offset += 12, i++) {
462 		ssize_t nread = file.Read(&entry, 12);
463 		if (nread != 12) {
464 			printf("unable to read entire ifd entry\n");
465 			return 0;
466 		}
467 		if (swap_data(B_UINT16_TYPE, &entry.tag, 4, swp) != B_OK ||
468 			swap_data(B_UINT32_TYPE, &entry.count, 4, swp) != B_OK) {
469 			printf("swap_data failed\n");
470 			return 0;
471 		}
472 
473 		if (!swap_value_field(entry, swp)) {
474 			printf("swap_value_field failed\n");
475 			return 0;
476 		}
477 
478 		printf("\nOffset: 0x%.8lx\n", static_cast<unsigned long>(offset));
479 		printf(  "   Tag: %s (%d)\n", get_tag_string(entry.tag), entry.tag);
480 		printf(  "  Type: %s (%d)\n", get_type_string(entry.fieldType),
481 			entry.fieldType);
482 		printf(  " Count: %d\n", static_cast<int>(entry.count));
483 		printf(  " Value: ");
484 		print_ifd_value(entry, file, swp);
485 		printf("\n");
486 	}
487 
488 	return 1;
489 }
490 
491 int
492 report_ifd(BFile &file, uint32 ifdoffset, swap_action swp)
493 {
494 	printf("\n<< BEGIN: IFD at 0x%.8lx >>\n\n", ifdoffset);
495 
496 	if (file.Seek(ifdoffset, SEEK_SET) != ifdoffset) {
497 		printf("failed to seek to IFD offset: %d\n",
498 			static_cast<unsigned int>(ifdoffset));
499 		return 0;
500 	}
501 
502 	uint16 entrycount = 0;
503 	ssize_t nread = file.Read(&entrycount, 2);
504 	if (nread != 2) {
505 		printf("unable to read entry count\n");
506 		return 0;
507 	}
508 	if (swap_data(B_UINT16_TYPE, &entrycount, sizeof(uint16), swp) != B_OK) {
509 		printf("failed to swap entrycount\n");
510 		return 0;
511 	}
512 	printf("Entry Count: %d\n", entrycount);
513 
514 	// Print out entries
515 	int ret = report_ifd_entries(file, entrycount, swp);
516 
517 	if (ret) {
518 		uint32 nextIFDOffset = 0;
519 
520 		nread = file.Read(&nextIFDOffset, 4);
521 		if (nread != 4) {
522 			printf("unable to read next IFD\n");
523 			return 0;
524 		}
525 		if (swap_data(B_UINT32_TYPE, &nextIFDOffset, sizeof(uint32), swp) != B_OK) {
526 			printf("failed to swap next IFD\n");
527 			return 0;
528 		}
529 
530 		printf("Next IFD Offset: 0x%.8lx\n", nextIFDOffset);
531 		printf("\n<< END: IFD at 0x%.8lx >>\n\n", ifdoffset);
532 
533 		if (nextIFDOffset != 0)
534 			return report_ifd(file, nextIFDOffset, swp);
535 		else
536 			return 1;
537 
538 	} else
539 		return 0;
540 }
541 
542 int generate_report(const char *filepath)
543 {
544 	BFile file(filepath, B_READ_ONLY);
545 
546 	if (file.InitCheck() == B_OK) {
547 
548 		uint8 buffer[64];
549 
550 		// Byte Order
551 		const uint8 kleSig[] = { 0x49, 0x49, 0x2a, 0x00 };
552 		const uint8 kbeSig[] = { 0x4d, 0x4d, 0x00, 0x2a };
553 
554 		ssize_t nread = file.Read(buffer, 4);
555 		if (nread != 4) {
556 			printf("Unable to read first 4 bytes\n");
557 			return 0;
558 		}
559 
560 		swap_action swp;
561 		if (memcmp(buffer, kleSig, 4) == 0) {
562 			swp = B_SWAP_LENDIAN_TO_HOST;
563 			printf("Byte Order: little endian\n");
564 
565 		} else if (memcmp(buffer, kbeSig, 4) == 0) {
566 			swp = B_SWAP_BENDIAN_TO_HOST;
567 			printf("Byte Order: big endian\n");
568 
569 		} else {
570 			printf("Invalid byte order value\n");
571 			return 0;
572 		}
573 
574 		// Location of first IFD
575 		uint32 firstIFDOffset = 0;
576 		nread = file.Read(&firstIFDOffset, 4);
577 		if (nread != 4) {
578 			printf("Unable to read first IFD offset\n");
579 			return 0;
580 		}
581 		if (swap_data(B_UINT32_TYPE, &firstIFDOffset, sizeof(uint32), swp) != B_OK) {
582 			printf("swap_data() error\n");
583 			return 0;
584 		}
585 		printf("First IFD: 0x%.8lx\n", firstIFDOffset);
586 
587 		// print out first IFD
588 		report_ifd(file, firstIFDOffset, swp);
589 
590 		return 1;
591 	}
592 
593 	return 0;
594 }
595 
596 int main(int argc, char **argv)
597 {
598 	printf("\n");
599 		// put a line break at the beginning of output
600 		// to improve readability
601 
602 	if (argc == 2) {
603 
604 		printf("TIFF Image: %s\n\n", argv[1]);
605 		generate_report(argv[1]);
606 
607 	} else {
608 
609 		printf("tiffinfo - reports information about a TIFF image\n");
610 		printf("\nUsage:\n");
611 		printf("tiffinfo filename.tif\n\n");
612 	}
613 
614 	return 0;
615 }
616 
617