#922 -pS and -pX support for TIFF. Added formatters to Image class and use them from {jpg/png/tiff}image.cpp

v0.27.3
Robin Mills 10 years ago
parent 23de41d333
commit 40ffba6033

@ -412,6 +412,16 @@ namespace Exiv2 {
bool writeXmpFromPacket() const; bool writeXmpFromPacket() const;
//! Return list of native previews. This is meant to be used only by the PreviewManager. //! Return list of native previews. This is meant to be used only by the PreviewManager.
const NativePreviewList& nativePreviews() const; 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: protected:

@ -351,6 +351,47 @@ namespace Exiv2 {
return ImageFactory::checkMode(imageType_, metadataId); 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) AccessMode ImageFactory::checkMode(int type, MetadataId metadataId)
{ {
const Registry* r = find(registry, type); const Registry* r = find(registry, type);

@ -515,7 +515,7 @@ namespace Exiv2 {
return true ; 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) void JpegBase::printStructure(std::ostream& out,printStructureOption_e option)
{ {
@ -527,9 +527,8 @@ namespace Exiv2 {
} }
if ( option == kpsBasic || option == kpsXMP ) { if ( option == kpsBasic || option == kpsXMP ) {
char sbuff[80];
// nemonic for markers // nmonic for markers
std::string nm[256] ; std::string nm[256] ;
nm[0xd8]="SOI" ; nm[0xd8]="SOI" ;
nm[0xd9]="EOI" ; nm[0xd9]="EOI" ;
@ -554,7 +553,6 @@ namespace Exiv2 {
// Container for the signature // Container for the signature
bool bExtXMP = false; bool bExtXMP = false;
long bufRead = 0; long bufRead = 0;
long startSig = 0;
const long bufMinSize = 36; const long bufMinSize = 36;
DataBuf buf(bufMinSize); DataBuf buf(bufMinSize);
@ -568,7 +566,7 @@ namespace Exiv2 {
// print marker bytes // print marker bytes
if ( first && option == kpsBasic ) { if ( first && option == kpsBasic ) {
out << "STRUCTURE OF JPEG FILE: " << io_->path() << std::endl; out << "STRUCTURE OF JPEG FILE: " << io_->path() << std::endl;
out << " address | marker | length | signature" << std::endl ; out << " address | marker | length | data" << std::endl ;
REPORT_MARKER; REPORT_MARKER;
} }
first = false; first = false;
@ -579,7 +577,6 @@ namespace Exiv2 {
if (io_->error()) throw Error(14); if (io_->error()) throw Error(14);
if (bufRead < 2) throw Error(15); if (bufRead < 2) throw Error(15);
uint16_t size = 0; uint16_t size = 0;
sbuff[0]=0;
// not all markers have size field. // not all markers have size field.
if( ( marker >= sof0_ && marker <= sof15_) if( ( marker >= sof0_ && marker <= sof15_)
@ -591,16 +588,15 @@ namespace Exiv2 {
|| marker == sos_ || marker == sos_
){ ){
size = getUShort(buf.pData_, bigEndian); 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 // only print the signature for appn
if (marker >= app0_ && marker <= (app0_ | 0x0F)) { if (marker >= app0_ && marker <= (app0_ | 0x0F)) {
char http[5]; char http[5];
http[4]=0; http[4]=0;
memcpy(http,buf.pData_+2,4); 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/ // http://ns.adobe.com/xap/1.0/
if ( size > 0 ) { if ( size > 0 ) {
io_->seek(-bufRead , BasicIo::cur); io_->seek(-bufRead , BasicIo::cur);
@ -623,9 +619,11 @@ namespace Exiv2 {
} }
xmp[size]=0; xmp[size]=0;
#if 1
out << xmp + start; // this is all we need to output without the blank line dance.
#else
// #922: Remove blank lines. // #922: Remove blank lines.
// cut the xmp into (non 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<std::string> lines ; std::vector<std::string> lines ;
std::string s((char*)xmp+start); std::string s((char*)xmp+start);
char nl = '\n'; char nl = '\n';
@ -645,21 +643,12 @@ namespace Exiv2 {
} }
} }
for ( size_t l = 0 ; l < lines.size() ; l++ ) out << lines[l]; for ( size_t l = 0 ; l < lines.size() ; l++ ) out << lines[l];
#endif
delete [] xmp; delete [] xmp;
bufRead = size; bufRead = size;
} }
} else if ( option == kpsBasic ) { } else if ( option == kpsBasic ) {
startSig = size>0?2:0; out << "| " << binaryToString(buf,32,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;
}
} }
} }

@ -109,13 +109,18 @@ namespace Exiv2 {
if ( option == kpsBasic || option == kpsXMP ) { 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; long index = 0;
const long imgSize = io_->size(); const long imgSize = io_->size();
DataBuf cheaderBuf(8); DataBuf cheaderBuf(8);
while( !io_->eof() && ::strcmp(chType,"IEND") ) { while( !io_->eof() && ::strcmp(chType,"IEND") ) {
size_t address = io_->tell();
std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_); std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_);
long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_); long bufRead = io_->read(cheaderBuf.pData_, cheaderBuf.size_);
if (io_->error()) throw Error(14); if (io_->error()) throw Error(14);
@ -132,31 +137,24 @@ namespace Exiv2 {
chType[i-4]=cheaderBuf.pData_[i]; chType[i-4]=cheaderBuf.pData_[i];
} }
byte buff[32]; size_t blen = 32 ;
size_t blen = sizeof(buff)-1;
buff [blen] = 0;
buff [ 0] = 0;
size_t dOff = dataOffset; size_t dOff = dataOffset;
std::string dataString ;
if ( dataOffset > blen ) { if ( dataOffset > blen ) {
io_->read(buff,blen); DataBuf buff(blen+1);
io_->read(buff.pData_,blen);
dataOffset -= blen ; dataOffset -= blen ;
for ( size_t i = 0 ; i < blen ; i++ ) { dataString = binaryToString(buff,blen);
int c = buff[i] ;
buff[i] = (' '<=c && c<128) ? c : '.' ;
}
} }
char sbuff[80]; if ( option == kpsBasic ) out << stringFormat("%8llu | %5ld | %10s |%8lu | ",address, index++,chType,dOff) << dataString << std::endl;
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 // for XMP, back up and read the whole block
const char* key = "XML:com.adobe.xmp" ; const char* key = "XML:com.adobe.xmp" ;
int start = ::strlen(key); 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) #if defined(_MSC_VER)
io_->seek(-static_cast<int64_t>(blen) , BasicIo::cur); io_->seek(-static_cast<int64_t>(blen) , BasicIo::cur);
#else #else
@ -166,8 +164,8 @@ namespace Exiv2 {
byte* xmp = new byte[dataOffset+5]; byte* xmp = new byte[dataOffset+5];
io_->read(xmp,dataOffset+4); io_->read(xmp,dataOffset+4);
xmp[dataOffset]=0; xmp[dataOffset]=0;
while ( xmp[start] == 0 ) start++; while ( xmp[start] == 0 ) start++; // crawl over the '\0' bytes between XML:....\0\0<xml stuff
out << xmp+start << std::endl; out << xmp+start; // output the xml
delete [] xmp; delete [] xmp;
dataOffset = 0; dataOffset = 0;
} }

@ -341,77 +341,23 @@ namespace Exiv2 {
return byteSwap(v,bSwap); return byteSwap(v,bSwap);
} }
// http://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf
static std::string stringFormat(const std::string fmt_str, ...) {
int n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
char* formatted;
std::string str;
va_list ap;
bool ok = true;
do {
formatted = new char[n];
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
int final = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
ok = final < 0 || final >= 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) static const char* typeName(uint16_t tag)
{ {
//! List of TIFF image tags //! List of TIFF image tags
const char* result = NULL; const char* result = NULL;
switch (tag ) { switch (tag ) {
case 1 : result = "BYTE" ; break; case Exiv2::unsignedByte : result = "BYTE" ; break;
case 2 : result = "ASCII" ; break; case Exiv2::asciiString : result = "ASCII" ; break;
case 3 : result = "SHORT" ; break; case Exiv2::unsignedShort : result = "SHORT" ; break;
case 4 : result = "LONG" ; break; case Exiv2::unsignedLong : result = "LONG" ; break;
case 5 : result = "RATIONAL" ; break; case Exiv2::unsignedRational : result = "RATIONAL" ; break;
case 6 : result = "SBYTE" ; break; case Exiv2::signedByte : result = "SBYTE" ; break;
case 7 : result = "UNDEFINED" ; break; case Exiv2::undefined : result = "UNDEFINED" ; break;
case 8 : result = "SSHORT" ; break; case Exiv2::signedShort : result = "SSHORT" ; break;
case 9 : result = "SLONG" ; break; case Exiv2::signedLong : result = "SLONG" ; break;
case 10 : result = "SRATIONAL" ; break; case Exiv2::signedRational : result = "SRATIONAL" ; break;
case 11 : result = "FLOAT" ; break; case Exiv2::tiffFloat : result = "FLOAT" ; break;
case 12 : result = "DOUBLE" ; break; case Exiv2::tiffDouble : result = "DOUBLE" ; break;
default : result = "unknown" ; break; default : result = "unknown" ; break;
} }
return result; return result;
@ -439,8 +385,8 @@ namespace Exiv2 {
try { try {
result = tags[tag].c_str(); result = tags[tag].c_str();
if ( nMaxLength > sizeof(buffer) ) if ( nMaxLength > sizeof(buffer) -2 )
nMaxLength = sizeof(buffer) - 2; nMaxLength = sizeof(buffer) -2;
strncpy(buffer,result,nMaxLength); strncpy(buffer,result,nMaxLength);
result = buffer; result = buffer;
} catch ( ... ) {} } catch ( ... ) {}
@ -455,6 +401,35 @@ namespace Exiv2 {
|| type == Exiv2::signedByte || 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) void TiffImage::printStructure(std::ostream& out,Exiv2::printStructureOption_e option)
{ {
@ -468,69 +443,97 @@ namespace Exiv2 {
if ( option == kpsBasic || option == kpsXMP ) { if ( option == kpsBasic || option == kpsXMP ) {
io_->seek(0,BasicIo::beg); io_->seek(0,BasicIo::beg);
// buffer // buffer
const size_t bufSize = 32; const size_t dirSize = 32;
DataBuf buf(bufSize); DataBuf dir(dirSize);
// read header (we already know for certain that we have a Tiff file) // read header (we already know for certain that we have a Tiff file)
io_->read(buf.pData_, 8); io_->read(dir.pData_, 8);
char c = (char) dir.pData_[0] ;
#if __LITTLE_ENDIAN__ #if __LITTLE_ENDIAN__
bool bSwap = buf.pData_[0] == 'M'; bool bSwap = c == 'M';
#else #else
bool bSwap = buf.pData_[0] == 'I'; bool bSwap = c == 'I';
#endif #endif
if ( option == kpsBasic ) { if ( option == kpsBasic ) {
// out << string_format("count = %u\n",count); out << stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io_->path() << std::endl;
out << "STRUCTURE OF TIFF FILE: " << io_->path() << std::endl;
out << " address | tag | type | count | offset | value\n"; out << " address | tag | type | count | offset | value\n";
} }
uint32_t offset = byteSwap4(buf,4,bSwap); uint32_t start = byteSwap4(dir,4,bSwap);
while ( offset ) { while ( start ) {
// if ( option == kpsBasic ) out << stringFormat("bSwap, offset = %d %u\n",bSwap,offset); // if ( option == kpsBasic ) out << stringFormat("bSwap, start = %d %u\n",bSwap,offset);
// Read top of dictionary // Read top of directory
io_->seek(offset,BasicIo::beg); io_->seek(start,BasicIo::beg);
io_->read(buf.pData_, 2); io_->read(dir.pData_, 2);
uint16_t count = byteSwap2(buf,0,bSwap); uint16_t dirLength = byteSwap2(dir,0,bSwap);
// Read the dictionary // Read the dictionary
for ( int i = 0 ; i < count ; i ++ ) { for ( int i = 0 ; i < dirLength ; i ++ ) {
io_->read(buf.pData_, 12); io_->read(dir.pData_, 12);
uint16_t Tag = byteSwap2(buf,0,bSwap); uint16_t tag = byteSwap2(dir,0,bSwap);
uint16_t Type = byteSwap2(buf,2,bSwap); uint16_t type = byteSwap2(dir,2,bSwap);
uint32_t Count = byteSwap4(buf,4,bSwap); uint32_t count = byteSwap4(dir,4,bSwap);
uint32_t Offset = byteSwap4(buf,8,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 ) { if ( option == kpsBasic ) {
uint32_t address = offset + 2 + i*12 ; uint32_t address = start + 2 + i*12 ;
out << stringFormat("%8u | %#06x %-20s |%10s |%9u |%9u | ",address,Tag,tagName(Tag,20),typeName(Type),Count,Offset); out << stringFormat("%8u | %#06x %-25s |%10s |%9u |%9u | ",address,tag,tagName(tag,25),typeName(type),count,offset);
if ( isStringType(Type) ) {
size_t restore = io_->tell(); if ( isShortType(type) ){
io_->seek(Offset,BasicIo::beg); for ( uint16_t k = 0 ; k < kount ; k++ ) {
size_t size = Count > bufSize ? bufSize : Count; out << sp << byteSwap2(buf,k*size,bSwap);
io_->read(buf.pData_,size ); sp = " ";
io_->seek(restore,BasicIo::beg); }
out << stringValue(buf.pData_,Count); } else if ( isLongType(type) ){
} else if ( Count == 1 && Type == Exiv2::unsignedShort ) { for ( uint16_t k = 0 ; k < kount ; k++ ) {
out << byteSwap2(buf,8,bSwap); out << sp << byteSwap4(buf,k*size,bSwap);
} else if ( Count == 1 && Type == Exiv2::unsignedLong ) { sp = " ";
out << byteSwap2(buf,8,bSwap); }
} } else if ( isRationalType(type) ){
for ( uint16_t k = 0 ; k < kount ; k++ ) {
out << std::endl; out << sp << byteSwap2(buf,k*size+(bSwap?2:0),bSwap) << "/" << byteSwap2(buf,k*size+(bSwap?0:2),bSwap);
} sp = " ";
if ( Tag == 700 ) { }
const long bSize = (long) Count; } else if ( isStringType(type) ) {
DataBuf buf(bSize+1); out << sp << binaryToString(buf,kount);
size_t restore = io_->tell(); }
io_->seek(Offset,BasicIo::beg); sp = kount == count ? "" : " ...";
io_->read(buf.pData_,bSize); out << sp << std::endl;
io_->seek(restore,BasicIo::beg); }
buf.pData_[bSize]=0;
if ( option == kpsXMP ) out << (char*) &buf.pData_[0]; if ( isPrintXMP(tag,option) ) {
} buf.pData_[count]=0;
} out << (char*) buf.pData_;
io_->read(buf.pData_, 4); }
offset = byteSwap4(buf,0,bSwap); }
io_->read(dir.pData_, 4);
start = byteSwap4(dir,0,bSwap);
} // while offset } // while offset
} }
} }

Loading…
Cancel
Save