1 /*****************************************************************************/
3 // Written by Michael Wilber, OBOS Translation Kit Team
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.
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
16 // Copyright (c) 2003 OpenBeOS Project
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:
25 // The above copyright notice and this permission notice shall be included
26 // in all copies or substantial portions of the Software.
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>
38 #include <StorageDefs.h>
46 // uniquely identifies the field
48 // number, string, float, etc.
50 // length / number of values
52 // The actual value or the file offset
53 // where the actual value is located
78 get_type_string(uint16 type
)
80 const char *kstrTypes
[] = {
95 if (type
>= 1 && type
<= 12)
96 return kstrTypes
[type
- 1];
102 get_tag_string(uint16 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?";
169 print_ifd_value(IFDEntry
&entry
, BFile
&file
, swap_action swp
)
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)
178 if (entry
.longval
& 4)
181 printf("(0x%.8lx)", entry
.longval
);
186 case 256: // ImageWidth
187 case 257: // ImageHeight
188 if (entry
.count
== 1) {
190 ((entry
.fieldType
== TIFF_SHORT
) ?
191 entry
.shortvals
[0] : static_cast<unsigned int>(entry
.longval
)));
197 if (entry
.count
== 1 && entry
.fieldType
== TIFF_SHORT
) {
198 switch (entry
.shortvals
[0]) {
200 printf("No Compression (1)");
203 printf("CCITT Group 3 1-Dimensional Modified Huffman run-length encoding (2)");
206 printf("Fax Group 3 (3)");
209 printf("Fax Group 4 (4)");
215 printf("PackBits (32773)");
221 case 262: // PhotometricInterpretation
222 if (entry
.count
== 1 && entry
.fieldType
== TIFF_SHORT
) {
223 switch (entry
.shortvals
[0]) {
225 printf("White is Zero (%d)", entry
.shortvals
[0]);
228 printf("Black is Zero (%d)", entry
.shortvals
[0]);
231 printf("RGB (%d)", entry
.shortvals
[0]);
234 printf("Palette Color (%d)", entry
.shortvals
[0]);
237 printf("Transparency Mask (%d)", entry
.shortvals
[0]);
243 case 274: // Orientation
244 if (entry
.count
== 1 && entry
.fieldType
== TIFF_SHORT
) {
245 switch (entry
.shortvals
[0]) {
247 printf("top to bottom, left to right (1)");
250 printf("top to bottom, right to left (2)");
253 printf("bottom to top, right to left (3)");
256 printf("bottom to top, left to right (4)");
259 printf("left to right, top to bottom (5)");
262 printf("right to left, top to bottom (6)");
265 printf("right to left, bottom to top (7)");
268 printf("left to right, bottom to top (8)");
274 case 278: // RowsPerStrip
276 const uint32 ksinglestrip
= 0xffffffffUL
;
278 static_cast<unsigned int>(entry
.longval
));
279 if (entry
.longval
== ksinglestrip
)
280 printf(" (All rows in first strip)");
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]);
290 else if (entry
.shortvals
[0] == 2) {
291 printf("Planar (%d)", entry
.shortvals
[0]);
297 case 296: // ResolutionUnit
298 if (entry
.count
== 1 && entry
.fieldType
== TIFF_SHORT
) {
299 switch (entry
.shortvals
[0]) {
301 printf("None (%d)", entry
.shortvals
[0]);
304 printf("Inch (%d)", entry
.shortvals
[0]);
307 printf("Cenimeter (%d)", entry
.shortvals
[0]);
314 if (entry
.fieldType
== TIFF_ASCII
) {
315 char ascfield
[256] = { 0 };
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
))
325 if (ascfield
[0] != '\0') {
326 printf("%s", ascfield
);
329 } else if (entry
.fieldType
== TIFF_RATIONAL
&& entry
.count
== 1) {
330 struct { uint32 numerator
; uint32 denominator
; } rational
;
332 ssize_t nread
= file
.ReadAt(entry
.longval
, &rational
, 8);
334 swap_data(B_UINT32_TYPE
, &rational
, 8, swp
) == B_OK
) {
336 printf("%u / %u (offset: 0x%.8lx)",
337 static_cast<unsigned int>(rational
.numerator
),
338 static_cast<unsigned int>(rational
.denominator
),
342 } else if (entry
.fieldType
== TIFF_SRATIONAL
&& entry
.count
== 1) {
343 struct { int32 numerator
; int32 denominator
; } srational
;
345 ssize_t nread
= file
.ReadAt(entry
.longval
, &srational
, 8);
347 swap_data(B_INT32_TYPE
, &srational
, 8, swp
) == B_OK
) {
349 printf("%d / %d (offset: 0x%.8lx)",
350 static_cast<int>(srational
.numerator
),
351 static_cast<int>(srational
.denominator
),
355 } else if (entry
.fieldType
== TIFF_LONG
&& entry
.count
== 1) {
357 static_cast<unsigned int>(entry
.longval
));
359 } else if (entry
.fieldType
== TIFF_SLONG
&& entry
.count
== 1) {
361 static_cast<int>(entry
.longval
));
363 } else if (entry
.fieldType
== TIFF_SHORT
&& entry
.count
<= 2) {
364 for (uint32 i
= 0; i
< entry
.count
; i
++) {
367 printf("%u", entry
.shortvals
[i
]);
370 } else if (entry
.fieldType
== TIFF_SSHORT
&& entry
.count
<= 2) {
371 for (uint32 i
= 0; i
< entry
.count
; i
++) {
374 printf("%d", entry
.shortvals
[i
]);
377 } else if (entry
.fieldType
== TIFF_BYTE
&& entry
.count
<= 4) {
378 for (uint32 i
= 0; i
< entry
.count
; i
++) {
381 printf("%u", entry
.bytevals
[i
]);
384 } else if (entry
.fieldType
== TIFF_SBYTE
&& entry
.count
<= 4) {
385 for (uint32 i
= 0; i
< entry
.count
; i
++) {
388 printf("%d", entry
.bytevals
[i
]);
391 } else if (entry
.fieldType
== TIFF_UNDEFINED
&& entry
.count
<= 4) {
392 for (uint32 i
= 0; i
< entry
.count
; i
++) {
396 static_cast<unsigned long>(entry
.bytevals
[i
]));
402 printf("0x%.8lx", entry
.longval
);
405 int swap_value_field(IFDEntry
&entry
, swap_action swp
)
407 switch (entry
.fieldType
) {
412 if (entry
.count
> 4) {
413 if (swap_data(B_UINT32_TYPE
, &entry
.longval
, 4, swp
) != B_OK
)
423 if (swap_data(B_UINT32_TYPE
, &entry
.longval
, 4, swp
) != B_OK
)
428 if (swap_data(B_FLOAT_TYPE
, &entry
.floatval
, 4, swp
) != B_OK
)
434 if (entry
.count
<= 2) {
435 if (swap_data(B_UINT16_TYPE
, &entry
.shortvals
,
436 entry
.count
* 2, swp
) != B_OK
)
439 if (swap_data(B_UINT32_TYPE
, &entry
.longval
, 4, swp
) != B_OK
)
446 // no error, but unknown type
451 report_ifd_entries(BFile
&file
, uint16 entrycount
, swap_action swp
)
455 if (sizeof(IFDEntry
) != 12) {
456 printf("IFDEntry size must be 12\n");
460 off_t offset
= file
.Position();
461 for (uint16 i
= 0; i
< entrycount
; offset
+= 12, i
++) {
462 ssize_t nread
= file
.Read(&entry
, 12);
464 printf("unable to read entire ifd entry\n");
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");
473 if (!swap_value_field(entry
, swp
)) {
474 printf("swap_value_field failed\n");
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
),
482 printf( " Count: %d\n", static_cast<int>(entry
.count
));
484 print_ifd_value(entry
, file
, swp
);
492 report_ifd(BFile
&file
, uint32 ifdoffset
, swap_action swp
)
494 printf("\n<< BEGIN: IFD at 0x%.8lx >>\n\n", ifdoffset
);
496 if (file
.Seek(ifdoffset
, SEEK_SET
) != ifdoffset
) {
497 printf("failed to seek to IFD offset: %d\n",
498 static_cast<unsigned int>(ifdoffset
));
502 uint16 entrycount
= 0;
503 ssize_t nread
= file
.Read(&entrycount
, 2);
505 printf("unable to read entry count\n");
508 if (swap_data(B_UINT16_TYPE
, &entrycount
, sizeof(uint16
), swp
) != B_OK
) {
509 printf("failed to swap entrycount\n");
512 printf("Entry Count: %d\n", entrycount
);
515 int ret
= report_ifd_entries(file
, entrycount
, swp
);
518 uint32 nextIFDOffset
= 0;
520 nread
= file
.Read(&nextIFDOffset
, 4);
522 printf("unable to read next IFD\n");
525 if (swap_data(B_UINT32_TYPE
, &nextIFDOffset
, sizeof(uint32
), swp
) != B_OK
) {
526 printf("failed to swap next IFD\n");
530 printf("Next IFD Offset: 0x%.8lx\n", nextIFDOffset
);
531 printf("\n<< END: IFD at 0x%.8lx >>\n\n", ifdoffset
);
533 if (nextIFDOffset
!= 0)
534 return report_ifd(file
, nextIFDOffset
, swp
);
542 int generate_report(const char *filepath
)
544 BFile
file(filepath
, B_READ_ONLY
);
546 if (file
.InitCheck() == B_OK
) {
551 const uint8 kleSig
[] = { 0x49, 0x49, 0x2a, 0x00 };
552 const uint8 kbeSig
[] = { 0x4d, 0x4d, 0x00, 0x2a };
554 ssize_t nread
= file
.Read(buffer
, 4);
556 printf("Unable to read first 4 bytes\n");
561 if (memcmp(buffer
, kleSig
, 4) == 0) {
562 swp
= B_SWAP_LENDIAN_TO_HOST
;
563 printf("Byte Order: little endian\n");
565 } else if (memcmp(buffer
, kbeSig
, 4) == 0) {
566 swp
= B_SWAP_BENDIAN_TO_HOST
;
567 printf("Byte Order: big endian\n");
570 printf("Invalid byte order value\n");
574 // Location of first IFD
575 uint32 firstIFDOffset
= 0;
576 nread
= file
.Read(&firstIFDOffset
, 4);
578 printf("Unable to read first IFD offset\n");
581 if (swap_data(B_UINT32_TYPE
, &firstIFDOffset
, sizeof(uint32
), swp
) != B_OK
) {
582 printf("swap_data() error\n");
585 printf("First IFD: 0x%.8lx\n", firstIFDOffset
);
587 // print out first IFD
588 report_ifd(file
, firstIFDOffset
, swp
);
596 int main(int argc
, char **argv
)
599 // put a line break at the beginning of output
600 // to improve readability
604 printf("TIFF Image: %s\n\n", argv
[1]);
605 generate_report(argv
[1]);
609 printf("tiffinfo - reports information about a TIFF image\n");
610 printf("\nUsage:\n");
611 printf("tiffinfo filename.tif\n\n");