r1108 Recursively dump an image (exiv2 -pR file...)

v0.27.3
Robin Mills 10 years ago
parent b7607c34db
commit fd5e983746

@ -113,7 +113,7 @@ namespace Exiv2 {
not valid (does not look like data of the specific image type).
@caution This function is not thread safe and intended for exiv2 -pS for debugging.
*/
virtual void printStructure(std::ostream& out, PrintStructureOption option =kpsNone);
virtual void printStructure(std::ostream& out, PrintStructureOption option =kpsNone, int depth=0);
/*!
@brief Read all metadata supported by a specific image format from the
image. Before this method is called, the image metadata will be

@ -159,7 +159,7 @@ namespace Exiv2 {
not valid (does not look like data of the specific image type).
@caution This function is not thread safe and intended for exiv2 -pS for debugging.
*/
void printStructure(std::ostream& out, PrintStructureOption option);
void printStructure(std::ostream& out, PrintStructureOption option,int depth);
//@}
protected:

@ -93,7 +93,7 @@ namespace Exiv2
not valid (does not look like data of the specific image type).
@caution This function is not thread safe and intended for exiv2 -pS for debugging.
*/
void printStructure(std::ostream& out, PrintStructureOption option);
void printStructure(std::ostream& out, PrintStructureOption option,int depth);
//@}
//! @name Accessors

@ -91,9 +91,23 @@ namespace Exiv2 {
@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 and intended for exiv2 -pS for debugging.
@caution This function is not thread safe and intended for exiv2 -p{S|R} as a file debugging aid
*/
void printStructure(std::ostream& out, PrintStructureOption option);
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);
/*!
@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,size_t start,bool bSwap,char c,int depth);
/*!
@brief Not supported. TIFF format does not contain a comment.

@ -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 "Dec 29, 2015"
.TH EXIV2 1 "Jan 5, 2016"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@ -175,9 +175,9 @@ fmt Default format is %Y%m%d_%H%M%S.
lvl d | i | i | w | e
debug, info, warning, error
mod s | a | t | v | h | i | x | c | p | i | S | X :
mod s | a | t | v | h | i | x | c | p | i | C | R | S | X :
summary, add, translated, vanilla, hex ...
iptc ,xmp, comment, preview, Structure,XMP raw
iptc ,xmp, comment, preview, ICC, Recursive, Structure, raw XMP
tgt a | c | e | i | t | x
all, comment, exif, iptc, thumb, xmp
@ -315,9 +315,13 @@ c : JPEG comment
.br
p : list available image previews, sorted by preview image size in pixels
.br
S : print image structure information (jpg, png and tiff only)
C : print image ICC Profile (jpg only)
.br
X : print "raw" XMP (jpg, png and tiff only)
R : print image structure recursively (jpg, tiff only)
.br
S : print image structure information (jpg, png, tiff only)
.br
X : print "raw" XMP (jpg, png, tiff only)
.TP
.B \-P \fIflgs\fP
Print flags for fine control of the tag list ('print' action). Allows

@ -169,7 +169,7 @@ namespace Exiv2 {
{
}
void Image::printStructure(std::ostream&, PrintStructureOption)
void Image::printStructure(std::ostream&, PrintStructureOption,int /*depth*/)
{
throw Error(13, io_->path());
}

