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