From 6c113871efacdb4760a0cac1c7cafa8677f53ed1 Mon Sep 17 00:00:00 2001 From: Robin Mills Date: Fri, 27 Mar 2015 18:47:52 +0000 Subject: [PATCH] #922. Added options -pS and -pX to exiv2(.exe). Still to deal with -dI --- include/exiv2/actions.hpp | 2 + include/exiv2/image.hpp | 11 ++- include/exiv2/jpgimage.hpp | 2 +- include/exiv2/pngimage.hpp | 2 +- src/actions.cpp | 23 ++++- src/exiv2.1 | 6 +- src/exiv2.cpp | 8 +- src/exiv2app.hpp | 4 +- src/image.cpp | 8 +- src/jpgimage.cpp | 186 +++++++++++++++++++++---------------- src/pngimage.cpp | 89 +++++++++++++----- 11 files changed, 221 insertions(+), 120 deletions(-) diff --git a/include/exiv2/actions.hpp b/include/exiv2/actions.hpp index a4ba1dfd..804e86a6 100644 --- a/include/exiv2/actions.hpp +++ b/include/exiv2/actions.hpp @@ -178,6 +178,8 @@ namespace Action { void printMetadatum(const Exiv2::Metadatum& md, const Exiv2::Image* image); //! Print the label for a summary line void printLabel(const std::string& label) const; + //! Print image Structure information + int printStructure(std::ostream& out,Exiv2::printStructureOption_e option); /*! @brief Print one summary line with a label (if provided) and requested data. A line break is printed only if a label is provided. diff --git a/include/exiv2/image.hpp b/include/exiv2/image.hpp index c824b6e1..96596947 100644 --- a/include/exiv2/image.hpp +++ b/include/exiv2/image.hpp @@ -71,6 +71,11 @@ namespace Exiv2 { //! List of native previews. This is meant to be used only by the PreviewManager. typedef std::vector NativePreviewList; + /*! + @brief options for printStructure + */ + typedef enum { kpsNone, kpsBasic, kpsXMP } printStructureOption_e ; + /*! @brief Abstract base class defining the interface for an image. This is the top-level interface to the Exiv2 library. @@ -107,7 +112,7 @@ namespace Exiv2 { @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). */ - virtual void printStructure(); + virtual void printStructure(std::ostream& out,printStructureOption_e option=kpsNone); /*! @brief Read all metadata supported by a specific image format from the image. Before this method is called, the image metadata will be @@ -450,7 +455,7 @@ namespace Exiv2 { class EXIV2API ImageFactory { friend bool Image::good() const; public: - /*! + /*! @brief Create the appropriate class type implemented BasicIo based on the protocol of the input. "-" path implies the data from stdin and it is handled by StdinIo. @@ -466,7 +471,7 @@ namespace Exiv2 { */ static BasicIo::AutoPtr createIo(const std::string& path, bool useCurl = true); #ifdef EXV_UNICODE_PATH - /*! + /*! @brief Like createIo() but accepts a unicode path in an std::wstring. @note This function is only available on Windows. */ diff --git a/include/exiv2/jpgimage.hpp b/include/exiv2/jpgimage.hpp index 5be3c384..c0a61593 100644 --- a/include/exiv2/jpgimage.hpp +++ b/include/exiv2/jpgimage.hpp @@ -150,7 +150,7 @@ namespace Exiv2 { public: //! @name Manipulators //@{ - void printStructure(); + void printStructure(std::ostream& out,printStructureOption_e option); void readMetadata(); void writeMetadata(); //@} diff --git a/include/exiv2/pngimage.hpp b/include/exiv2/pngimage.hpp index 6bfd037a..fc0339bd 100644 --- a/include/exiv2/pngimage.hpp +++ b/include/exiv2/pngimage.hpp @@ -84,7 +84,7 @@ namespace Exiv2 //! @name Manipulators //@{ - void printStructure(); + void printStructure(std::ostream& out,printStructureOption_e option); void readMetadata(); void writeMetadata(); //@} diff --git a/src/actions.cpp b/src/actions.cpp index 95ed65ec..129ef5d6 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -237,10 +237,12 @@ namespace Action { path_ = path; int rc = 0; switch (Params::instance().printMode_) { - case Params::pmSummary: rc = printSummary(); break; - case Params::pmList: rc = printList(); break; - case Params::pmComment: rc = printComment(); break; - case Params::pmPreview: rc = printPreviewList(); break; + case Params::pmSummary: rc = printSummary(); break; + case Params::pmList: rc = printList(); break; + case Params::pmComment: rc = printComment(); break; + case Params::pmPreview: rc = printPreviewList(); break; + case Params::pmStructure: rc = printStructure(std::cout,Exiv2::kpsBasic); break; + case Params::pmXMP: rc = printStructure(std::cout,Exiv2::kpsXMP); break; } return rc; } @@ -250,6 +252,19 @@ namespace Action { return 1; } // Print::run + int Print::printStructure(std::ostream& out,Exiv2::printStructureOption_e option) + { + if (!Exiv2::fileExists(path_, true)) { + std::cerr << path_ << ": " + << _("Failed to open the file\n"); + return -1; + } + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(path_); + assert(image.get() != 0); + image->printStructure(out,option); + return 0; + } + int Print::printSummary() { if (!Exiv2::fileExists(path_, true)) { diff --git a/src/exiv2.1 b/src/exiv2.1 index c4212709..993da896 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 "Mar 11, 2015" +.TH EXIV2 1 "Mar 27, 2015" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: @@ -231,6 +231,10 @@ x : XMP properties (\-PXkyct) c : JPEG comment .br p : list available image previews, sorted by preview image size in pixels +.br +S : print image structure information (jpg and png only) +.br +X : print "raw" XMP (jpg and png only) .TP .B \-P \fIflgs\fP Print flags for fine control of the tag list ('print' action). Allows diff --git a/src/exiv2.cpp b/src/exiv2.cpp index 6ac6662e..9fe5e183 100644 --- a/src/exiv2.cpp +++ b/src/exiv2.cpp @@ -296,6 +296,8 @@ void Params::help(std::ostream& os) const << _(" x : XMP properties (-PXkyct)\n") << _(" c : JPEG comment\n") << _(" p : list available previews\n") + << _(" S : print structure of image\n") + << _(" X : extract XMP from image\n") << _(" -P flgs Print flags for fine control of tag lists ('print' action):\n") << _(" E : include Exif tags in the list\n") << _(" I : IPTC datasets\n") @@ -555,8 +557,10 @@ int Params::evalPrint(const std::string& optarg) case 'h': rc = evalPrintFlags("Exgnycsh"); break; case 'i': rc = evalPrintFlags("Ikyct"); break; case 'x': rc = evalPrintFlags("Xkyct"); break; - case 'c': action_ = Action::print; printMode_ = pmComment; break; - case 'p': action_ = Action::print; printMode_ = pmPreview; break; + case 'c': action_ = Action::print; printMode_ = pmComment ; break; + case 'p': action_ = Action::print; printMode_ = pmPreview ; break; + case 'S': action_ = Action::print; printMode_ = pmStructure; break; + case 'X': action_ = Action::print; printMode_ = pmXMP ; break; default: std::cerr << progname() << ": " << _("Unrecognized print mode") << " `" << optarg << "'\n"; diff --git a/src/exiv2app.hpp b/src/exiv2app.hpp index 5cd3b1aa..6674624c 100644 --- a/src/exiv2app.hpp +++ b/src/exiv2app.hpp @@ -144,7 +144,9 @@ public: pmSummary, pmList, pmComment, - pmPreview + pmPreview, + pmStructure, + pmXMP }; //! Individual items to print, bitmap diff --git a/src/image.cpp b/src/image.cpp index d13b8ec5..47f75081 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -68,6 +68,8 @@ EXIV2_RCSID("@(#) $Id$") #include #include #include +#include + #include #include #ifdef _MSC_VER @@ -160,7 +162,7 @@ namespace Exiv2 { Image::~Image() { } - void Image::printStructure() + void Image::printStructure(std::ostream&, printStructureOption_e) { } @@ -435,13 +437,13 @@ namespace Exiv2 { return BasicIo::AutoPtr(new XPathIo(path)); // may throw return BasicIo::AutoPtr(new FileIo(path)); - + UNUSED(useCurl); } // ImageFactory::createIo #ifdef EXV_UNICODE_PATH BasicIo::AutoPtr ImageFactory::createIo(const std::wstring& wpath, bool useCurl) - { + { Protocol fProt = fileProtocol(wpath); #if EXV_USE_SSH == 1 if (fProt == pSsh || fProt == pSftp) { diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index f7b997c7..802c0a6e 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -507,7 +507,7 @@ namespace Exiv2 { } } // JpegBase::readMetadata - void JpegBase::printStructure() + void JpegBase::printStructure(std::ostream& out,printStructureOption_e option) { if (io_->open() != 0) throw Error(9, io_->path(), strError()); // Ensure that this is the correct image type @@ -515,90 +515,116 @@ namespace Exiv2 { if (io_->error() || io_->eof()) throw Error(14); throw Error(15); } - - // nemonic for markers - std::string nm[256] ; - nm[0xd8]="SOI" ; - nm[0xd9]="EOI" ; - nm[0xda]="SOS" ; - nm[0xdb]="DQT" ; - nm[0xdd]="DRI" ; - nm[0xfe]="COM" ; - - // 0xe0 .. 0xef are APPn - // 0xc0 .. 0xcf are SOFn (except 4) - nm[0xc4]="DHT" ; - for ( int i = 0 ; i <= 15 ; i++ ) { - char MN[10]; - sprintf(MN,"APP%d",i); - nm[0xe0+i] = MN; - if ( i != 4 ) { - sprintf(MN,"SOF%d",i); - nm[0xc0+i] = MN; + + if ( option == kpsBasic || option == kpsXMP ) { + char sbuff[80]; + + // nemonic for markers + std::string nm[256] ; + nm[0xd8]="SOI" ; + nm[0xd9]="EOI" ; + nm[0xda]="SOS" ; + nm[0xdb]="DQT" ; + nm[0xdd]="DRI" ; + nm[0xfe]="COM" ; + + // 0xe0 .. 0xef are APPn + // 0xc0 .. 0xcf are SOFn (except 4) + nm[0xc4]="DHT" ; + for ( int i = 0 ; i <= 15 ; i++ ) { + char MN[10]; + sprintf(MN,"APP%d",i); + nm[0xe0+i] = MN; + if ( i != 4 ) { + sprintf(MN,"SOF%d",i); + nm[0xc0+i] = MN; + } } - } - // Container for the signature - const long bufMinSize = 36; - long bufRead = 0, startSig = 0; - DataBuf buf(bufMinSize); + // Container for the signature + const long bufMinSize = 36; + long bufRead = 0, startSig = 0; + DataBuf buf(bufMinSize); + + // Read section marker + int marker = advanceToMarker(); + if (marker < 0) throw Error(15); + + if ( option == kpsBasic ) out << " offset | marker | length | signature" << std::endl ; + + while (1) { + // print marker bytes + sprintf(sbuff,"%8ld %#02x %-5s",io_->tell(), marker,nm[marker].c_str()); + if ( option == kpsBasic ) out << sbuff; + if ( marker == eoi_ ) break ; + + // Read size and signature + std::memset(buf.pData_, 0x0, buf.size_); + bufRead = io_->read(buf.pData_, bufMinSize); + 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_) + || ( marker >= app0_ && marker <= (app0_ | 0x0F)) + || marker == dht_ + || marker == dqt_ + || marker == dri_ + || marker == com_ + || marker == sos_ + ){ + size = getUShort(buf.pData_, bigEndian); + sprintf(sbuff,"%7d ", size); + } else { + sprintf(sbuff," "); + } + if ( option == kpsBasic ) out << sbuff ; + + // 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 ) { + // http://ns.adobe.com/xap/1.0/ + if ( size > 0 ) { + io_->seek(-bufRead , BasicIo::cur); + byte* xmp = new byte[size+1]; + io_->read(xmp,size); + int start = 0 ; + while (xmp[start]) start++; start++; + xmp[size]=0; + out << xmp + start << std::endl; + delete [] xmp; + bufRead = size; + } + } else if ( option == kpsBasic ) { + startSig = size>0?2:0; + int endSig = size?size:bufRead; + if (endSig > 32) endSig = 32 ; + while (startSig++ < endSig ) { + byte c = buf.pData_[startSig-1] ; + c = (' '<=c && c<128) ? c : '.' ; + out << (char) c ; + // else endSig = startSig; + } + } + } - // Read section marker - int marker = advanceToMarker(); - if (marker < 0) throw Error(15); - printf("STRUCTURE OF FILE:\n"); - printf(" offset | marker | size | signature\n"); - while (1) { - // print marker bytes - printf("%8ld %#02x %-5s",io_->tell(), marker,nm[marker].c_str()); - if ( marker == eoi_ ) break ; - - // Read size and signature - std::memset(buf.pData_, 0x0, buf.size_); - bufRead = io_->read(buf.pData_, bufMinSize); - if (io_->error()) throw Error(14); - if (bufRead < 2) throw Error(15); - uint16_t size = 0; - - // not all markers have size field. - if( ( marker >= sof0_ && marker <= sof15_) - || ( marker >= app0_ && marker <= (app0_ | 0x0F)) - || marker == dht_ - || marker == dqt_ - || marker == dri_ - || marker == com_ - || marker == sos_ - ){ - size = getUShort(buf.pData_, bigEndian); - printf("%7d ", size); - } else { - printf(" "); - } - - // only print the signature for appn - if (marker >= app0_ && marker <= (app0_ | 0x0F)) { - startSig = size>0?2:0; - int endSig = size?size:bufRead; - if (endSig > 32) endSig = 32 ; - while (startSig++ < endSig ) { - int c = buf.pData_[startSig-1] ; - printf("%c", (' '<=c && c<128) ? c : '.' ); - // else endSig = startSig; - } - } - - // Skip the segment if the size is known - if (io_->seek(size - bufRead, BasicIo::cur)) throw Error(14); - - printf("\n"); - // sos_ is immediately followed by entropy-coded data & eoi_ - if (marker == sos_) break; - - // Read the beginning of the next segment - marker = advanceToMarker(); + // Skip the segment if the size is known + if (io_->seek(size - bufRead, BasicIo::cur)) throw Error(14); + + if ( option == kpsBasic ) out << std::endl; + // sos_ is immediately followed by entropy-coded data & eoi_ + if (marker == sos_) break; + + // Read the beginning of the next segment + marker = advanceToMarker(); + } } - printf("-----------------\n"); } void JpegBase::writeMetadata() diff --git a/src/pngimage.cpp b/src/pngimage.cpp index ef57991f..5c05f86d 100644 --- a/src/pngimage.cpp +++ b/src/pngimage.cpp @@ -91,7 +91,7 @@ namespace Exiv2 { return "image/png"; } - void PngImage::printStructure() + void PngImage::printStructure(std::ostream& out,printStructureOption_e option) { if (io_->open() != 0) { throw Error(9, io_->path(), strError()); @@ -103,34 +103,75 @@ namespace Exiv2 { throw Error(3, "PNG"); } - printf("index | chunk_type | chunk_size\n"); - long index = 0, i = 0; - const long imgSize = io_->size(); - DataBuf cheaderBuf(8); + char chType[5]; + chType[0]=0; + chType[4]=0; - while(!io_->eof()) { - std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_); - long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); - if (io_->error()) throw Error(14); - if (bufRead != cheaderBuf.size_) throw Error(20); + if ( option == kpsBasic || option == kpsXMP ) { - // Decode chunk data length. - uint32_t dataOffset = Exiv2::getULong(cheaderBuf.pData_, Exiv2::bigEndian); - long pos = io_->tell(); - if ( pos == -1 - || dataOffset > uint32_t(0x7FFFFFFF) - || static_cast(dataOffset) > imgSize - pos) throw Exiv2::Error(14); + if ( option == kpsBasic ) out << "index | chunk_type | length | data" << std::endl; - printf("%5ld ", index); - for (i = 4; i < 8; i++) - printf("%c", cheaderBuf.pData_[i]); - printf(" %u\n", dataOffset); + long index = 0; + const long imgSize = io_->size(); + DataBuf cheaderBuf(8); - index++; - io_->seek(dataOffset + 4 , BasicIo::cur); - if (io_->error() || io_->eof()) throw Error(14); - } + while( !io_->eof() && ::strcmp(chType,"IEND") ) { + std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_); + long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); + if (io_->error()) throw Error(14); + if (bufRead != cheaderBuf.size_) throw Error(20); + + // Decode chunk data length. + uint32_t dataOffset = Exiv2::getULong(cheaderBuf.pData_, Exiv2::bigEndian); + long pos = io_->tell(); + if ( pos == -1 + || dataOffset > uint32_t(0x7FFFFFFF) + || static_cast(dataOffset) > imgSize - pos) throw Exiv2::Error(14); + + for (int i = 4; i < 8; i++) { + 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; + + if ( dataOffset > blen ) { + io_->read(buff,blen); + dataOffset -= blen ; + for ( size_t i = 0 ; i < blen ; i++ ) { + int c = buff[i] ; + buff[i] = (' '<=c && c<128) ? c : '.' ; + } + } + + char sbuff[80]; + sprintf(sbuff,"%5ld %12s %8lu %s\n", index++,chType,dOff,buff); + if ( option == kpsBasic ) out << sbuff; + + // 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 ) { + io_->seek(-blen , BasicIo::cur); + dataOffset = dOff ; + byte* xmp = new byte[dataOffset+5]; + io_->read(xmp,dataOffset+4); + xmp[dataOffset]=0; + while ( xmp[start] == 0 ) start++; + out << xmp+start << std::endl; + delete [] xmp; + dataOffset = 0; + } + if ( dataOffset ) io_->seek(dataOffset + 4 , BasicIo::cur); + if (io_->error()) throw Error(14); + } + } } void PngImage::readMetadata()