#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;
//! 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:

@ -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);

@ -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<std::string> 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;
}
}
}

@ -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<int64_t>(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<xml stuff
out << xmp+start; // output the xml
delete [] xmp;
dataOffset = 0;
}

@ -323,138 +323,113 @@ namespace Exiv2 {
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);
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);
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);
}
// 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)
{
//! 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<int,std::string> tags;
// build a static map of tags for fast search
static std::map<int,std::string> 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
}
}

Loading…
Cancel
Save