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