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