diff --git a/src/tools/translation/tiffinfo/Jamfile b/src/tools/translation/tiffinfo/Jamfile new file mode 100644 index 0000000000..9949683b19 --- /dev/null +++ b/src/tools/translation/tiffinfo/Jamfile @@ -0,0 +1,4 @@ +SubDir OBOS_TOP src tools translation tiffinfo ; + +BinCommand tiffinfo : tiffinfo.cpp : be ; + diff --git a/src/tools/translation/tiffinfo/tiffinfo.cpp b/src/tools/translation/tiffinfo/tiffinfo.cpp new file mode 100644 index 0000000000..2ac821c4b2 --- /dev/null +++ b/src/tools/translation/tiffinfo/tiffinfo.cpp @@ -0,0 +1,557 @@ +/*****************************************************************************/ +// tiffinfo +// Written by Michael Wilber, OBOS Translation Kit Team +// +// Version: +// +// tiffinfo is a command line program for displaying text information about +// TIFF images. This information includes a listing of every field (tag) in +// the TIFF file, for every image in the file. Also, for some fields, +// the numerical value for the field is converted to descriptive text. +// +// This application and all source files used in its construction, except +// where noted, are licensed under the MIT License, and have been written +// and are: +// +// Copyright (c) 2003 OpenBeOS Project +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +/*****************************************************************************/ +#include +#include +#include +#include +#include +#include + +struct IFDEntry { + uint16 tag; + // uniquely identifies the field + uint16 fieldType; + // number, string, float, etc. + uint32 count; + // length / number of values + + // The actual value or the file offset + // where the actual value is located + union { + float floatval; + uint32 longval; + uint16 shortvals[2]; + uint8 bytevals[4]; + }; +}; + +enum ENTRY_TYPE { + TIFF_BYTE = 1, + TIFF_ASCII, + TIFF_SHORT, + TIFF_LONG, + TIFF_RATIONAL, + TIFF_SBYTE, + TIFF_UNDEFINED, + TIFF_SSHORT, + TIFF_SRATIONAL, + TIFF_FLOAT, + TIFF_DOUBLE +}; + +const char * +get_type_string(uint16 type) +{ + const char *kstrTypes[] = { + "Byte", + "ASCII", + "Short", + "Long", + "Rational", + "Signed Byte", + "Undefined", + "Signed Short", + "Signed Long", + "Signed Rational", + "Float", + "Double" + }; + + if (type >= 1 && type <= 12) + return kstrTypes[type - 1]; + else + return "?"; +} + +const char * +get_tag_string(uint16 tag) +{ + switch (tag) { + case 254: return "New Subfile Type"; + case 255: return "Subfile Type"; + case 256: return "Image Width"; + case 257: return "Image Height"; + case 258: return "Bits Per Sample"; + case 259: return "Compression"; + case 262: return "Photometric Interpretation"; + case 263: return "Thresholding"; + case 264: return "CellWidth"; + case 265: return "CellLength"; + case 266: return "Fill Order"; + case 269: return "Document Name"; + case 270: return "Image Description"; + case 271: return "Make"; + case 272: return "Model"; + case 273: return "Strip Offsets"; + case 274: return "Orientation"; + case 277: return "Samples Per Pixel"; + case 278: return "Rows Per Strip"; + case 279: return "Strip Byte Counts"; + case 280: return "Min Sample Value"; + case 281: return "Max Sample Value"; + case 282: return "X Resolution"; + case 283: return "Y Resolution"; + case 284: return "Planar Configuration"; + case 285: return "Page Name"; + case 286: return "X Position"; + case 287: return "Y Position"; + case 288: return "Free Offsets"; + case 289: return "Free Byte Counts"; + case 290: return "Gray Response Unit"; + case 291: return "Gray Response Curve"; + case 292: return "T4 Options"; + case 293: return "T6 Options"; + case 296: return "Resolution Unit"; + case 297: return "Page Number"; + case 305: return "Software"; + case 306: return "DateTime"; + case 315: return "Artist"; + case 316: return "Host Computer"; + case 320: return "Color Map"; + case 322: return "Tile Width"; + case 323: return "Tile Height"; + case 324: return "Tile Offsets"; + case 325: return "Tile Byte Counts"; + case 338: return "Extra Samples"; + case 339: return "Sample Format"; + case 529: return "YCbCr Coefficients"; + case 530: return "YCbCr Subsampling"; + case 531: return "YCbCr Positioning"; + case 532: return "Reference Black White"; + case 32995: return "Matteing"; + case 32996: return "Data Type"; // obseleted by SampleFormat tag + case 32997: return "Image Depth"; // tile / strip calculations + case 32998: return "Tile Depth"; // tile / strip calculations + case 33432: return "Copyright"; + case 37439: return "StoNits?"; + + default: + return "?"; + } +} + +void +print_ifd_value(IFDEntry &entry, BFile &file, swap_action swp) +{ + switch (entry.tag) { + case 254: // NewSubfileType + if (entry.count == 1 && entry.fieldType == TIFF_LONG) { + if (entry.longval & 1) + printf("Low Res "); + if (entry.longval & 2) + printf("Page "); + if (entry.longval & 4) + printf("Mask "); + + printf("(0x%.8lx)", entry.longval); + return; + } + break; + + case 256: // ImageWidth + case 257: // ImageHeight + if (entry.count == 1) { + printf("%d", + ((entry.fieldType == TIFF_SHORT) ? + entry.shortvals[0] : static_cast(entry.longval))); + return; + } + break; + + case 259: + if (entry.count == 1 && entry.fieldType == TIFF_SHORT) { + switch (entry.shortvals[0]) { + case 1: + printf("No Compression"); + return; + case 2: + printf("CCITT Group 3 1-Dimensional Modified Huffman run-length encoding"); + return; + case 5: + printf("LZW"); + return; + case 32773: + printf("PackBits"); + return; + } + } + break; + + case 262: // PhotometricInterpretation + if (entry.count == 1 && entry.fieldType == TIFF_SHORT) { + switch (entry.shortvals[0]) { + case 0: + printf("White is Zero (%d)", entry.shortvals[0]); + return; + case 1: + printf("Black is Zero (%d)", entry.shortvals[0]); + return; + case 2: + printf("RGB (%d)", entry.shortvals[0]); + return; + case 3: + printf("Palette Color (%d)", entry.shortvals[0]); + return; + case 4: + printf("Transparency Mask (%d)", entry.shortvals[0]); + return; + } + } + break; + + case 274: // Orientation + if (entry.count == 1 && entry.fieldType == TIFF_SHORT) { + switch (entry.shortvals[0]) { + case 1: + printf("top to bottom, left to right"); + return; + case 2: + printf("top to bottom, right to left"); + return; + case 3: + printf("bottom to top, right to left"); + return; + case 4: + printf("bottom to top, left to right"); + return; + case 5: + printf("left to right, top to bottom"); + return; + case 6: + printf("right to left, top to bottom"); + return; + case 7: + printf("right to left, bottom to top"); + return; + case 8: + printf("left to right, bottom to top"); + return; + } + } + break; + + case 284: // PlanarConfiguration + if (entry.count == 1 && entry.fieldType == TIFF_SHORT) { + if (entry.shortvals[0] == 1) { + printf("Chunky (%d)", entry.shortvals[0]); + return; + } + else if (entry.shortvals[0] == 2) { + printf("Planar (%d)", entry.shortvals[0]); + return; + } + } + break; + + case 296: // ResolutionUnit + if (entry.count == 1 && entry.fieldType == TIFF_SHORT) { + switch (entry.shortvals[0]) { + case 1: + printf("None (%d)", entry.shortvals[0]); + return; + case 2: + printf("Inch (%d)", entry.shortvals[0]); + return; + case 3: + printf("Cenimeter (%d)", entry.shortvals[0]); + return; + } + } + break; + + default: + if (entry.fieldType == TIFF_ASCII) { + char ascfield[256] = { 0 }; + + if (entry.count <= 4) + memcpy(ascfield, &entry.longval, entry.count); + else if (entry.count > 4 && entry.count < 256) { + ssize_t nread = file.ReadAt(entry.longval, ascfield, entry.count); + if (nread != static_cast(entry.count)) + ascfield[0] = '\0'; + } + + if (ascfield[0] != '\0') { + printf("%s", ascfield); + return; + } + } else if (entry.fieldType == TIFF_RATIONAL && entry.count == 1) { + struct { uint32 numerator; uint32 denominator; } rational; + + ssize_t nread = file.ReadAt(entry.longval, &rational, 8); + if (nread == 8 && + swap_data(B_UINT32_TYPE, &rational, 8, swp) == B_OK) { + + printf("%d / %d", + static_cast(rational.numerator), + static_cast(rational.denominator)); + return; + } + } else if (entry.fieldType == TIFF_LONG && entry.count == 1) { + printf("%d", + static_cast(entry.longval)); + return; + } else if (entry.fieldType == TIFF_SHORT && entry.count <= 2) { + for (uint32 i = 0; i < entry.count; i++) { + if (i > 0) + printf(", "); + printf("%d", entry.shortvals[i]); + } + return; + } else if (entry.fieldType == TIFF_BYTE && entry.count <= 4) { + for (uint32 i = 0; i < entry.count; i++) { + if (i > 0) + printf(", "); + printf("%d", entry.bytevals[i]); + } + return; + } + break; + } + printf("0x%.8lx", entry.longval); +} + +int swap_value_field(IFDEntry &entry, swap_action swp) +{ + switch (entry.fieldType) { + case TIFF_BYTE: + case TIFF_ASCII: + case TIFF_SBYTE: + case TIFF_UNDEFINED: + if (entry.count > 4) { + if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK) + return 0; + } + return 1; + + case TIFF_LONG: + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_DOUBLE: + if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK) + return 0; + return 1; + + case TIFF_FLOAT: + if (swap_data(B_FLOAT_TYPE, &entry.floatval, 4, swp) != B_OK) + return 0; + return 1; + + case TIFF_SHORT: + case TIFF_SSHORT: + if (entry.count <= 2) { + if (swap_data(B_UINT16_TYPE, &entry.shortvals, + entry.count * 2, swp) != B_OK) + return 0; + } else { + if (swap_data(B_UINT32_TYPE, &entry.longval, 4, swp) != B_OK) + return 0; + } + + return 1; + } + + // no error, but unknown type + return 2; +} + +int +report_ifd_entries(BFile &file, uint16 entrycount, swap_action swp) +{ + IFDEntry entry; + + if (sizeof(IFDEntry) != 12) { + printf("IFDEntry size must be 12\n"); + return 0; + } + + off_t offset = file.Position(); + for (uint16 i = 0; i < entrycount; offset += 12, i++) { + ssize_t nread = file.Read(&entry, 12); + if (nread != 12) { + printf("unable to read entire ifd entry\n"); + return 0; + } + if (swap_data(B_UINT16_TYPE, &entry.tag, 4, swp) != B_OK || + swap_data(B_UINT32_TYPE, &entry.count, 4, swp) != B_OK) { + printf("swap_data failed\n"); + return 0; + } + + if (!swap_value_field(entry, swp)) { + printf("swap_value_field failed\n"); + return 0; + } + + printf("\nOffset: 0x%.8lx\n", static_cast(offset)); + printf( " Tag: %s (%d)\n", get_tag_string(entry.tag), entry.tag); + printf( " Type: %s (%d)\n", get_type_string(entry.fieldType), + entry.fieldType); + printf( " Count: %d\n", static_cast(entry.count)); + printf( " Value: "); + print_ifd_value(entry, file, swp); + printf("\n"); + } + + return 1; +} + +int +report_ifd(BFile &file, uint32 ifdoffset, swap_action swp) +{ + printf("\n<< BEGIN: IFD at 0x%.8lx >>\n\n", ifdoffset); + + if (file.Seek(ifdoffset, SEEK_SET) != ifdoffset) { + printf("failed to seek to IFD offset: %d\n", + static_cast(ifdoffset)); + return 0; + } + + uint16 entrycount = 0; + ssize_t nread = file.Read(&entrycount, 2); + if (nread != 2) { + printf("unable to read entry count\n"); + return 0; + } + if (swap_data(B_UINT16_TYPE, &entrycount, sizeof(uint16), swp) != B_OK) { + printf("failed to swap entrycount\n"); + return 0; + } + printf("Entry Count: %d\n", entrycount); + + // Print out entries + int ret = report_ifd_entries(file, entrycount, swp); + + if (ret) { + uint32 nextIFDOffset = 0; + + nread = file.Read(&nextIFDOffset, 4); + if (nread != 4) { + printf("unable to read next IFD\n"); + return 0; + } + if (swap_data(B_UINT32_TYPE, &nextIFDOffset, sizeof(uint32), swp) != B_OK) { + printf("failed to swap next IFD\n"); + return 0; + } + + printf("Next IFD Offset: 0x%.8lx\n", nextIFDOffset); + printf("\n<< END: IFD at 0x%.8lx >>\n\n", ifdoffset); + + if (nextIFDOffset != 0) + return report_ifd(file, nextIFDOffset, swp); + else + return 1; + + } else + return 0; +} + +int generate_report(const char *filepath) +{ + BFile file(filepath, B_READ_ONLY); + + if (file.InitCheck() == B_OK) { + + uint8 buffer[64]; + + // Byte Order + const uint8 kleSig[] = { 0x49, 0x49, 0x2a, 0x00 }; + const uint8 kbeSig[] = { 0x4d, 0x4d, 0x00, 0x2a }; + + ssize_t nread = file.Read(buffer, 4); + if (nread != 4) { + printf("Unable to read first 4 bytes\n"); + return 0; + } + + swap_action swp; + if (memcmp(buffer, kleSig, 4) == 0) { + swp = B_SWAP_LENDIAN_TO_HOST; + printf("Byte Order: little endian\n"); + + } else if (memcmp(buffer, kbeSig, 4) == 0) { + swp = B_SWAP_BENDIAN_TO_HOST; + printf("Byte Order: big endian\n"); + + } else { + printf("Invalid byte order value\n"); + return 0; + } + + // Location of first IFD + uint32 firstIFDOffset = 0; + nread = file.Read(&firstIFDOffset, 4); + if (nread != 4) { + printf("Unable to read first IFD offset\n"); + return 0; + } + if (swap_data(B_UINT32_TYPE, &firstIFDOffset, sizeof(uint32), swp) != B_OK) { + printf("swap_data() error\n"); + return 0; + } + printf("First IFD: 0x%.8lx\n", firstIFDOffset); + + // print out first IFD + report_ifd(file, firstIFDOffset, swp); + + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + printf("\n"); + // put a line break at the beginning of output + // to improve readability + + if (argc == 2) { + + printf("TIFF Image: %s\n\n", argv[1]); + generate_report(argv[1]); + + } else { + + printf("tiffinfo - reports information about a TIFF image\n"); + printf("\nUsage:\n"); + printf("tiffinfo filename.tif\n\n"); + } + + return 0; +} +