@ -35,6 +35,7 @@ EXIV2_RCSID("@(#) $Id$")
#include "config.h"
#include "jpgimage.hpp"
#include "tiffimage.hpp"
#include "image_int.hpp"
#include "error.hpp"
#include "futils.hpp"
@ -516,9 +517,9 @@ namespace Exiv2 {
return true ;
}
#define REPORT_MARKER if ( option == kpsBasic ) out << Internal::stringFormat("%8ld | %#02x %-5s",io_->tell(), marker,nm[marker].c_str())
#define REPORT_MARKER if ( (option == kpsBasic||option == kpsRecursive) ) out << Internal::stringFormat("%8ld | %#02x %-5s",io_->tell(), marker,nm[marker].c_str())
void JpegBase::printStructure(std::ostream& out, PrintStructureOption option)
void JpegBase::printStructure(std::ostream& out, PrintStructureOption option,int depth)
{
if (io_->open() != 0) throw Error(9, io_->path(), strError());
// Ensure that this is the correct image type
@ -571,6 +572,7 @@ namespace Exiv2 {
REPORT_MARKER;
}
first = false;
bool bLF = option == kpsBasic||option == kpsRecursive;
// Read size and signature
std::memset(buf.pData_, 0x0, buf.size_);
@ -590,7 +592,7 @@ namespace Exiv2 {
){
size = getUShort(buf.pData_, bigEndian);
}
if ( option == kpsBasic ) out << Internal::stringFormat(" | %7d ", size);
if ( option == kpsBasic||option==kpsRecursive ) out << Internal::stringFormat(" | %7d ", size);
// only print the signature for appn
if (marker >= app0_ && marker <= (app0_ | 0x0F)) {
@ -637,15 +639,43 @@ namespace Exiv2 {
bufRead = size;
delete [] icc;
}
} else if ( option == kpsBasic ) {
out << "| " << Internal::binaryToString(buf,32,size>0?2:0);
} else if ( option == kpsBasic||option==kpsRecursive ) {
out << "| " << Internal::binaryToString(buf,size>32?32:size,size>0?2:0);
}
// for MPF: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html
if( (option == kpsRecursive && marker == (app0_+1) && std::strcmp(http,"Exif")==0 )
|| (option == kpsRecursive && marker == (app0_+2) && std::strcmp(http,"MPF" )==0 )
) {
// extract Exif data block which is tiff formatted
if ( size > 0 ) {
out << std::endl;
// allocate storage and current file position
byte* exif = new byte[size];
size_t restore = io_->tell();
// copy the data to memory
io_->seek(-bufRead , BasicIo::cur);
io_->read(exif,size);
std::size_t start = std::strcmp(http,"Exif")==0 ? 8 : 6;
// 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));
TiffImage::printTiffStructure(*p,out,option,depth);
// restore and clean up
io_->seek(restore,Exiv2::BasicIo::beg);
delete [] exif;
bLF = false;
}
}
}
// Skip the segment if the size is known
if (io_->seek(size - bufRead, BasicIo::cur)) throw Error(14);
if ( option == kpsBasic ) out << std::endl;
if ( bLF ) out << std::endl;
if (marker == sos_)
// sos_ is immediately followed by entropy-coded data & eoi_

