diff --git a/include/exiv2/image.hpp b/include/exiv2/image.hpp index 099d7c5e..eb87d9b3 100644 --- a/include/exiv2/image.hpp +++ b/include/exiv2/image.hpp @@ -113,7 +113,7 @@ namespace Exiv2 { not valid (does not look like data of the specific image type). @caution This function is not thread safe and intended for exiv2 -pS for debugging. */ - virtual void printStructure(std::ostream& out, PrintStructureOption option =kpsNone); + virtual void printStructure(std::ostream& out, PrintStructureOption option =kpsNone, int depth=0); /*! @brief Read all metadata supported by a specific image format from the image. Before this method is called, the image metadata will be diff --git a/include/exiv2/jpgimage.hpp b/include/exiv2/jpgimage.hpp index 50eaffd0..b61bfc72 100644 --- a/include/exiv2/jpgimage.hpp +++ b/include/exiv2/jpgimage.hpp @@ -159,7 +159,7 @@ namespace Exiv2 { not valid (does not look like data of the specific image type). @caution This function is not thread safe and intended for exiv2 -pS for debugging. */ - void printStructure(std::ostream& out, PrintStructureOption option); + void printStructure(std::ostream& out, PrintStructureOption option,int depth); //@} protected: diff --git a/include/exiv2/pngimage.hpp b/include/exiv2/pngimage.hpp index 2fa940d8..083d0178 100644 --- a/include/exiv2/pngimage.hpp +++ b/include/exiv2/pngimage.hpp @@ -93,7 +93,7 @@ namespace Exiv2 not valid (does not look like data of the specific image type). @caution This function is not thread safe and intended for exiv2 -pS for debugging. */ - void printStructure(std::ostream& out, PrintStructureOption option); + void printStructure(std::ostream& out, PrintStructureOption option,int depth); //@} //! @name Accessors diff --git a/include/exiv2/tiffimage.hpp b/include/exiv2/tiffimage.hpp index 7ca67ade..76c1af71 100644 --- a/include/exiv2/tiffimage.hpp +++ b/include/exiv2/tiffimage.hpp @@ -91,9 +91,23 @@ namespace Exiv2 { @brief Print out the structure of image file. @throw Error if reading of the file fails or the image data is not valid (does not look like data of the specific image type). - @caution This function is not thread safe and intended for exiv2 -pS for debugging. + @caution This function is not thread safe and intended for exiv2 -p{S|R} as a file debugging aid */ - void printStructure(std::ostream& out, PrintStructureOption option); + void printStructure(std::ostream& out, PrintStructureOption option,int depth=-1); + + /*! + @brief Print out the structure of image file. + @throw Error if reading of the file fails or the image data is + not valid (does not look like data of the specific image type). + @caution This function is not thread safe. See TiffImage::printStructure for more details + */ + static void printTiffStructure(BasicIo& io,std::ostream& out, PrintStructureOption option,int depth); + + /*! + @brief Print out the structure of a TIFF IFD + @caution This function is not thread safe. See TiffImage::printStructure for more details + */ + static void printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,size_t start,bool bSwap,char c,int depth); /*! @brief Not supported. TIFF format does not contain a comment. diff --git a/src/exiv2.1 b/src/exiv2.1 index a2428113..28f92ee9 100644 --- a/src/exiv2.1 +++ b/src/exiv2.1 @@ -3,7 +3,7 @@ .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) -.TH EXIV2 1 "Dec 29, 2015" +.TH EXIV2 1 "Jan 5, 2016" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -175,9 +175,9 @@ fmt Default format is %Y%m%d_%H%M%S. lvl d | i | i | w | e debug, info, warning, error -mod s | a | t | v | h | i | x | c | p | i | S | X : +mod s | a | t | v | h | i | x | c | p | i | C | R | S | X : summary, add, translated, vanilla, hex ... - iptc ,xmp, comment, preview, Structure,XMP raw + iptc ,xmp, comment, preview, ICC, Recursive, Structure, raw XMP tgt a | c | e | i | t | x all, comment, exif, iptc, thumb, xmp @@ -315,9 +315,13 @@ c : JPEG comment .br p : list available image previews, sorted by preview image size in pixels .br -S : print image structure information (jpg, png and tiff only) +C : print image ICC Profile (jpg only) .br -X : print "raw" XMP (jpg, png and tiff only) +R : print image structure recursively (jpg, tiff only) +.br +S : print image structure information (jpg, png, tiff only) +.br +X : print "raw" XMP (jpg, png, tiff only) .TP .B \-P \fIflgs\fP Print flags for fine control of the tag list ('print' action). Allows diff --git a/src/image.cpp b/src/image.cpp index 7cea7d9d..9e8ddf40 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -169,7 +169,7 @@ namespace Exiv2 { { } - void Image::printStructure(std::ostream&, PrintStructureOption) + void Image::printStructure(std::ostream&, PrintStructureOption,int /*depth*/) { throw Error(13, io_->path()); } diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index 4bc8c4c6..2f465eca 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -35,6 +35,7 @@ EXIV2_RCSID("@(#) $Id$") #include "config.h" #include "jpgimage.hpp" +#include "tiffimage.hpp" #include "image_int.hpp" #include "error.hpp" #include "futils.hpp" @@ -516,9 +517,9 @@ namespace Exiv2 { return true ; } -#define REPORT_MARKER if ( option == kpsBasic ) out << Internal::stringFormat("%8ld | %#02x %-5s",io_->tell(), marker,nm[marker].c_str()) +#define REPORT_MARKER if ( (option == kpsBasic||option == kpsRecursive) ) out << Internal::stringFormat("%8ld | %#02x %-5s",io_->tell(), marker,nm[marker].c_str()) - void JpegBase::printStructure(std::ostream& out, PrintStructureOption option) + void JpegBase::printStructure(std::ostream& out, PrintStructureOption option,int depth) { if (io_->open() != 0) throw Error(9, io_->path(), strError()); // Ensure that this is the correct image type @@ -570,7 +571,8 @@ namespace Exiv2 { out << " address | marker | length | data" << std::endl ; REPORT_MARKER; } - first = false; + first = false; + bool bLF = option == kpsBasic||option == kpsRecursive; // Read size and signature std::memset(buf.pData_, 0x0, buf.size_); @@ -590,7 +592,7 @@ namespace Exiv2 { ){ size = getUShort(buf.pData_, bigEndian); } - if ( option == kpsBasic ) out << Internal::stringFormat(" | %7d ", size); + if ( option == kpsBasic||option==kpsRecursive ) out << Internal::stringFormat(" | %7d ", size); // only print the signature for appn if (marker >= app0_ && marker <= (app0_ | 0x0F)) { @@ -637,15 +639,43 @@ namespace Exiv2 { bufRead = size; delete [] icc; } - } else if ( option == kpsBasic ) { - out << "| " << Internal::binaryToString(buf,32,size>0?2:0); + } else if ( option == kpsBasic||option==kpsRecursive ) { + out << "| " << Internal::binaryToString(buf,size>32?32:size,size>0?2:0); + } + + // for MPF: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html + if( (option == kpsRecursive && marker == (app0_+1) && std::strcmp(http,"Exif")==0 ) + || (option == kpsRecursive && marker == (app0_+2) && std::strcmp(http,"MPF" )==0 ) + ) { + // extract Exif data block which is tiff formatted + if ( size > 0 ) { + out << std::endl; + + // allocate storage and current file position + byte* exif = new byte[size]; + size_t restore = io_->tell(); + + // copy the data to memory + io_->seek(-bufRead , BasicIo::cur); + io_->read(exif,size); + std::size_t start = std::strcmp(http,"Exif")==0 ? 8 : 6; + + // create a copy on write memio object with the data, then print the structure + BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(exif+start,size-start)); + TiffImage::printTiffStructure(*p,out,option,depth); + + // restore and clean up + io_->seek(restore,Exiv2::BasicIo::beg); + delete [] exif; + bLF = false; + } } } // Skip the segment if the size is known if (io_->seek(size - bufRead, BasicIo::cur)) throw Error(14); - if ( option == kpsBasic ) out << std::endl; + if ( bLF ) out << std::endl; if (marker == sos_) // sos_ is immediately followed by entropy-coded data & eoi_ diff --git a/src/pngimage.cpp b/src/pngimage.cpp index 6a4c8b42..79d34b90 100644 --- a/src/pngimage.cpp +++ b/src/pngimage.cpp @@ -48,6 +48,8 @@ EXIV2_RCSID("@(#) $Id$") #include #include +#include // To uncompress IccProfiles + // Signature from front of PNG file const unsigned char pngSignature[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; @@ -92,7 +94,47 @@ namespace Exiv2 { return "image/png"; } - void PngImage::printStructure(std::ostream& out, PrintStructureOption option) + static void zlibUncompress(const byte* compressedText, + unsigned int compressedTextSize, + DataBuf& arr) + { + uLongf uncompressedLen = compressedTextSize * 2; // just a starting point + int zlibResult; + int dos = 0; + + do { + arr.alloc(uncompressedLen); + zlibResult = uncompress((Bytef*)arr.pData_, + &uncompressedLen, + compressedText, + compressedTextSize); + if (zlibResult == Z_OK) { + assert((uLongf)arr.size_ >= uncompressedLen); + arr.size_ = uncompressedLen; + } + else if (zlibResult == Z_BUF_ERROR) { + // the uncompressedArray needs to be larger + uncompressedLen *= 2; + // DoS protection. can't be bigger than 64k + if (uncompressedLen > 131072) { + if (++dos > 1) break; + uncompressedLen = 131072; + } + } + else { + // something bad happened + throw Error(14); + } + } + while (zlibResult == Z_BUF_ERROR); + + if (zlibResult != Z_OK) { + throw Error(14); + } + } + + + void PngImage::printStructure(std::ostream& out, PrintStructureOption option, int /*depth*/) { if (io_->open() != 0) { throw Error(9, io_->path(), strError()); @@ -104,7 +146,7 @@ namespace Exiv2 { throw Error(3, "PNG"); } - if ( option == kpsIccProfile || option == kpsRecursive ) { + if ( option == kpsRecursive || option == kpsIccProfile ) { // disable kpsIccProfile because decompress isn't working! throw Error(13, io_->path()); } @@ -112,7 +154,7 @@ namespace Exiv2 { chType[0]=0; chType[4]=0; - if ( option == kpsBasic || option == kpsXMP ) { + if ( option == kpsBasic || option == kpsXMP || option == kpsIccProfile ) { if ( option == kpsBasic ) { out << "STRUCTURE OF PNG FILE: " << io_->path() << std::endl; @@ -154,24 +196,38 @@ namespace Exiv2 { } if ( option == kpsBasic ) out << Internal::stringFormat("%8d | %5d | %10s |%8d | ",(uint32_t)address, index++,chType,dOff) << dataString << std::endl; - // for XMP, back up and read the whole block + + // for XMP and ICC, back up and read the whole block const char* key = "XML:com.adobe.xmp" ; size_t start = ::strlen(key); - if ( option == kpsXMP && dataString.find(key)==0 ) { + if( (option == kpsXMP && dataString.find(key)==0) + || (option == kpsIccProfile && std::strcmp(chType,"iCCP")==0) + ){ #if defined(_MSC_VER) io_->seek(-static_cast(blen) , BasicIo::cur); #else io_->seek(-static_cast(blen) , BasicIo::cur); #endif dataOffset = dOff ; - byte* xmp = new byte[dataOffset+5]; - io_->read(xmp,dataOffset+4); - xmp[dataOffset]=0; - while ( xmp[start] == 0 ) start++; // crawl over the '\0' bytes between XML:....\0\0read(xmp,dataOffset+4); + xmp[dataOffset]=0; + while ( xmp[start] == 0 ) start++; // crawl over the '\0' bytes between XML:....\0\0read(icc,dataOffset); + DataBuf decompressed; + size_t name_l = std::strlen((const char*)icc)+1; // length of profile name + zlibUncompress(icc+name_l,dataOffset-name_l,decompressed); + out.write((const char*)decompressed.pData_,decompressed.size_); + delete [] icc; + dataOffset = 0; + } } if ( dataOffset ) io_->seek(dataOffset + 4 , BasicIo::cur); diff --git a/src/tags.cpp b/src/tags.cpp index b6ef5278..44190404 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -85,6 +85,7 @@ namespace Exiv2 { { exifId, "Exif", "Photo", exifTagList }, { gpsId, "GPSInfo", "GPSInfo", gpsTagList }, { iopId, "Iop", "Iop", iopTagList }, + { mpfId, "MPF", "MPF", mpfTagList }, { subImage1Id, "SubImage1", "SubImage1", ifdTagList }, { subImage2Id, "SubImage2", "SubImage2", ifdTagList }, { subImage3Id, "SubImage3", "SubImage3", ifdTagList }, @@ -2118,11 +2119,81 @@ namespace Exiv2 { iopId, iopTags, asciiString, -1, printValue) }; + // MPF Tags http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html + static const TagInfo mpfTagInfo[] = { + TagInfo(0xb000, "MPFVersion", N_("MPFVersion"), + N_("MPF Version"), + mpfId, iopTags, asciiString, 0, printValue), + TagInfo(0xb001, "MPFNumberOfImages", N_("MPFNumberOfImages"), + N_("MPF Number of Images"), + mpfId, iopTags, undefined, -1, printExifVersion), + TagInfo(0xb002, "MPFImageList", N_("MPFImageList"), + N_("MPF Image List"), + mpfId, iopTags, asciiString, 0, printValue), + TagInfo(0xb003, "MPFImageUIDList", N_("MPFImageUIDList "), + N_("MPF Image UID List"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb004, "MPFTotalFrames", N_("MPFTotalFrames"), + N_("MPF Total Frames"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb101, "MPFIndividualNum", N_("MPFIndividualNum"), + N_("MPF Individual Num"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb201, "MPFPanOrientation", N_("MPFPanOrientation"), + N_("MPFPanOrientation"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb202, "MPFPanOverlapH", N_("MPFPanOverlapH"), + N_("MPF Pan Overlap Horizonal"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb203, "MPFPanOverlapV", N_("MPFPanOverlapV"), + N_("MPF Pan Overlap Vertical"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb204, "MPFBaseViewpointNum", N_("MPFBaseViewpointNum"), + N_("MPF Base Viewpoint Number"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb205, "MPFConvergenceAngle", N_("MPFConvergenceAngle"), + N_("MPF Convergence Angle"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb206, "MPFBaselineLength", N_("MPFBaselineLength"), + N_("MPF Baseline Length"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb207, "MPFVerticalDivergence", N_("MPFVerticalDivergence"), + N_("MPF Vertical Divergence"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb208, "MPFAxisDistanceX", N_("MPFAxisDistanceX"), + N_("MPF Axis Distance X"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb209, "MPFAxisDistanceY", N_("MPFAxisDistanceY"), + N_("MPF Axis Distance Y"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb20a, "MPFAxisDistanceZ", N_("MPFAxisDistanceZ"), + N_("MPF Axis Distance Z"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb20b, "MPFYawAngle", N_("MPFYawAngle"), + N_("MPF Yaw Angle"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb20c, "MPFPitchAngle", N_("MPFPitchAngle"), + N_("MPF Pitch Angle"), + mpfId, iopTags, unsignedLong, 1, printValue), + TagInfo(0xb20d, "MPFRollAngle", N_("MPFRollAngle"), + N_("MPF Roll Angle"), + mpfId, iopTags, unsignedLong, 1, printValue), + // End of list marker + TagInfo(0xffff, "(UnknownIopTag)", N_("Unknown MPF tag"), + N_("Unknown MPF tag"), + mpfId, iopTags, asciiString, -1, printValue) + }; + const TagInfo* iopTagList() { return iopTagInfo; } + const TagInfo* mpfTagList() + { + return mpfTagInfo; + } + // Synthesized Exiv2 Makernote info Tags (read-only) static const TagInfo mnTagInfo[] = { TagInfo(0x0001, "Offset", N_("Offset"), diff --git a/src/tags_int.hpp b/src/tags_int.hpp index 7d03969b..d1f2214c 100644 --- a/src/tags_int.hpp +++ b/src/tags_int.hpp @@ -62,6 +62,7 @@ namespace Exiv2 { exifId, gpsId, iopId, + mpfId, subImage1Id, subImage2Id, subImage3Id, @@ -304,6 +305,8 @@ namespace Exiv2 { const TagInfo* gpsTagList(); //! Return read-only list of built-in Exiv2 Makernote info tags const TagInfo* mnTagList(); + //! Return read-only list of built-in mfp Tags http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html + const TagInfo* mpfTagList(); //! Return the group id for a group name IfdId groupId(const std::string& groupName); diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp index d6390941..bb8a5ff5 100644 --- a/src/tiffimage.cpp +++ b/src/tiffimage.cpp @@ -393,6 +393,7 @@ namespace Exiv2 { for (ti = Exiv2:: gpsTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; for (ti = Exiv2:: ifdTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; for (ti = Exiv2::exifTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Exiv2:: mpfTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; } init = false; @@ -446,7 +447,14 @@ namespace Exiv2 { #define MIN(a,b) ((a)<(b))?(b):(a) - void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option) + static std::string indent(int depth) + { + std::string result; + while ( depth -- ) result += " "; + return result; + } + + void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option,int depth) { if (io_->open() != 0) throw Error(9, io_->path(), strError()); // Ensure that this is the correct image type @@ -455,113 +463,138 @@ namespace Exiv2 { throw Error(15); } - if ( option == kpsIccProfile || option == kpsRecursive ) { - throw Error(13, io_->path()); + if ( option == kpsIccProfile ) { + throw Error(13, io_->path()); + } + io_->seek(0,BasicIo::beg); + + printTiffStructure(io(),out,option,depth-1); + } + + void TiffImage::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,size_t start,bool bSwap,char c,int depth) + { + depth++; + if ( option == kpsBasic || option == kpsRecursive ) { + out << indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io.path() << std::endl; + out << indent(depth) << " address | tag | type | count | offset | value\n"; } + // buffer + const size_t dirSize = 32; + DataBuf dir(dirSize); + while ( start ) { + // if ( option == kpsBasic ) out << Internal::stringFormat("bSwap, start = %d %u\n",bSwap,offset); + + // Read top of directory + io.seek(start,BasicIo::beg); + io.read(dir.pData_, 2); + uint16_t dirLength = byteSwap2(dir,0,bSwap); + + // Read the dictionary + for ( int i = 0 ; i < dirLength ; i ++ ) { + io.read(dir.pData_, 12); + uint16_t tag = byteSwap2(dir,0,bSwap); + uint16_t type = byteSwap2(dir,2,bSwap); + uint32_t count = byteSwap4(dir,4,bSwap); + uint32_t offset = byteSwap4(dir,8,bSwap); + + std::string sp = "" ; // output spacer + + //prepare to print the value + uint16_t kount = isPrintXMP(tag,option) ? count // restrict long arrays + : isStringType(type) ? (count > 32 ? 32 : count) + : count > 5 ? 5 + : count + ; + uint32_t pad = isStringType(type) ? 1 : 0; + uint32_t size = isStringType(type) ? 1 + : is2ByteType(type) ? 2 + : is4ByteType(type) ? 4 + : 1 + ; + uint32_t Offset = 0 ; // used by ExifTag == 0x8769 && MakerNote == 0x927c to locate an FID + + // if ( offset > io.size() ) offset = 0; + DataBuf buf(MIN(size*kount + pad,48)); // allocate a buffer + if ( isStringType(type) || count*size > 4 ) { // data is in the directory => read into buffer + size_t restore = io.tell(); // save + io.seek(offset,BasicIo::beg); // position + io.read(buf.pData_,kount*size);// read + io.seek(restore,BasicIo::beg); // restore + } else { // move data from directory to the buffer + std::memcpy(buf.pData_,dir.pData_+8,12); + } + + if ( option == kpsBasic || option == kpsRecursive ) { + uint32_t address = start + 2 + i*12 ; + out << indent(depth) << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%9u | ",address,tag,tagName(tag,25),typeName(type),count,offset); + + if ( isShortType(type) ){ + for ( uint16_t k = 0 ; k < kount ; k++ ) { + out << sp << byteSwap2(buf,k*size,bSwap); + sp = " "; + } + } else if ( isLongType(type) ){ + for ( uint16_t k = 0 ; k < kount ; k++ ) { + out << sp << byteSwap4(buf,k*size,bSwap); + if ( k == 0 ) Offset = byteSwap4(buf,k*size,bSwap) ; + sp = " "; + } + } else if ( isRationalType(type) ){ + for ( uint16_t k = 0 ; k < kount ; k++ ) { + uint16_t a = byteSwap2(buf,k*size+0,bSwap); + uint16_t b = byteSwap2(buf,k*size+2,bSwap); + if ( isLittleEndian() ) { + if ( bSwap ) out << sp << b << "/" << a; + else out << sp << a << "/" << b; + } else { + if ( bSwap ) out << sp << a << "/" << b; + else out << sp << b << "/" << a; + } + sp = " "; + } + } else if ( isStringType(type) ) { + out << sp << Internal::binaryToString(buf, kount); + } + sp = kount == count ? "" : " ..."; + out << sp << std::endl; + if ( option == kpsRecursive + && (tag == 0x8769 /* ExifTag */ || tag == 0x927c /* MakerNote */) + ){ + size_t restore = io.tell(); + printIFDStructure(io,out,option,Offset,bSwap,c,depth); + io.seek(restore,BasicIo::beg); + } + } - if ( option == kpsBasic || option == kpsXMP ) { - io_->seek(0,BasicIo::beg); + if ( isPrintXMP(tag,option) ) { + buf.pData_[count]=0; + out << (char*) buf.pData_; + } + } + io.read(dir.pData_, 4); + start = byteSwap4(dir,0,bSwap); + out.flush(); + } // while start + depth--; + } + + void TiffImage::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,int depth) + { + if ( option == kpsBasic || option == kpsXMP || option == kpsRecursive ) { // buffer const size_t dirSize = 32; DataBuf dir(dirSize); // read header (we already know for certain that we have a Tiff file) - io_->read(dir.pData_, 8); + io.read(dir.pData_, 8); char c = (char) dir.pData_[0] ; bool bSwap = ( c == 'M' && isLittleEndian() ) || ( c == 'I' && isBigEndian() ) ; - if ( option == kpsBasic ) { - out << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io_->path() << std::endl; - out << " address | tag | type | count | offset | value\n"; - } - uint32_t start = byteSwap4(dir,4,bSwap); - while ( start ) { - // if ( option == kpsBasic ) out << Internal::stringFormat("bSwap, start = %d %u\n",bSwap,offset); - - // Read top of directory - io_->seek(start,BasicIo::beg); - io_->read(dir.pData_, 2); - uint16_t dirLength = byteSwap2(dir,0,bSwap); - - // Read the dictionary - for ( int i = 0 ; i < dirLength ; i ++ ) { - io_->read(dir.pData_, 12); - uint16_t tag = byteSwap2(dir,0,bSwap); - uint16_t type = byteSwap2(dir,2,bSwap); - uint32_t count = byteSwap4(dir,4,bSwap); - uint32_t offset = byteSwap4(dir,8,bSwap); - - std::string sp = "" ; // output spacer - - //prepare to print the value - uint16_t kount = isPrintXMP(tag,option) ? count // restrict long arrays - : isStringType(type) ? (count > 32 ? 32 : count) - : count > 5 ? 5 - : count - ; - uint32_t pad = isStringType(type) ? 1 : 0; - uint32_t size = isStringType(type) ? 1 - : is2ByteType(type) ? 2 - : is4ByteType(type) ? 4 - : 1 - ; - - DataBuf buf(MIN(size*kount + pad,48)); // allocate a buffer - if ( isStringType(type) || count*size > 4 ) { // data is in the directory => read into buffer - size_t restore = io_->tell(); // save - io_->seek(offset,BasicIo::beg); // position - io_->read(buf.pData_,kount*size);// read - io_->seek(restore,BasicIo::beg); // restore - } else { // move data from directory to the buffer - std::memcpy(buf.pData_,dir.pData_+8,12); - } - - if ( option == kpsBasic ) { - uint32_t address = start + 2 + i*12 ; - out << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%9u | ",address,tag,tagName(tag,25),typeName(type),count,offset); - - if ( isShortType(type) ){ - for ( uint16_t k = 0 ; k < kount ; k++ ) { - out << sp << byteSwap2(buf,k*size,bSwap); - sp = " "; - } - } else if ( isLongType(type) ){ - for ( uint16_t k = 0 ; k < kount ; k++ ) { - out << sp << byteSwap4(buf,k*size,bSwap); - sp = " "; - } - } else if ( isRationalType(type) ){ - for ( uint16_t k = 0 ; k < kount ; k++ ) { - uint16_t a = byteSwap2(buf,k*size+0,bSwap); - uint16_t b = byteSwap2(buf,k*size+2,bSwap); - if ( isLittleEndian() ) { - if ( bSwap ) out << sp << b << "/" << a; - else out << sp << a << "/" << b; - } else { - if ( bSwap ) out << sp << a << "/" << b; - else out << sp << b << "/" << a; - } - sp = " "; - } - } else if ( isStringType(type) ) { - out << sp << Internal::binaryToString(buf, kount); - } - sp = kount == count ? "" : " ..."; - out << sp << std::endl; - } - - if ( isPrintXMP(tag,option) ) { - buf.pData_[count]=0; - out << (char*) buf.pData_; - } - } - io_->read(dir.pData_, 4); - start = byteSwap4(dir,0,bSwap); - } // while offset + printIFDStructure(io,out,option,start,bSwap,c,depth); } }