diff --git a/include/exiv2/image.hpp b/include/exiv2/image.hpp index 31f41d61..9f96d3cc 100644 --- a/include/exiv2/image.hpp +++ b/include/exiv2/image.hpp @@ -412,6 +412,16 @@ namespace Exiv2 { bool writeXmpFromPacket() const; //! Return list of native previews. This is meant to be used only by the PreviewManager. const NativePreviewList& nativePreviews() const; + /*! + @brief format a string in the pattern of \em sprintf \em . + */ + std::string stringFormat(const std::string fmt, ...) const; + + /*! + @brief format binary for display in \em printStructure() \em . + */ + std::string binaryToString(DataBuf& buf,size_t size,size_t start=0) const; + //@} protected: diff --git a/src/image.cpp b/src/image.cpp index bead82b9..620f1908 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -351,6 +351,47 @@ namespace Exiv2 { return ImageFactory::checkMode(imageType_, metadataId); } + std::string Image::stringFormat(const std::string fmt, ...) const + { + std::string result; + + int need = fmt.size()*4; // initial guess + char* buffer = new char[need]; // allocate a buffer + va_list ap; // variable arg list + + va_start(ap, fmt); + need=vsnprintf(buffer, need, fmt.c_str(), ap); + va_end(ap); + + if (need < 0) { // make buffer bigger + delete[] buffer; + buffer = new char[need+2]; + va_start(ap, fmt); + need=vsnprintf(buffer, need, fmt.c_str(), ap); + va_end(ap); + } + + if ( need > 0 ) result = std::string(buffer) ; + + delete[] buffer; // free buffer + return result; + } + + std::string Image::binaryToString(DataBuf& buf,size_t size,size_t start /* = 0 */) const + { + std::string result = ""; + byte* buff = buf.pData_; + + size += start; + + while (start < size) { + int c = (int) buff[start++] ; + if (c < ' ' || c > 127) c = '.' ; + result += (char) c ; + } + return result; + } + AccessMode ImageFactory::checkMode(int type, MetadataId metadataId) { const Registry* r = find(registry, type); diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index 4fe3741a..7c7fb807 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -509,13 +509,13 @@ namespace Exiv2 { bool isBlank(std::string& s) { - for ( std::size_t i = 0 ; i < s.length() ; i++ ) - if ( s[i] != ' ' ) - return false ; - return true ; + for ( std::size_t i = 0 ; i < s.length() ; i++ ) + if ( s[i] != ' ' ) + return false ; + return true ; } -#define REPORT_MARKER if ( option == kpsBasic ) { sprintf(sbuff,"%8ld | %#02x %-5s",io_->tell(), marker,nm[marker].c_str()); out << sbuff; } +#define REPORT_MARKER if ( option == kpsBasic ) out << stringFormat("%8ld | %#02x %-5s",io_->tell(), marker,nm[marker].c_str()) void JpegBase::printStructure(std::ostream& out,printStructureOption_e option) { @@ -527,9 +527,8 @@ namespace Exiv2 { } if ( option == kpsBasic || option == kpsXMP ) { - char sbuff[80]; - // nemonic for markers + // nmonic for markers std::string nm[256] ; nm[0xd8]="SOI" ; nm[0xd9]="EOI" ; @@ -554,9 +553,8 @@ namespace Exiv2 { // Container for the signature bool bExtXMP = false; long bufRead = 0; - long startSig = 0; const long bufMinSize = 36; - DataBuf buf(bufMinSize); + DataBuf buf(bufMinSize); // Read section marker int marker = advanceToMarker(); @@ -566,12 +564,12 @@ namespace Exiv2 { bool first= true; while (!done) { // print marker bytes - if ( first && option == kpsBasic ) { + if ( first && option == kpsBasic ) { out << "STRUCTURE OF JPEG FILE: " << io_->path() << std::endl; - out << " address | marker | length | signature" << std::endl ; - REPORT_MARKER; - } - first = false; + out << " address | marker | length | data" << std::endl ; + REPORT_MARKER; + } + first = false; // Read size and signature std::memset(buf.pData_, 0x0, buf.size_); @@ -579,7 +577,6 @@ namespace Exiv2 { if (io_->error()) throw Error(14); if (bufRead < 2) throw Error(15); uint16_t size = 0; - sbuff[0]=0; // not all markers have size field. if( ( marker >= sof0_ && marker <= sof15_) @@ -591,16 +588,15 @@ namespace Exiv2 { || marker == sos_ ){ size = getUShort(buf.pData_, bigEndian); - sprintf(sbuff," | %7d ", size); } - if ( option == kpsBasic ) out << sbuff ; + if ( option == kpsBasic ) out << stringFormat(" | %7d ", size); // only print the signature for appn if (marker >= app0_ && marker <= (app0_ | 0x0F)) { char http[5]; http[4]=0; memcpy(http,buf.pData_+2,4); - if ( option == kpsXMP && strncmp(http,"http",4) == 0 ) { + if ( option == kpsXMP && std::strcmp(http,"http") == 0 ) { // http://ns.adobe.com/xap/1.0/ if ( size > 0 ) { io_->seek(-bufRead , BasicIo::cur); @@ -613,53 +609,46 @@ namespace Exiv2 { // the first extended block is a copy of the Standard block. // a robust implementation enables extended blocks to be out of sequence if ( ! bExtXMP ) { - while (xmp[start]) start++; start++; - if ( ::strstr((char*)xmp+start,"HasExtendedXMP") ) { - start = size ; // ignore this packet, we'll get on the next time around - bExtXMP = true; - } + while (xmp[start]) start++; start++; + if ( ::strstr((char*)xmp+start,"HasExtendedXMP") ) { + start = size ; // ignore this packet, we'll get on the next time around + bExtXMP = true; + } } else { - start = 2+35+32+4+4; // Adobe Spec, p19 + start = 2+35+32+4+4; // Adobe Spec, p19 } xmp[size]=0; - // #922: Remove blank lines. +#if 1 + out << xmp + start; // this is all we need to output without the blank line dance. +#else + // #922: Remove blank lines. // cut the xmp into (non blank) lines - // out << xmp + start; // this is all we need to output without the blank line dance. std::vector lines ; std::string s((char*)xmp+start); char nl = '\n'; while( s.length() ) - { - std::size_t end = s.find(nl); - if ( end != std::string::npos ) - { - std::string line = s.substr(0,end); - if ( !isBlank(line) ) - lines.push_back(line+nl); - s = s.substr(end+1,std::string::npos); - } else { - lines.push_back(s); - s=""; - } - } - for ( size_t l = 0 ; l < lines.size() ; l++ ) out << lines[l]; - + { + std::size_t end = s.find(nl); + if ( end != std::string::npos ) + { + std::string line = s.substr(0,end); + if ( !isBlank(line) ) + lines.push_back(line+nl); + s = s.substr(end+1,std::string::npos); + } else { + lines.push_back(s); + s=""; + } + } + for ( size_t l = 0 ; l < lines.size() ; l++ ) out << lines[l]; +#endif delete [] xmp; bufRead = size; } } else if ( option == kpsBasic ) { - startSig = size>0?2:0; - int endSig = size?size:bufRead; - if (endSig > 32) endSig = 32 ; - out << "| "; - while (startSig++ < endSig ) { - byte c = buf.pData_[startSig-1] ; - c = (' '<=c && c<128) ? c : '.' ; - out << (char) c ; - // else endSig = startSig; - } + out << "| " << binaryToString(buf,32,size>0?2:0); } } @@ -670,14 +659,14 @@ namespace Exiv2 { if (marker == sos_) // sos_ is immediately followed by entropy-coded data & eoi_ - done = true; + done = true; else { - // Read the beginning of the next segment - marker = advanceToMarker(); - REPORT_MARKER; + // Read the beginning of the next segment + marker = advanceToMarker(); + REPORT_MARKER; if ( marker == eoi_ ) { if ( option == kpsBasic ) out << std::endl; - done = true; + done = true; } } } diff --git a/src/pngimage.cpp b/src/pngimage.cpp index a1c424d7..95431d05 100644 --- a/src/pngimage.cpp +++ b/src/pngimage.cpp @@ -109,13 +109,18 @@ namespace Exiv2 { if ( option == kpsBasic || option == kpsXMP ) { - if ( option == kpsBasic ) out << "index | chunk_type | length | data" << std::endl; + if ( option == kpsBasic ) { + out << "STRUCTURE OF PNG FILE: " << io_->path() << std::endl; + out << " address | index | chunk_type | length | data" << std::endl; + } long index = 0; const long imgSize = io_->size(); DataBuf cheaderBuf(8); while( !io_->eof() && ::strcmp(chType,"IEND") ) { + size_t address = io_->tell(); + std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_); long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); if (io_->error()) throw Error(14); @@ -132,31 +137,24 @@ namespace Exiv2 { chType[i-4]=cheaderBuf.pData_[i]; } - byte buff[32]; - size_t blen = sizeof(buff)-1; - buff [blen] = 0; - buff [ 0] = 0; - - size_t dOff = dataOffset; + size_t blen = 32 ; + size_t dOff = dataOffset; + std::string dataString ; if ( dataOffset > blen ) { - io_->read(buff,blen); + DataBuf buff(blen+1); + io_->read(buff.pData_,blen); dataOffset -= blen ; - for ( size_t i = 0 ; i < blen ; i++ ) { - int c = buff[i] ; - buff[i] = (' '<=c && c<128) ? c : '.' ; - } + dataString = binaryToString(buff,blen); } - char sbuff[80]; - sprintf(sbuff,"%5ld %12s %8lu %s\n", index++,chType,dOff,buff); - if ( option == kpsBasic ) out << sbuff; + if ( option == kpsBasic ) out << stringFormat("%8llu | %5ld | %10s |%8lu | ",address, index++,chType,dOff) << dataString << std::endl; // for XMP, back up and read the whole block const char* key = "XML:com.adobe.xmp" ; int start = ::strlen(key); - buff[start] = 0; - if ( option == kpsXMP && ::strcmp((const char*)buff,key) == 0 ) { + + if ( option == kpsXMP && dataString.find(key)==0 ) { #if defined(_MSC_VER) io_->seek(-static_cast(blen) , BasicIo::cur); #else @@ -166,8 +164,8 @@ namespace Exiv2 { byte* xmp = new byte[dataOffset+5]; io_->read(xmp,dataOffset+4); xmp[dataOffset]=0; - while ( xmp[start] == 0 ) start++; - out << xmp+start << std::endl; + while ( xmp[start] == 0 ) start++; // crawl over the '\0' bytes between XML:....\0\0= n; - if (ok) { - n += abs(final - n + 1); - } - else { - str = std::string(formatted); - } - delete[] formatted; - } while (ok); - return str; - } - - std::string stringValue(byte* buff,size_t size) - { - std::string result = ""; - size_t start = 0 ; - bool bLong = size > 32; - if ( bLong ) size = 32 ; - - while (start < size ) { - int c = (int) buff[start++] ; - if (c < ' ' || c > 127) c = '.' ; - result += (char) c ; - } - return result + (bLong ? " ..." : "") ; - } - - std::string rationalValue(byte* buff,size_t size) - { - std::string result = ""; - size_t start = 0 ; - if (size > 32) size = 32 ; - - while (start < size ) { - int c = (int) buff[start++] ; - if (c < ' ' || c > 127) c = '.' ; - result += (char) c ; - } - return result; - } - static const char* typeName(uint16_t tag) { //! List of TIFF image tags - const char* result = NULL; - switch (tag ) { - case 1 : result = "BYTE" ; break; - case 2 : result = "ASCII" ; break; - case 3 : result = "SHORT" ; break; - case 4 : result = "LONG" ; break; - case 5 : result = "RATIONAL" ; break; - case 6 : result = "SBYTE" ; break; - case 7 : result = "UNDEFINED" ; break; - case 8 : result = "SSHORT" ; break; - case 9 : result = "SLONG" ; break; - case 10 : result = "SRATIONAL" ; break; - case 11 : result = "FLOAT" ; break; - case 12 : result = "DOUBLE" ; break; - default : result = "unknown" ; break; + 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; + return result; } static const char* tagName(uint16_t tag,size_t nMaxLength) { - const char* result = NULL; + const char* result = NULL; - // build a static map of tags for fast search - static std::map tags; + // 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; + 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:: 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_; } init = false; try { - result = tags[tag].c_str(); - if ( nMaxLength > sizeof(buffer) ) - nMaxLength = sizeof(buffer) - 2; - strncpy(buffer,result,nMaxLength); - result = buffer; + 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 - ; - } + static bool isStringType(uint16_t type) + { + return type == Exiv2::asciiString + || type == Exiv2::unsignedByte + || type == Exiv2::signedByte + ; + } + 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_e option) + { + return type == 700 && option == kpsXMP; + } void TiffImage::printStructure(std::ostream& out,Exiv2::printStructureOption_e option) { @@ -466,72 +441,100 @@ namespace Exiv2 { } if ( option == kpsBasic || option == kpsXMP ) { - io_->seek(0,BasicIo::beg); - // buffer - const size_t bufSize = 32; - DataBuf buf(bufSize); - - // read header (we already know for certain that we have a Tiff file) - io_->read(buf.pData_, 8); + io_->seek(0,BasicIo::beg); + // 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] ; #if __LITTLE_ENDIAN__ - bool bSwap = buf.pData_[0] == 'M'; + bool bSwap = c == 'M'; #else - bool bSwap = buf.pData_[0] == 'I'; + bool bSwap = c == 'I'; #endif - if ( option == kpsBasic ) { - // out << string_format("count = %u\n",count); - out << "STRUCTURE OF TIFF FILE: " << io_->path() << std::endl; - out << " address | tag | type | count | offset | value\n"; - } - - uint32_t offset = byteSwap4(buf,4,bSwap); - while ( offset ) { - // if ( option == kpsBasic ) out << stringFormat("bSwap, offset = %d %u\n",bSwap,offset); - - // Read top of dictionary - io_->seek(offset,BasicIo::beg); - io_->read(buf.pData_, 2); - uint16_t count = byteSwap2(buf,0,bSwap); - - // Read the dictionary - for ( int i = 0 ; i < count ; i ++ ) { - io_->read(buf.pData_, 12); - uint16_t Tag = byteSwap2(buf,0,bSwap); - uint16_t Type = byteSwap2(buf,2,bSwap); - uint32_t Count = byteSwap4(buf,4,bSwap); - uint32_t Offset = byteSwap4(buf,8,bSwap); - if ( option == kpsBasic ) { - uint32_t address = offset + 2 + i*12 ; - out << stringFormat("%8u | %#06x %-20s |%10s |%9u |%9u | ",address,Tag,tagName(Tag,20),typeName(Type),Count,Offset); - if ( isStringType(Type) ) { - size_t restore = io_->tell(); - io_->seek(Offset,BasicIo::beg); - size_t size = Count > bufSize ? bufSize : Count; - io_->read(buf.pData_,size ); - io_->seek(restore,BasicIo::beg); - out << stringValue(buf.pData_,Count); - } else if ( Count == 1 && Type == Exiv2::unsignedShort ) { - out << byteSwap2(buf,8,bSwap); - } else if ( Count == 1 && Type == Exiv2::unsignedLong ) { - out << byteSwap2(buf,8,bSwap); - } - - out << std::endl; - } - if ( Tag == 700 ) { - const long bSize = (long) Count; - DataBuf buf(bSize+1); - size_t restore = io_->tell(); - io_->seek(Offset,BasicIo::beg); - io_->read(buf.pData_,bSize); - io_->seek(restore,BasicIo::beg); - buf.pData_[bSize]=0; - if ( option == kpsXMP ) out << (char*) &buf.pData_[0]; - } - } - io_->read(buf.pData_, 4); - offset = byteSwap4(buf,0,bSwap); - } // while offset + if ( option == kpsBasic ) { + out << 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 << 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 + ; + size_t pad = isStringType(type) ? 1 : 0; + size_t size = isStringType(type) ? 1 + : is2ByteType(type) ? 2 + : is4ByteType(type) ? 4 + : 1 + ; + + DataBuf buf(size*kount + pad); // 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 << 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++ ) { + out << sp << byteSwap2(buf,k*size+(bSwap?2:0),bSwap) << "/" << byteSwap2(buf,k*size+(bSwap?0:2),bSwap); + sp = " "; + } + } else if ( isStringType(type) ) { + out << sp << 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 } }