@ -48,6 +48,8 @@ EXIV2_RCSID("@(#) $Id$")
#include <iostream>
#include <cassert>
#include <zlib.h> // To uncompress IccProfiles
// Signature from front of PNG file
const unsigned char pngSignature[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
@ -92,7 +94,47 @@ namespace Exiv2 {
return "image/png";
}
void PngImage::printStructure(std::ostream& out, PrintStructureOption option)
static void zlibUncompress(const byte* compressedText,
unsigned int compressedTextSize,
DataBuf& arr)
{
uLongf uncompressedLen = compressedTextSize * 2; // just a starting point
int zlibResult;
int dos = 0;
do {
arr.alloc(uncompressedLen);
zlibResult = uncompress((Bytef*)arr.pData_,
&uncompressedLen,
compressedText,
compressedTextSize);
if (zlibResult == Z_OK) {
assert((uLongf)arr.size_ >= uncompressedLen);
arr.size_ = uncompressedLen;
}
else if (zlibResult == Z_BUF_ERROR) {
// the uncompressedArray needs to be larger
uncompressedLen *= 2;
// DoS protection. can't be bigger than 64k
if (uncompressedLen > 131072) {
if (++dos > 1) break;
uncompressedLen = 131072;
}
}
else {
// something bad happened
throw Error(14);
}
}
while (zlibResult == Z_BUF_ERROR);
if (zlibResult != Z_OK) {
throw Error(14);
}
}
void PngImage::printStructure(std::ostream& out, PrintStructureOption option, int /*depth*/)
{
if (io_->open() != 0) {
throw Error(9, io_->path(), strError());
@ -104,7 +146,7 @@ namespace Exiv2 {
throw Error(3, "PNG");
}
if ( option == kpsIccProfile || option == kpsRecursive ) {
if ( option == kpsRecursive || option == kpsIccProfile ) { // disable kpsIccProfile because decompress isn't working!
throw Error(13, io_->path());
}
@ -112,7 +154,7 @@ namespace Exiv2 {
chType[0]=0;
chType[4]=0;
if ( option == kpsBasic || option == kpsXMP ) {
if ( option == kpsBasic || option == kpsXMP || option == kpsIccProfile ) {
if ( option == kpsBasic ) {
out << "STRUCTURE OF PNG FILE: " << io_->path() << std::endl;
@ -154,25 +196,39 @@ namespace Exiv2 {
}
if ( option == kpsBasic ) out << Internal::stringFormat("%8d | %5d | %10s |%8d | ",(uint32_t)address, index++,chType,dOff) << dataString << std::endl;
// for XMP, back up and read the whole block
// for XMP and ICC, back up and read the whole block
const char* key = "XML:com.adobe.xmp" ;
size_t start = ::strlen(key);
if ( option == kpsXMP && dataString.find(key)==0 ) {
if( (option == kpsXMP && dataString.find(key)==0)
|| (option == kpsIccProfile && std::strcmp(chType,"iCCP")==0)
){
#if defined(_MSC_VER)
io_->seek(-static_cast<int64_t>(blen) , BasicIo::cur);
#else
io_->seek(-static_cast<long>(blen) , BasicIo::cur);
#endif
dataOffset = dOff ;
if ( option == kpsXMP ) {
byte* xmp = new byte[dataOffset+5];
io_->read(xmp,dataOffset+4);
xmp[dataOffset]=0;
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; // we've read the data, so don't seek past the block
} else if ( option == kpsIccProfile ) {
byte* icc = new byte[dataOffset];
io_->read(icc,dataOffset);
DataBuf decompressed;
size_t name_l = std::strlen((const char*)icc)+1; // length of profile name
zlibUncompress(icc+name_l,dataOffset-name_l,decompressed);
out.write((const char*)decompressed.pData_,decompressed.size_);
delete [] icc;
dataOffset = 0;
}
}
if ( dataOffset ) io_->seek(dataOffset + 4 , BasicIo::cur);
if (io_->error()) throw Error(14);

@ -85,6 +85,7 @@ namespace Exiv2 {
{ exifId, "Exif", "Photo", exifTagList },
{ gpsId, "GPSInfo", "GPSInfo", gpsTagList },
{ iopId, "Iop", "Iop", iopTagList },
{ mpfId, "MPF", "MPF", mpfTagList },
{ subImage1Id, "SubImage1", "SubImage1", ifdTagList },
{ subImage2Id, "SubImage2", "SubImage2", ifdTagList },
{ subImage3Id, "SubImage3", "SubImage3", ifdTagList },
@ -2118,11 +2119,81 @@ namespace Exiv2 {
iopId, iopTags, asciiString, -1, printValue)
};
// MPF Tags http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html
static const TagInfo mpfTagInfo[] = {
TagInfo(0xb000, "MPFVersion", N_("MPFVersion"),
N_("MPF Version"),
mpfId, iopTags, asciiString, 0, printValue),
TagInfo(0xb001, "MPFNumberOfImages", N_("MPFNumberOfImages"),
N_("MPF Number of Images"),
mpfId, iopTags, undefined, -1, printExifVersion),
TagInfo(0xb002, "MPFImageList", N_("MPFImageList"),
N_("MPF Image List"),
mpfId, iopTags, asciiString, 0, printValue),
TagInfo(0xb003, "MPFImageUIDList", N_("MPFImageUIDList "),
N_("MPF Image UID List"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb004, "MPFTotalFrames", N_("MPFTotalFrames"),
N_("MPF Total Frames"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb101, "MPFIndividualNum", N_("MPFIndividualNum"),
N_("MPF Individual Num"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb201, "MPFPanOrientation", N_("MPFPanOrientation"),
N_("MPFPanOrientation"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb202, "MPFPanOverlapH", N_("MPFPanOverlapH"),
N_("MPF Pan Overlap Horizonal"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb203, "MPFPanOverlapV", N_("MPFPanOverlapV"),
N_("MPF Pan Overlap Vertical"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb204, "MPFBaseViewpointNum", N_("MPFBaseViewpointNum"),
N_("MPF Base Viewpoint Number"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb205, "MPFConvergenceAngle", N_("MPFConvergenceAngle"),
N_("MPF Convergence Angle"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb206, "MPFBaselineLength", N_("MPFBaselineLength"),
N_("MPF Baseline Length"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb207, "MPFVerticalDivergence", N_("MPFVerticalDivergence"),
N_("MPF Vertical Divergence"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb208, "MPFAxisDistanceX", N_("MPFAxisDistanceX"),
N_("MPF Axis Distance X"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb209, "MPFAxisDistanceY", N_("MPFAxisDistanceY"),
N_("MPF Axis Distance Y"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb20a, "MPFAxisDistanceZ", N_("MPFAxisDistanceZ"),
N_("MPF Axis Distance Z"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb20b, "MPFYawAngle", N_("MPFYawAngle"),
N_("MPF Yaw Angle"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb20c, "MPFPitchAngle", N_("MPFPitchAngle"),
N_("MPF Pitch Angle"),
mpfId, iopTags, unsignedLong, 1, printValue),
TagInfo(0xb20d, "MPFRollAngle", N_("MPFRollAngle"),
N_("MPF Roll Angle"),
mpfId, iopTags, unsignedLong, 1, printValue),
// End of list marker
TagInfo(0xffff, "(UnknownIopTag)", N_("Unknown MPF tag"),
N_("Unknown MPF tag"),
mpfId, iopTags, asciiString, -1, printValue)
};
const TagInfo* iopTagList()
{
return iopTagInfo;
}
const TagInfo* mpfTagList()
{
return mpfTagInfo;
}
// Synthesized Exiv2 Makernote info Tags (read-only)
static const TagInfo mnTagInfo[] = {
TagInfo(0x0001, "Offset", N_("Offset"),

@ -62,6 +62,7 @@ namespace Exiv2 {
exifId,
gpsId,
iopId,
mpfId,
subImage1Id,
subImage2Id,
subImage3Id,
@ -304,6 +305,8 @@ namespace Exiv2 {
const TagInfo* gpsTagList();
//! Return read-only list of built-in Exiv2 Makernote info tags
const TagInfo* mnTagList();
//! Return read-only list of built-in mfp Tags http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/MPF.html
const TagInfo* mpfTagList();
//! Return the group id for a group name
IfdId groupId(const std::string& groupName);

@ -393,6 +393,7 @@ namespace Exiv2 {
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_;
}
init = false;
@ -446,7 +447,14 @@ namespace Exiv2 {
#define MIN(a,b) ((a)<(b))?(b):(a)
void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option)
static std::string indent(int depth)
{
std::string result;
while ( depth -- ) result += " ";
return result;
}
void TiffImage::printStructure(std::ostream& out, Exiv2::PrintStructureOption option,int depth)
{
if (io_->open() != 0) throw Error(9, io_->path(), strError());
// Ensure that this is the correct image type
@ -455,41 +463,36 @@ namespace Exiv2 {
throw Error(15);
}
if ( option == kpsIccProfile || option == kpsRecursive ) {
if ( option == kpsIccProfile ) {
throw Error(13, io_->path());
}
if ( option == kpsBasic || option == kpsXMP ) {
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] ;
bool bSwap = ( c == 'M' && isLittleEndian() )
|| ( c == 'I' && isBigEndian() )
;
printTiffStructure(io(),out,option,depth-1);
}
if ( option == kpsBasic ) {
out << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io_->path() << std::endl;
out << " address | tag | type | count | offset | value\n";
void TiffImage::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,size_t start,bool bSwap,char c,int depth)
{
depth++;
if ( option == kpsBasic || option == kpsRecursive ) {
out << indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ",c,c) << io.path() << std::endl;
out << indent(depth) << " address | tag | type | count | offset | value\n";
}
uint32_t start = byteSwap4(dir,4,bSwap);
// buffer
const size_t dirSize = 32;
DataBuf dir(dirSize);
while ( start ) {
// if ( option == kpsBasic ) out << Internal::stringFormat("bSwap, start = %d %u\n",bSwap,offset);
// Read top of directory
io_->seek(start,BasicIo::beg);
io_->read(dir.pData_, 2);
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);
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);
@ -509,20 +512,22 @@ namespace Exiv2 {
: is4ByteType(type) ? 4
: 1
;
uint32_t Offset = 0 ; // used by ExifTag == 0x8769 && MakerNote == 0x927c to locate an FID
// if ( offset > io.size() ) offset = 0;
DataBuf buf(MIN(size*kount + pad,48)); // 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
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 || option == kpsRecursive ) {
uint32_t address = start + 2 + i*12 ;
out << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%9u | ",address,tag,tagName(tag,25),typeName(type),count,offset);
out << indent(depth) << Internal::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++ ) {
@ -532,6 +537,7 @@ namespace Exiv2 {
} else if ( isLongType(type) ){
for ( uint16_t k = 0 ; k < kount ; k++ ) {
out << sp << byteSwap4(buf,k*size,bSwap);
if ( k == 0 ) Offset = byteSwap4(buf,k*size,bSwap) ;
sp = " ";
}
} else if ( isRationalType(type) ){
@ -552,6 +558,13 @@ namespace Exiv2 {
}
sp = kount == count ? "" : " ...";
out << sp << std::endl;
if ( option == kpsRecursive
&& (tag == 0x8769 /* ExifTag */ || tag == 0x927c /* MakerNote */)
){
size_t restore = io.tell();
printIFDStructure(io,out,option,Offset,bSwap,c,depth);
io.seek(restore,BasicIo::beg);
}
}
if ( isPrintXMP(tag,option) ) {
@ -559,9 +572,29 @@ namespace Exiv2 {
out << (char*) buf.pData_;
}
}
io_->read(dir.pData_, 4);
io.read(dir.pData_, 4);
start = byteSwap4(dir,0,bSwap);
} // while offset
out.flush();
} // while start
depth--;
}
void TiffImage::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option,int depth)
{
if ( option == kpsBasic || option == kpsXMP || option == kpsRecursive ) {
// 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);
printIFDStructure(io,out,option,start,bSwap,c,depth);
}
}

Loading…
Cancel
Save