diff --git a/include/exiv2/basicio.hpp b/include/exiv2/basicio.hpp index 669eed6a..1c849f18 100644 --- a/include/exiv2/basicio.hpp +++ b/include/exiv2/basicio.hpp @@ -18,13 +18,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. */ -/*! - @file basicio.hpp - @brief Simple binary IO abstraction - @version $Rev: 3091 $ - @author Brad Schick (brad) - brad@robotbattle.com - @date 04-Dec-04, brad: created +/* + File: basicio.hpp + Version: $Rev: 4633 $ */ #ifndef BASICIO_HPP_ #define BASICIO_HPP_ @@ -97,6 +93,7 @@ namespace Exiv2 { Nonzero if failure. */ virtual int open() = 0; + /*! @brief Close the IO source. After closing a BasicIo instance can not be read or written. Closing flushes any unwritten data. It is @@ -259,6 +256,18 @@ namespace Exiv2 { */ virtual BasicIo::AutoPtr temporary() const = 0; + /*! + @brief markBlocks that we know for certain are not metadata + @note This method is designed to enable TIFF files to mark StripOffsets/StringByteCounts + */ + virtual void markRead(long offset,long count); + + /*! + @brief read all blocks that are not marked + @note This method is designed to enable TIFF file to read everything except the markRead blocks! + */ + virtual void readUnmarked(); + /*! @brief Mark all the bNone blocks to bKnow. This avoids allocating memory for parts of the file that contain image-date (non-metadata/pixel data) @@ -945,6 +954,13 @@ namespace Exiv2 { Nonzero if failure. */ virtual int open(); + + virtual void markRead(long offset,long count); + + virtual void readUnmarked(); + + byte* bigBlock_; + /*! @brief Reset the IO position to the start. It does not release the data. @return 0 if successful;
@@ -1097,6 +1113,7 @@ namespace Exiv2 { are all downloaded from the remote file to memory. */ virtual void populateFakeData(); + //@} protected: diff --git a/include/exiv2/image.hpp b/include/exiv2/image.hpp index 6eee4ff1..8e26b13f 100644 --- a/include/exiv2/image.hpp +++ b/include/exiv2/image.hpp @@ -318,6 +318,42 @@ namespace Exiv2 { little-endian byte order (II) is used by default. */ void setByteOrder(ByteOrder byteOrder); + + /*! + @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). + */ + void printTiffStructure(BasicIo& io,std::ostream& out, PrintStructureOption option,int depth,size_t offset=0); + + /*! + @brief Print out the structure of a TIFF IFD + */ + void printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,uint32_t start,bool bSwap,char c,int depth); + + /*! + @brief is the host platform bigEndian + */ + bool isBigEndianPlatform(); + + /*! + @brief is the host platform littleEndian + */ + bool isLittleEndianPlatform(); + bool isStringType(uint16_t type); + bool isShortType(uint16_t type); + bool isLongType(uint16_t type); + bool isRationalType(uint16_t type); + bool is2ByteType(uint16_t type); + bool is4ByteType(uint16_t type); + bool isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option); + bool isPrintICC(uint16_t type, Exiv2::PrintStructureOption option); + + uint32_t byteSwap(uint32_t value,bool bSwap); + uint16_t byteSwap(uint16_t value,bool bSwap); + uint16_t byteSwap2(DataBuf& buf,size_t offset,bool bSwap); + uint32_t byteSwap4(DataBuf& buf,size_t offset,bool bSwap); + //@} //! @name Accessors @@ -452,6 +488,9 @@ namespace Exiv2 { int pixelHeight_; //!< image pixel height NativePreviewList nativePreviews_; //!< list of native previews + std::vector stripOffsets; //!< StripOffset data + std::vector stripByteCounts ; //!< StripByteCount data + private: //! @name NOT implemented //@{ diff --git a/include/exiv2/tiffimage.hpp b/include/exiv2/tiffimage.hpp index cf5dc15e..d1fe35ca 100644 --- a/include/exiv2/tiffimage.hpp +++ b/include/exiv2/tiffimage.hpp @@ -18,14 +18,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. */ -/*! - @file tiffimage.hpp - @brief Class TiffImage - @version $Rev: 3090 $ - @author Andreas Huggel (ahu) - ahuggel@gmx.net - @date 15-Mar-06, ahu: created +/* + File: tiffimage.hpp + Version: $Rev: 4633 $ */ + #ifndef TIFFIMAGE_HPP_ #define TIFFIMAGE_HPP_ @@ -95,20 +92,6 @@ namespace Exiv2 { */ virtual 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,size_t offset=0); - - /*! - @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,uint32_t start,bool bSwap,char c,int depth); - /*! @brief Not supported. TIFF format does not contain a comment. Calling this function will throw an Error(32). diff --git a/src/basicio.cpp b/src/basicio.cpp index 201bc056..93677531 100644 --- a/src/basicio.cpp +++ b/src/basicio.cpp @@ -21,8 +21,6 @@ /* File: basicio.cpp Version: $Rev$ - Author(s): Brad Schick (brad) - History: 04-Dec-04, brad: created */ // ***************************************************************************** #include "rcsid_int.hpp" @@ -86,6 +84,10 @@ namespace Exiv2 { { } + void BasicIo::markRead(long /*offset*/,long /*count*/) { return ; } + + void BasicIo::readUnmarked() { return ; } + //! Internal Pimpl structure of class FileIo. class FileIo::Impl { public: @@ -620,25 +622,25 @@ namespace Exiv2 { std::string FileIo::temporaryPath() { - static int count = 0 ; - char sCount[12]; - sprintf(sCount,"_%d",count++); + static int count = 0 ; + char sCount[12]; + sprintf(sCount,"_%d",count++); #if defined(_MSC_VER) || defined(__MINGW__) char lpTempPathBuffer[MAX_PATH]; - GetTempPath(MAX_PATH,lpTempPathBuffer); - std::string tmp(lpTempPathBuffer); - tmp += "\\"; + GetTempPath(MAX_PATH,lpTempPathBuffer); + std::string tmp(lpTempPathBuffer); + tmp += "\\"; #else - std::string tmp = "/tmp/"; + std::string tmp = "/tmp/"; #endif - pid_t pid = ::getpid(); - std::string result = tmp + toString(pid) + sCount ; - if ( Exiv2::fileExists(result) ) std::remove(result.c_str()); + pid_t pid = ::getpid(); + std::string result = tmp + toString(pid) + sCount ; + if ( Exiv2::fileExists(result) ) std::remove(result.c_str()); - return result; - } + return result; + } long FileIo::write(const byte* data, long wcount) { @@ -1562,6 +1564,7 @@ namespace Exiv2 { bool isMalloced_; //!< Was the blocksMap_ allocated? bool eof_; //!< EOF indicator Protocol protocol_; //!< the protocol of url + uint32_t totalRead_; // METHODS /*! @@ -1604,7 +1607,7 @@ namespace Exiv2 { RemoteIo::Impl::Impl(const std::string& url, size_t blockSize) : path_(url), blockSize_(blockSize), blocksMap_(0), size_(0), - idx_(0), isMalloced_(false), eof_(false), protocol_(fileProtocol(url)) + idx_(0), isMalloced_(false), eof_(false), protocol_(fileProtocol(url)),totalRead_(0) { } #ifdef EXV_UNICODE_PATH @@ -1663,6 +1666,7 @@ namespace Exiv2 { int RemoteIo::open() { close(); // reset the IO position + bigBlock_ = NULL; if (p_->isMalloced_ == false) { long length = p_->getFileLength(); if (length < 0) { // unable to get the length of remote file, get the whole file content. @@ -1693,12 +1697,68 @@ namespace Exiv2 { return 0; // means OK } + void RemoteIo::markRead(long offset,long count) + { + // std::cout << "RemoteIo::markRead offset = " << offset << " count = " << count << std::endl; + int blockLow = (offset )/p_->blockSize_ ; + int blockHigh = (offset + count )/p_->blockSize_ ; + for ( int block = blockLow+1 ; block < blockHigh-1 ; block++ ) { + p_->blocksMap_[block].markKnown(p_->blockSize_); + } + } + + void RemoteIo::readUnmarked() + { + long blockSize = p_->blockSize_; + int nBlocks = (p_->size_ + blockSize -1) / blockSize ; +#ifdef DEBUG + int nRead = 0; + int nHigh = 0; + int nGulp = 0; + int nBytes = 0; + for ( int block = 0 ; block < nBlocks ; block++ ) { + if ( p_->blocksMap_[block].isNone() ) { + nRead++ ; + nHigh = block; + } + } + std::cerr << "RemoteIo::readUnmarked nBlocks = " << nBlocks << " nRead = " << nRead << " nHigh = " << nHigh << std::endl; + for ( int block = 0 ; block < nBlocks ; block ++ ) { + char x = 'X'; + if ( p_->blocksMap_[block].isNone() ) x='.'; + if ( p_->blocksMap_[block].isInMem() ) x='R'; + if ( p_->blocksMap_[block].isKnown() ) x='-'; + std::cerr << x ; + } + std::cerr << std::endl; +#endif + for ( int block = 0 ; block < nBlocks ; block ++ ) { + if ( p_->blocksMap_[block].isNone() ) { + int b = block; + if ( b < (nBlocks -1)) while ( p_->blocksMap_[b+1].isNone() && b < nBlocks ) b++; + seek(block*blockSize,BasicIo::beg); + read(blockSize); +#ifdef DEBUG + nBytes += blockSize*(b-block+1); + nGulp ++ ; +#endif + block = b ; + } + } +#ifdef DEBUG + std::cerr << "RemoteIo::readUnmarked nGulp = " << nGulp << " nBytes = " << nBytes << std::endl; +#endif + } + int RemoteIo::close() { if (p_->isMalloced_) { p_->eof_ = false; p_->idx_ = 0; } +#ifdef DEBUG + std::cerr << "RemoteIo::close totalRead_ = " << p_->totalRead_ << std::endl; +#endif return 0; } @@ -1746,7 +1806,6 @@ namespace Exiv2 { blockIndex++; } - // find $right findDiff = false; blockIndex = nBlocks - 1; @@ -1802,6 +1861,7 @@ namespace Exiv2 { { assert(p_->isMalloced_); if (p_->eof_) return 0; + p_->totalRead_ += rcount; size_t allow = EXV_MIN(rcount, (long)( p_->size_ - p_->idx_)); size_t lowBlock = p_->idx_ /p_->blockSize_; @@ -1900,7 +1960,23 @@ namespace Exiv2 { byte* RemoteIo::mmap(bool /*isWriteable*/) { - return NULL; + long nRealData = 0 ; + if ( !bigBlock_ ) { + int blockSize = p_->blockSize_; + int blocks = (p_->size_ + blockSize -1)/blockSize ; + bigBlock_ = new byte[blocks*blockSize] ; + for ( int block = 0 ; block < blocks ; block ++ ) { + void* p = p_->blocksMap_[block].getData(); + if ( p ) { + nRealData += blockSize ; + memcpy(bigBlock_+(block*blockSize),p,blockSize); + } + } + } +#ifdef DEBUG + std::cerr << "RemoteIo::mmap nRealData = " << nRealData << std::endl; +#endif + return bigBlock_; } int RemoteIo::munmap() diff --git a/src/cr2image.cpp b/src/cr2image.cpp index aedd48a9..0808a3a8 100644 --- a/src/cr2image.cpp +++ b/src/cr2image.cpp @@ -21,9 +21,6 @@ /* File: cr2image.cpp Version: $Rev$ - Author(s): Andreas Huggel (ahu) - History: 22-Apr-06, ahu: created - */ // ***************************************************************************** #include "rcsid_int.hpp" @@ -86,10 +83,9 @@ namespace Exiv2 { { if (io_->open() != 0) throw Error(9, io_->path(), strError()); io_->seek(0,BasicIo::beg); - TiffImage::printTiffStructure(io(),out,option,depth-1); + printTiffStructure(io(),out,option,depth-1); } - void Cr2Image::setComment(const std::string& /*comment*/) { // not supported diff --git a/src/image.cpp b/src/image.cpp index fde1fac8..621391e8 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -44,6 +44,9 @@ EXIV2_RCSID("@(#) $Id$") #endif // EXV_HAVE_LIBZ #include "rafimage.hpp" #include "tiffimage.hpp" +#include "tiffimage_int.hpp" +#include "tiffcomposite_int.hpp" +#include "tiffvisitor_int.hpp" #include "webpimage.hpp" #include "orfimage.hpp" #include "gifimage.hpp" @@ -51,6 +54,8 @@ EXIV2_RCSID("@(#) $Id$") #include "tgaimage.hpp" #include "bmpimage.hpp" #include "jp2image.hpp" +#include "nikonmn_int.hpp" + #ifdef EXV_ENABLE_VIDEO #include "matroskavideo.hpp" #include "quicktimevideo.hpp" @@ -170,6 +175,335 @@ namespace Exiv2 { throw Error(13, io_->path()); } + bool Image::isStringType(uint16_t type) + { + return type == Exiv2::asciiString + || type == Exiv2::unsignedByte + || type == Exiv2::signedByte + || type == Exiv2::undefined + ; + } + bool Image::isShortType(uint16_t type) { + return type == Exiv2::unsignedShort + || type == Exiv2::signedShort + ; + } + bool Image::isLongType(uint16_t type) { + return type == Exiv2::unsignedLong + || type == Exiv2::signedLong + ; + } + bool Image::isRationalType(uint16_t type) { + return type == Exiv2::unsignedRational + || type == Exiv2::signedRational + ; + } + bool Image::is2ByteType(uint16_t type) + { + return isShortType(type); + } + bool Image::is4ByteType(uint16_t type) + { + return isLongType(type) + || isRationalType(type) + ; + } + bool Image::isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option) + { + return type == 700 && option == kpsXMP; + } + bool Image::isPrintICC(uint16_t type, Exiv2::PrintStructureOption option) + { + return type == 0x8773 && option == kpsIccProfile; + } + + bool Image::isBigEndianPlatform() + { + union { + uint32_t i; + char c[4]; + } e = { 0x01000000 }; + + return e.c[0]?true:false; + } + bool Image::isLittleEndianPlatform() { return !isBigEndianPlatform(); } + + uint32_t Image::byteSwap(uint32_t value,bool bSwap) + { + uint32_t result = 0; + result |= (value & 0x000000FF) << 24; + result |= (value & 0x0000FF00) << 8; + result |= (value & 0x00FF0000) >> 8; + result |= (value & 0xFF000000) >> 24; + return bSwap ? result : value; + } + + uint16_t Image::byteSwap(uint16_t value,bool bSwap) + { + uint16_t result = 0; + result |= (value & 0x00FF) << 8; + result |= (value & 0xFF00) >> 8; + return bSwap ? result : value; + } + + uint16_t Image::byteSwap2(DataBuf& buf,size_t offset,bool bSwap) + { + uint16_t v; + char* p = (char*) &v; + p[0] = buf.pData_[offset]; + p[1] = buf.pData_[offset+1]; + return Image::byteSwap(v,bSwap); + } + + uint32_t Image::byteSwap4(DataBuf& buf,size_t offset,bool bSwap) + { + uint32_t v; + char* p = (char*) &v; + p[0] = buf.pData_[offset]; + p[1] = buf.pData_[offset+1]; + p[2] = buf.pData_[offset+2]; + p[3] = buf.pData_[offset+3]; + return Image::byteSwap(v,bSwap); + } + + static const char* tagName(uint16_t tag,size_t nMaxLength) + { + const char* result = NULL; + + // build a static map of tags for fast search + static std::map tags; + static bool init = true; + static char buffer[80]; + + if ( init ) { + int idx; + const TagInfo* ti ; + for (ti = Internal:: mnTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Internal:: iopTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Internal:: gpsTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Internal:: ifdTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Internal::exifTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Internal:: mpfTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + for (ti = Internal::Nikon1MakerNote::tagList(), idx = 0 + ; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; + } + init = false; + + try { + result = tags[tag].c_str(); + if ( nMaxLength > sizeof(buffer) -2 ) + nMaxLength = sizeof(buffer) -2; + strncpy(buffer,result,nMaxLength); + result = buffer; + } catch ( ... ) {} + + return result ; + } + + static const char* typeName(uint16_t tag) + { + //! List of TIFF image tags + const char* result = NULL; + switch (tag ) { + case Exiv2::unsignedByte : result = "BYTE" ; break; + case Exiv2::asciiString : result = "ASCII" ; break; + case Exiv2::unsignedShort : result = "SHORT" ; break; + case Exiv2::unsignedLong : result = "LONG" ; break; + case Exiv2::unsignedRational : result = "RATIONAL" ; break; + case Exiv2::signedByte : result = "SBYTE" ; break; + case Exiv2::undefined : result = "UNDEFINED" ; break; + case Exiv2::signedShort : result = "SSHORT" ; break; + case Exiv2::signedLong : result = "SLONG" ; break; + case Exiv2::signedRational : result = "SRATIONAL" ; break; + case Exiv2::tiffFloat : result = "FLOAT" ; break; + case Exiv2::tiffDouble : result = "DOUBLE" ; break; + default : result = "unknown" ; break; + } + return result; + } + + void Image::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,uint32_t start,bool bSwap,char c,int depth) + { + depth++; + bool bFirst = true ; + + // buffer + const size_t dirSize = 32; + DataBuf dir(dirSize); + bool bPrint = option == kpsBasic || option == kpsRecursive; + + do { + // Read top of directory + io.seek(start,BasicIo::beg); + io.read(dir.pData_, 2); + uint16_t dirLength = byteSwap2(dir,0,bSwap); + + bool tooBig = dirLength > 500; + + if ( bFirst && bPrint ) { + out << Internal::indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io.path() << std::endl; + if ( tooBig ) out << Internal::indent(depth) << "dirLength = " << dirLength << std::endl; + } + if (tooBig) break; + + // Read the dictionary + for ( int i = 0 ; i < dirLength ; i ++ ) { + if ( bFirst && bPrint ) { + out << Internal::indent(depth) + << " address | tag | " + << " type | count | offset | value\n"; + } + bFirst = false; + + 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 + uint32_t kount = isPrintXMP(tag,option) ? count // haul in all the data + : isPrintICC(tag,option) ? count // ditto + : isStringType(type) ? (count > 32 ? 32 : count) // restrict long arrays + : count > 5 ? 5 + : count + ; + uint32_t pad = isStringType(type) ? 1 : 0; + uint32_t size = isStringType(type) ? 1 + : is2ByteType(type) ? 2 + : is4ByteType(type) ? 4 + : 1 + ; + + // if ( offset > io.size() ) offset = 0; // Denial of service? + DataBuf buf(size*count + pad); // allocate a buffer + std::memcpy(buf.pData_,dir.pData_+8,4); // copy dir[8:11] into buffer (short strings) + if ( count*size > 4 ) { // read into buffer + size_t restore = io.tell(); // save + io.seek(offset,BasicIo::beg); // position + io.read(buf.pData_,count*size);// read + io.seek(restore,BasicIo::beg); // restore + } + + if ( bPrint ) { + uint32_t address = start + 2 + i*12 ; + out << Internal::indent(depth) + << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%10u | " + ,address,tag,tagName(tag,25),typeName(type),count,offset); + if ( isShortType(type) ){ + for ( size_t k = 0 ; k < kount ; k++ ) { + out << sp << byteSwap2(buf,k*size,bSwap); + sp = " "; + } + } else if ( isLongType(type) ){ + for ( size_t k = 0 ; k < kount ; k++ ) { + out << sp << byteSwap4(buf,k*size,bSwap); + sp = " "; + } + // populate strips and bytes + if ( tag == 0x0111 ) { + for ( size_t k = 0 ; k < count ; k++ ) // stripOffsets + stripOffsets.push_back(byteSwap4(buf,k*size,bSwap)); + } + if ( tag == 0x0117 ) { + for ( size_t k = 0 ; k < count ; k++ ) // stripByteCounts + stripByteCounts.push_back(byteSwap4(buf,k*size,bSwap)); + } + + } else if ( isRationalType(type) ){ + for ( size_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 ( isLittleEndianPlatform() ) { + 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 == 0x014a/*SubIFDs*/ ) ) { + for ( size_t k = 0 ; k < count ; k++ ) { + size_t restore = io.tell(); + uint32_t offset = byteSwap4(buf,k*size,bSwap); + printIFDStructure(io,out,option,offset,bSwap,c,depth); + io.seek(restore,BasicIo::beg); + } + } else if ( option == kpsRecursive && tag == 0x83bb /* IPTCNAA */ ) { + size_t restore = io.tell(); // save + io.seek(offset,BasicIo::beg); // position + byte* bytes=new byte[count] ; // allocate memory + io.read(bytes,count) ; // read + io.seek(restore,BasicIo::beg); // restore + IptcData::printStructure(out,bytes,count,depth); + delete[] bytes; // free + } else if ( option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) { + uint32_t jump= 10 ; + byte bytes[20] ; + const char* chars = (const char*) &bytes[0] ; + size_t restore = io.tell(); // save + io.seek(offset,BasicIo::beg); // position + io.read(bytes,jump ) ; // read + bytes[jump]=0 ; + if ( ::strcmp("Nikon",chars) == 0 ) { + // tag is an embedded tiff + byte* bytes=new byte[count-jump] ; // allocate memory + io.read(bytes,count-jump) ; // read + MemIo memIo(bytes,count-jump) ; // create a file + printTiffStructure(memIo,out,option,depth); + delete[] bytes ; // free + } + io.seek(restore,BasicIo::beg); // restore + } + } + + if ( isPrintXMP(tag,option) ) { + buf.pData_[count]=0; + out << (char*) buf.pData_; + } + if ( isPrintICC(tag,option) ) { + out.write((const char*)buf.pData_,count); + } + } + io.read(dir.pData_, 4); + start = tooBig ? 0 : byteSwap4(dir,0,bSwap); + out.flush(); + } while (start) ; + + if ( bPrint ) { + out << Internal::indent(depth) << "END " << io.path() << std::endl; + } + depth--; + } + + void Image::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,int depth,size_t offset /*=0*/) + { + if ( option == kpsBasic || option == kpsXMP || option == kpsRecursive || option == kpsIccProfile ) { + // 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); + char c = (char) dir.pData_[0] ; + bool bSwap = ( c == 'M' && isLittleEndianPlatform() ) + || ( c == 'I' && isBigEndianPlatform() ) + ; + uint32_t start = byteSwap4(dir,4,bSwap); + printIFDStructure(io,out,option,start+(uint32_t)offset,bSwap,c,depth); + } + } + void Image::clearMetadata() { clearExifData(); diff --git a/src/jp2image.cpp b/src/jp2image.cpp index 4e56072b..385d49fa 100644 --- a/src/jp2image.cpp +++ b/src/jp2image.cpp @@ -553,7 +553,7 @@ namespace Exiv2 && (rawData.pData_[0]=='I' || rawData.pData_[0]=='M' ) ) { BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(rawData.pData_,rawData.size_)); - TiffImage::printTiffStructure(*p,out,option,depth); + printTiffStructure(*p,out,option,depth); } } diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index 7059aae3..21d3d79e 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -769,7 +769,7 @@ namespace Exiv2 { } else { // 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)); - if ( start < max ) TiffImage::printTiffStructure(*p,out,option,depth); + if ( start < max ) printTiffStructure(*p,out,option,depth); } // restore and clean up diff --git a/src/pgfimage.cpp b/src/pgfimage.cpp index 358ab588..8b399d92 100644 --- a/src/pgfimage.cpp +++ b/src/pgfimage.cpp @@ -64,19 +64,7 @@ const unsigned char pgfBlank[] = { 0x50,0x47,0x46,0x36,0x10,0x00,0x00,0x00,0x01, namespace Exiv2 { - // http://en.wikipedia.org/wiki/Endianness - static bool isBigEndian() - { - union { - uint32_t i; - char c[4]; - } e = { 0x01000000 }; - - return e.c[0]?true:false; - } - // static bool isLittleEndian() { return !isBigEndian(); } - - static uint32_t byteSwap(uint32_t value,bool bSwap) + static uint32_t byteSwap_(uint32_t value,bool bSwap) { uint32_t result = 0; result |= (value & 0x000000FF) << 24; @@ -85,31 +73,14 @@ namespace Exiv2 { result |= (value & 0xFF000000) >> 24; return bSwap ? result : value; } -/* - static uint16_t byteSwap(uint16_t value,bool bSwap) - { - uint16_t result = 0; - result |= (value & 0x00FF) << 8; - result |= (value & 0xFF00) >> 8; - return bSwap ? result : value; - } - static uint16_t byteSwap2(Exiv2::DataBuf& buf,size_t offset,bool bSwap) - { - uint16_t v; - char* p = (char*) &v; - p[0] = buf.pData_[offset]; - p[1] = buf.pData_[offset+1]; - return byteSwap(v,bSwap); - } -*/ - static uint32_t byteSwap(Exiv2::DataBuf& buf,size_t offset,bool bSwap) + static uint32_t byteSwap_(Exiv2::DataBuf& buf,size_t offset,bool bSwap) { uint32_t v; char* p = (char*) &v; int i; for ( i = 0 ; i < 4 ; i++ ) p[i] = buf.pData_[offset+i]; - uint32_t result = byteSwap(v,bSwap); + uint32_t result = byteSwap_(v,bSwap); p = (char*) &result; for ( i = 0 ; i < 4 ; i++ ) buf.pData_[offset+i] = p[i]; return result; @@ -117,7 +88,7 @@ namespace Exiv2 { PgfImage::PgfImage(BasicIo::AutoPtr io, bool create) : Image(ImageType::pgf, mdExif | mdIptc| mdXmp | mdComment, io) - , bSwap_(isBigEndian()) + , bSwap_(isBigEndianPlatform()) { if (create) { @@ -251,7 +222,7 @@ namespace Exiv2 { uint32_t newHeaderSize = header.size_ + imgSize; DataBuf buffer(4); memcpy (buffer.pData_, &newHeaderSize, 4); - byteSwap(buffer,0,bSwap_); + byteSwap_(buffer,0,bSwap_); if (outIo.write(buffer.pData_, 4) != 4) throw Error(21); #ifdef DEBUG @@ -304,7 +275,7 @@ namespace Exiv2 { if (iIo.error()) throw Error(14); if (bufRead != buffer.size_) throw Error(20); - int headerSize = (int) byteSwap(buffer,0,bSwap_); + int headerSize = (int) byteSwap_(buffer,0,bSwap_); if (headerSize <= 0 ) throw Error(22); #ifdef DEBUG @@ -323,8 +294,8 @@ namespace Exiv2 { DataBuf work(8); // don't disturb the binary data - doWriteMetadata reuses it memcpy (work.pData_,header.pData_,8); - width = byteSwap(work,0,bSwap_); - height = byteSwap(work,4,bSwap_); + width = byteSwap_(work,0,bSwap_); + height = byteSwap_(work,4,bSwap_); /* NOTE: properties not yet used byte nLevels = buffer.pData_[8]; diff --git a/src/pngimage.cpp b/src/pngimage.cpp index 4c9d5991..b0a288e3 100644 --- a/src/pngimage.cpp +++ b/src/pngimage.cpp @@ -334,7 +334,7 @@ namespace Exiv2 { if ( bExif ) { // create memio object with the data, then print the structure BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(parsedBuf.pData_+6,parsedBuf.size_-6)); - TiffImage::printTiffStructure(*p,out,option,depth); + printTiffStructure(*p,out,option,depth); } if ( bIptc ) { IptcData::printStructure(out,parsedBuf.pData_,parsedBuf.size_,depth); diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp index f0eb1f1f..ef78fa35 100644 --- a/src/tiffimage.cpp +++ b/src/tiffimage.cpp @@ -41,6 +41,7 @@ EXIV2_RCSID("@(#) $Id$") #include "error.hpp" #include "futils.hpp" #include "types.hpp" +#include "basicio.hpp" #include "i18n.h" // NLS support. // + standard includes @@ -183,6 +184,25 @@ namespace Exiv2 { throw Error(3, "TIFF"); } clearMetadata(); + + // recursively print the structure to /dev/null to ensure all metadata is in memory + // must be recursive to handle NEFs which stores the raw image in a subIFDs + std::ofstream devnull; + printStructure(devnull,kpsRecursive,0); +#ifdef DEBUG + assert(stripOffsets.size() == stripByteCounts.size()); + int ignored =0 ; + for ( int strip = 0 ; strip < stripOffsets.size() ; strip ++ ) + ignored += stripByteCounts[strip]; + std::cout << "TiffImage::readMetadata ignored = " << ignored << " size = " << io().size() << std::endl; +#endif +#if 0 + assert(stripOffsets.size() == stripByteCounts.size()); + // tell io_ to ignore the strips and read everything else into memory + for ( size_t strip = 0 ; strip < stripOffsets.size() ; strip ++ ) + io_->markRead(stripOffsets[strip],stripByteCounts[strip]); + io_->readUnmarked(); +#endif ByteOrder bo = TiffParser::decode(exifData_, iptcData_, xmpData_, @@ -327,157 +347,6 @@ namespace Exiv2 { return rc; } - bool isBigEndian() - { - union { - uint32_t i; - char c[4]; - } e = { 0x01000000 }; - - return e.c[0]?true:false; - } - bool isLittleEndian() { return !isBigEndian(); } - - - // http://en.wikipedia.org/wiki/Endianness - static uint32_t byteSwap(uint32_t value,bool bSwap) - { - uint32_t result = 0; - result |= (value & 0x000000FF) << 24; - result |= (value & 0x0000FF00) << 8; - result |= (value & 0x00FF0000) >> 8; - result |= (value & 0xFF000000) >> 24; - return bSwap ? result : value; - } - - static uint16_t byteSwap(uint16_t value,bool bSwap) - { - uint16_t result = 0; - result |= (value & 0x00FF) << 8; - result |= (value & 0xFF00) >> 8; - return bSwap ? result : value; - } - - static uint16_t byteSwap2(DataBuf& buf,size_t offset,bool bSwap) - { - uint16_t v; - char* p = (char*) &v; - p[0] = buf.pData_[offset]; - p[1] = buf.pData_[offset+1]; - return byteSwap(v,bSwap); - } - - static uint32_t byteSwap4(DataBuf& buf,size_t offset,bool bSwap) - { - uint32_t v; - char* p = (char*) &v; - p[0] = buf.pData_[offset]; - p[1] = buf.pData_[offset+1]; - p[2] = buf.pData_[offset+2]; - p[3] = buf.pData_[offset+3]; - return byteSwap(v,bSwap); - } - - static const char* typeName(uint16_t tag) - { - //! List of TIFF image tags - const char* result = NULL; - switch (tag ) { - case Exiv2::unsignedByte : result = "BYTE" ; break; - case Exiv2::asciiString : result = "ASCII" ; break; - case Exiv2::unsignedShort : result = "SHORT" ; break; - case Exiv2::unsignedLong : result = "LONG" ; break; - case Exiv2::unsignedRational : result = "RATIONAL" ; break; - case Exiv2::signedByte : result = "SBYTE" ; break; - case Exiv2::undefined : result = "UNDEFINED" ; break; - case Exiv2::signedShort : result = "SSHORT" ; break; - case Exiv2::signedLong : result = "SLONG" ; break; - case Exiv2::signedRational : result = "SRATIONAL" ; break; - case Exiv2::tiffFloat : result = "FLOAT" ; break; - case Exiv2::tiffDouble : result = "DOUBLE" ; break; - default : result = "unknown" ; break; - } - return result; - } - - static const char* tagName(uint16_t tag,size_t nMaxLength) - { - const char* result = NULL; - - // build a static map of tags for fast search - static std::map tags; - static bool init = true; - static char buffer[80]; - - if ( init ) { - int idx; - const TagInfo* ti ; - for (ti = Exiv2:: mnTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; - for (ti = Exiv2:: iopTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; - 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_; - for (ti = Nikon1MakerNote::tagList(), idx = 0 - ; ti[idx].tag_ != 0xffff; ++idx) tags[ti[idx].tag_] = ti[idx].name_; - } - init = false; - - try { - result = tags[tag].c_str(); - if ( nMaxLength > sizeof(buffer) -2 ) - nMaxLength = sizeof(buffer) -2; - strncpy(buffer,result,nMaxLength); - result = buffer; - } catch ( ... ) {} - - return result ; - } - - static bool isStringType(uint16_t type) - { - return type == Exiv2::asciiString - || type == Exiv2::unsignedByte - || type == Exiv2::signedByte - || type == Exiv2::undefined - ; - } - static bool isShortType(uint16_t type) { - return type == Exiv2::unsignedShort - || type == Exiv2::signedShort - ; - } - static bool isLongType(uint16_t type) { - return type == Exiv2::unsignedLong - || type == Exiv2::signedLong - ; - } - static bool isRationalType(uint16_t type) { - return type == Exiv2::unsignedRational - || type == Exiv2::signedRational - ; - } - static bool is2ByteType(uint16_t type) - { - return isShortType(type); - } - static bool is4ByteType(uint16_t type) - { - return isLongType(type) - || isRationalType(type) - ; - } - static bool isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option) - { - return type == 700 && option == kpsXMP; - } - static bool isPrintICC(uint16_t type, Exiv2::PrintStructureOption option) - { - return type == 0x8773 && option == kpsIccProfile; - } - -#define MIN(a,b) ((a)<(b))?(b):(a) - void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option,int depth) { if (io_->open() != 0) throw Error(9, io_->path(), strError()); @@ -493,178 +362,6 @@ namespace Exiv2 { printTiffStructure(io(),out,option,depth-1); } - void TiffImage::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,uint32_t start,bool bSwap,char c,int depth) - { - depth++; - bool bFirst = true ; - - // buffer - const size_t dirSize = 32; - DataBuf dir(dirSize); - bool bPrint = option == kpsBasic || option == kpsRecursive; - - do { - // Read top of directory - io.seek(start,BasicIo::beg); - io.read(dir.pData_, 2); - uint16_t dirLength = byteSwap2(dir,0,bSwap); - - bool tooBig = dirLength > 500; - - if ( bFirst && bPrint ) { - out << Internal::indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io.path() << std::endl; - if ( tooBig ) out << Internal::indent(depth) << "dirLength = " << dirLength << std::endl; - } - if (tooBig) break; - - // Read the dictionary - for ( int i = 0 ; i < dirLength ; i ++ ) { - if ( bFirst && bPrint ) { - out << Internal::indent(depth) - << " address | tag | " - << " type | count | offset | value\n"; - } - bFirst = false; - - 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 - uint32_t kount = isPrintXMP(tag,option) ? count // haul in all the data - : isPrintICC(tag,option) ? count // ditto - : isStringType(type) ? (count > 32 ? 32 : count) // restrict long arrays - : count > 5 ? 5 - : count - ; - uint32_t pad = isStringType(type) ? 1 : 0; - uint32_t size = isStringType(type) ? 1 - : is2ByteType(type) ? 2 - : is4ByteType(type) ? 4 - : 1 - ; - - // if ( offset > io.size() ) offset = 0; // Denial of service? - DataBuf buf(MIN(size*kount + pad,48)); // allocate a buffer - std::memcpy(buf.pData_,dir.pData_+8,4); // copy dir[8:11] into buffer (short strings) - if ( count*size > 4 ) { // 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 - } - - uint32_t Offset = isLongType(type) ? byteSwap4(buf,0,bSwap) : 0 ; - - if ( bPrint ) { - uint32_t address = start + 2 + i*12 ; - out << Internal::indent(depth) - << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%10u | " - ,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 ( option == kpsRecursive && (tag == 0x8769 /* ExifTag */ || tag == 0x014a/*SubIFDs*/ ) ) { - size_t restore = io.tell(); - printIFDStructure(io,out,option,Offset,bSwap,c,depth); - io.seek(restore,BasicIo::beg); - } else if ( option == kpsRecursive && tag == 0x83bb /* IPTCNAA */ ) { - size_t restore = io.tell(); // save - io.seek(offset,BasicIo::beg); // position - byte* bytes=new byte[count] ; // allocate memory - io.read(bytes,count) ; // read - io.seek(restore,BasicIo::beg); // restore - IptcData::printStructure(out,bytes,count,depth); - delete[] bytes; // free - } else if ( option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) { - uint32_t jump= 10 ; - byte bytes[20] ; - const char* chars = (const char*) &bytes[0] ; - size_t restore = io.tell(); // save - io.seek(offset,BasicIo::beg); // position - io.read(bytes,jump ) ; // read - bytes[jump]=0 ; - if ( ::strcmp("Nikon",chars) == 0 ) { - // tag is an embedded tiff - byte* bytes=new byte[count-jump] ; // allocate memory - io.read(bytes,count-jump) ; // read - MemIo memIo(bytes,count-jump) ; // create a file - printTiffStructure(memIo,out,option,depth); - delete[] bytes ; // free - } - io.seek(restore,BasicIo::beg); // restore - } - - } - - if ( isPrintXMP(tag,option) ) { - buf.pData_[count]=0; - out << (char*) buf.pData_; - } - if ( isPrintICC(tag,option) ) { - out.write((const char*)buf.pData_,count); - } - } - io.read(dir.pData_, 4); - start = tooBig ? 0 : byteSwap4(dir,0,bSwap); - out.flush(); - } while (start) ; - - if ( bPrint ) { - out << Internal::indent(depth) << "END " << io.path() << std::endl; - } - depth--; - } - - void TiffImage::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,int depth,size_t offset /*=0*/) - { - if ( option == kpsBasic || option == kpsXMP || option == kpsRecursive || option == kpsIccProfile ) { - // 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); - char c = (char) dir.pData_[0] ; - bool bSwap = ( c == 'M' && isLittleEndian() ) - || ( c == 'I' && isBigEndian() ) - ; - - uint32_t start = byteSwap4(dir,4,bSwap)+(uint32_t)offset; - printIFDStructure(io,out,option,start,bSwap,c,depth); - } - } - } // namespace Exiv2 // Shortcuts for the newTiffBinaryArray templates. diff --git a/src/webpimage.cpp b/src/webpimage.cpp index 2e1f65e6..86cc94ba 100644 --- a/src/webpimage.cpp +++ b/src/webpimage.cpp @@ -19,11 +19,8 @@ * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. */ /* - File: webpimage.cpp - Version: $Rev: 3845 $ - Author(s): Ben Touchette - History: 10-Aug-16 - Credits: See header file + File: webpimage.cpp + Version: $Rev: 4633 $ */ /* Google's WEBP container spec can be found at the link below: @@ -461,7 +458,7 @@ namespace Exiv2 { if ( equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_EXIF) && option==kpsRecursive ) { // create memio object with the payload, then print the structure BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(payload.pData_,payload.size_)); - TiffImage::printTiffStructure(*p,out,option,depth); + printTiffStructure(*p,out,option,depth); } bool bPrintPayload = (equalsWebPTag(chunkId, WEBP_CHUNK_HEADER_XMP) && option==kpsXMP)