* Added options -pp and -ep to list and extract preview images

* #584: Implemented missing member function
* API change, class PreviewImage: Added members to access all preview properties
* Minor fix: Suppress XMP encoding error when XMP is not enabled.
v0.27.3
Andreas Huggel 17 years ago
parent 2f958d8a56
commit b5a00fcd5c

@ -51,6 +51,7 @@ EXIV2_RCSID("@(#) $Id$")
#include "canonmn.hpp" #include "canonmn.hpp"
#include "iptc.hpp" #include "iptc.hpp"
#include "xmp.hpp" #include "xmp.hpp"
#include "preview.hpp"
#include "futils.hpp" #include "futils.hpp"
#include "i18n.h" // NLS support. #include "i18n.h" // NLS support.
@ -228,6 +229,7 @@ namespace Action {
case Params::pmIptc: rc = printIptc(); break; case Params::pmIptc: rc = printIptc(); break;
case Params::pmXmp: rc = printXmp(); break; case Params::pmXmp: rc = printXmp(); break;
case Params::pmComment: rc = printComment(); break; case Params::pmComment: rc = printComment(); break;
case Params::pmPreview: rc = printPreviewList(); break;
} }
return rc; return rc;
} }
@ -770,7 +772,7 @@ namespace Action {
} }
Exiv2::IptcData::const_iterator end = iptcData.end(); Exiv2::IptcData::const_iterator end = iptcData.end();
Exiv2::IptcData::const_iterator md; Exiv2::IptcData::const_iterator md;
bool manyFiles = Params::instance().files_.size() > 1; bool const manyFiles = Params::instance().files_.size() > 1;
for (md = iptcData.begin(); md != end; ++md) { for (md = iptcData.begin(); md != end; ++md) {
std::cout << std::setfill(' ') << std::left; std::cout << std::setfill(' ') << std::left;
if (manyFiles) { if (manyFiles) {
@ -808,7 +810,7 @@ namespace Action {
} }
Exiv2::XmpData::const_iterator end = xmpData.end(); Exiv2::XmpData::const_iterator end = xmpData.end();
Exiv2::XmpData::const_iterator md; Exiv2::XmpData::const_iterator md;
bool manyFiles = Params::instance().files_.size() > 1; bool const manyFiles = Params::instance().files_.size() > 1;
for (md = xmpData.begin(); md != end; ++md) { for (md = xmpData.begin(); md != end; ++md) {
std::cout << std::setfill(' ') << std::left; std::cout << std::setfill(' ') << std::left;
if (manyFiles) { if (manyFiles) {
@ -845,6 +847,36 @@ namespace Action {
return 0; return 0;
} // Print::printComment } // Print::printComment
int Print::printPreviewList()
{
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->readMetadata();
bool const manyFiles = Params::instance().files_.size() > 1;
int cnt = 0;
Exiv2::PreviewManager pm(*image);
Exiv2::PreviewPropertiesList list = pm.getPreviewProperties();
for (Exiv2::PreviewPropertiesList::const_iterator pos = list.begin(); pos != list.end(); ++pos) {
if (manyFiles) {
std::cout << std::setfill(' ') << std::left << std::setw(20)
<< path_ << " ";
}
std::cout << _("Preview") << " " << ++cnt << ": "
<< pos->mimeType_ << ", ";
if (pos->width_ != 0 && pos->height_ != 0) {
std::cout << pos->width_ << "x" << pos->height_ << " "
<< _("pixels") << ", ";
}
std::cout << pos->size_ << " " << _("bytes") << "\n";
}
return 0;
} // Print::printPreviewList
Print::AutoPtr Print::clone() const Print::AutoPtr Print::clone() const
{ {
return AutoPtr(clone_()); return AutoPtr(clone_());
@ -1061,8 +1093,12 @@ namespace Action {
if (dontOverwrite(xmpPath)) return 0; if (dontOverwrite(xmpPath)) return 0;
rc = metacopy(path_, xmpPath, Exiv2::ImageType::xmp, false); rc = metacopy(path_, xmpPath, Exiv2::ImageType::xmp, false);
} }
if (Params::instance().target_ & Params::ctPreview) {
rc = writePreviews();
}
if ( !(Params::instance().target_ & Params::ctXmpSidecar) if ( !(Params::instance().target_ & Params::ctXmpSidecar)
&& !(Params::instance().target_ & Params::ctThumb)) { && !(Params::instance().target_ & Params::ctThumb)
&& !(Params::instance().target_ & Params::ctPreview)) {
std::string exvPath = newFilePath(path_, ".exv"); std::string exvPath = newFilePath(path_, ".exv");
if (dontOverwrite(exvPath)) return 0; if (dontOverwrite(exvPath)) return 0;
rc = metacopy(path_, exvPath, Exiv2::ImageType::exv, false); rc = metacopy(path_, exvPath, Exiv2::ImageType::exv, false);
@ -1118,6 +1154,61 @@ namespace Action {
return rc; return rc;
} // Extract::writeThumbnail } // Extract::writeThumbnail
int Extract::writePreviews() const
{
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->readMetadata();
Exiv2::PreviewManager pvMgr(*image);
Exiv2::PreviewPropertiesList pvList = pvMgr.getPreviewProperties();
const Params::PreviewNumbers& numbers = Params::instance().previewNumbers_;
for (Params::PreviewNumbers::const_iterator n = numbers.begin(); n != numbers.end(); ++n) {
if (*n == 0) {
// Write all previews
for (int num = 0; num < static_cast<int>(pvList.size()); ++num) {
writePreviewFile(pvMgr.getPreviewImage(pvList[num]), num + 1);
}
break;
}
if (*n > static_cast<int>(pvList.size())) {
std::cerr << path_ << ": " << _("Image does not have preview")
<< " " << *n << "\n";
continue;
}
writePreviewFile(pvMgr.getPreviewImage(pvList[*n - 1]), *n);
}
return 0;
} // Extract::writePreviews
void Extract::writePreviewFile(const Exiv2::PreviewImage& pvImg, int num) const
{
std::string pvFile = newFilePath(path_, "-preview") + Exiv2::toString(num);
std::string pvPath = pvFile + pvImg.extension();
if (dontOverwrite(pvPath)) return;
if (Params::instance().verbose_) {
std::cout << _("Writing preview") << " " << num << " ("
<< pvImg.mimeType() << ", ";
if (pvImg.width() != 0 && pvImg.height() != 0) {
std::cout << pvImg.width() << "x" << pvImg.height() << " "
<< _("pixels") << ", ";
}
std::cout << pvImg.size() << " " << _("bytes") << ") "
<< _("to file") << " " << pvPath << std::endl;
}
long rc = pvImg.writeFile(pvFile);
if (rc == 0) {
std::cerr << path_ << ": " << _("Image does not have preview")
<< " " << num << "\n";
}
} // Extract::writePreviewFile
Extract::AutoPtr Extract::clone() const Extract::AutoPtr Extract::clone() const
{ {
return AutoPtr(clone_()); return AutoPtr(clone_());

@ -48,6 +48,7 @@
namespace Exiv2 { namespace Exiv2 {
class ExifData; class ExifData;
class Image; class Image;
class PreviewImage;
} }
// ***************************************************************************** // *****************************************************************************
@ -162,6 +163,8 @@ namespace Action {
//! Print the Jpeg comment //! Print the Jpeg comment
int printComment(); int printComment();
//! Print list of available preview images
int printPreviewList();
//! Print uninterpreted Iptc information //! Print uninterpreted Iptc information
int printIptc(); int printIptc();
//! print uninterpreted XMP information //! print uninterpreted XMP information
@ -278,6 +281,17 @@ namespace Action {
on the format of the Exif thumbnail image. on the format of the Exif thumbnail image.
*/ */
int writeThumbnail() const; int writeThumbnail() const;
/*!
@brief Write preview images to files.
*/
int writePreviews() const;
/*!
@brief Write one preview image to a file. The filename is composed by
removing the suffix from the image filename and appending
"-preview<num>" and the appropriate suffix (".jpg" or ".tif"),
depending on the format of the Exif thumbnail image.
*/
void writePreviewFile(const Exiv2::PreviewImage& pvImg, int num) const;
private: private:
virtual Extract* clone_() const; virtual Extract* clone_() const;

@ -3,7 +3,7 @@
.\" First parameter, NAME, should be all caps .\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1) .\" other parameters are allowed: see man(7), man(1)
.TH EXIV2 1 "Sept 10th, 2008" .TH EXIV2 1 "Dec 7th, 2008"
.\" Please adjust this date whenever revising the manpage. .\" Please adjust this date whenever revising the manpage.
.\" .\"
.\" Some roff macros, for reference: .\" Some roff macros, for reference:
@ -152,6 +152,8 @@ i : IPTC data values
x : XMP properties x : XMP properties
.br .br
c : JPEG comment c : JPEG comment
.br
p : list available image previews, sorted by preview image size in pixels
.TP .TP
.B \-P \fIcols\fP .B \-P \fIcols\fP
Print columns for the Exif taglist ('print' action), allows detailed Print columns for the Exif taglist ('print' action), allows detailed
@ -209,7 +211,13 @@ be named \fIfile\fP\-thumb.jpg.
.TP .TP
.B \-e \fItgt\fP .B \-e \fItgt\fP
Extract target(s) for the 'extract' action. Possible targets are the same Extract target(s) for the 'extract' action. Possible targets are the same
as those for the \fB\-d\fP option, plus a modifier: as those for the \fB\-d\fP option, plus a target to extract preview
images and a modifier to generate an XMP sidecar file:
.br
p[<n>[,<m> ...]] : Extract preview images. The optional comma separated
list of preview image numbers is used to determine which preview images
to extract. The available preview images and their numbers are displayed
with the 'print' option -pp.
.br .br
X : Extract metadata to an XMP sidecar file <file>.xmp. The remaining X : Extract metadata to an XMP sidecar file <file>.xmp. The remaining
extract targets determine what metadata to extract to the sidecar extract targets determine what metadata to extract to the sidecar
@ -363,6 +371,10 @@ exiv2 \-it img1.jpg img2.jpg
Inserts (copies) metadata from img1.exv to img1.jpg and from img2.exv Inserts (copies) metadata from img1.exv to img1.jpg and from img2.exv
to img2.jpg. to img2.jpg.
.TP .TP
exiv2 \-ep1,2 image.jpg
Extracts previews 1 and 2 from the image to the files image\-preview1.jpg
and image\-preview2.jpg.
.TP
exiv2 \-eiX image.jpg exiv2 \-eiX image.jpg
Extracts IPTC datasets into an XMP sidecar file image.xmp and in the Extracts IPTC datasets into an XMP sidecar file image.xmp and in the
process converts them to "IPTC Core" XMP schema. process converts them to "IPTC Core" XMP schema.

@ -19,7 +19,7 @@
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
*/ */
/* /*
Abstract: Command line program to display and manipulate image %Exif data Abstract: Command line program to display and manipulate image metadata.
File: exiv2.cpp File: exiv2.cpp
Version: $Rev$ Version: $Rev$
@ -50,6 +50,7 @@ EXIV2_RCSID("@(#) $Id$")
#include <iomanip> #include <iomanip>
#include <cstring> #include <cstring>
#include <cassert> #include <cassert>
#include <cctype>
// ***************************************************************************** // *****************************************************************************
// local declarations // local declarations
@ -80,6 +81,17 @@ namespace {
int parseCommonTargets(const std::string& optarg, int parseCommonTargets(const std::string& optarg,
const std::string& action); const std::string& action);
/*!
@brief Parse numbers separated by commas into container
@param previewNumbers Container for the numbers
@param optarg Option arguments
@param j Starting index into optarg
@return Number of characters processed
*/
int parsePreviewNumbers(Params::PreviewNumbers& previewNumbers,
const std::string& optarg,
int j);
/*! /*!
@brief Parse metadata modification commands from multiple files @brief Parse metadata modification commands from multiple files
@param modifyCmds Reference to a structure to store the parsed commands @param modifyCmds Reference to a structure to store the parsed commands
@ -257,6 +269,7 @@ void Params::help(std::ostream& os) const
<< _(" i : IPTC data values\n") << _(" i : IPTC data values\n")
<< _(" x : XMP properties\n") << _(" x : XMP properties\n")
<< _(" c : JPEG comment\n") << _(" c : JPEG comment\n")
<< _(" p : list available previews\n")
<< _(" -P cols Print columns for the Exif taglist ('print' action). Valid are:\n") << _(" -P cols Print columns for the Exif taglist ('print' action). Valid are:\n")
<< _(" x : print a column with the tag value\n") << _(" x : print a column with the tag value\n")
<< _(" g : group name\n") << _(" g : group name\n")
@ -282,7 +295,9 @@ void Params::help(std::ostream& os) const
" Only JPEG thumbnails can be inserted, they need to be named\n" " Only JPEG thumbnails can be inserted, they need to be named\n"
" <file>-thumb.jpg\n") " <file>-thumb.jpg\n")
<< _(" -e tgt Extract target(s) for the 'extract' action. Possible targets\n" << _(" -e tgt Extract target(s) for the 'extract' action. Possible targets\n"
" are the same as those for the -d option, plus a modifier:\n" " are the same as those for the -d option, plus a target to extract\n"
" preview images and a modifier to generate an XMP sidecar file:\n"
" p[<n>[,<m> ...]] : Extract preview images.\n"
" X : Extract metadata to an XMP sidecar file <file>.xmp\n") " X : Extract metadata to an XMP sidecar file <file>.xmp\n")
<< _(" -r fmt Filename format for the 'rename' action. The format string\n" << _(" -r fmt Filename format for the 'rename' action. The format string\n"
" follows strftime(3). The following keywords are supported:\n") " follows strftime(3). The following keywords are supported:\n")
@ -457,6 +472,7 @@ int Params::evalPrint(const std::string& optarg)
case 'i': printMode_ = pmIptc; break; case 'i': printMode_ = pmIptc; break;
case 'x': printMode_ = pmXmp; break; case 'x': printMode_ = pmXmp; break;
case 'c': printMode_ = pmComment; break; case 'c': printMode_ = pmComment; break;
case 'p': printMode_ = pmPreview; break;
default: default:
std::cerr << progname() << ": " << _("Unrecognized print mode") << " `" std::cerr << progname() << ": " << _("Unrecognized print mode") << " `"
<< optarg << "'\n"; << optarg << "'\n";
@ -850,6 +866,15 @@ namespace {
target |= Params::ctXmpSidecar; target |= Params::ctXmpSidecar;
if (optarg == "X") target |= Params::ctExif | Params::ctIptc | Params::ctXmp; if (optarg == "X") target |= Params::ctExif | Params::ctIptc | Params::ctXmp;
break; break;
case 'p':
{
if (strcmp(action.c_str(), "extract") == 0) {
i += parsePreviewNumbers(Params::instance().previewNumbers_, optarg, i + 1);
target |= Params::ctPreview;
break;
}
// fallthrough
}
default: default:
std::cerr << Params::instance().progname() << ": " << _("Unrecognized ") std::cerr << Params::instance().progname() << ": " << _("Unrecognized ")
<< action << " " << _("target") << " `" << optarg[i] << "'\n"; << action << " " << _("target") << " `" << optarg[i] << "'\n";
@ -860,6 +885,46 @@ namespace {
return rc ? rc : target; return rc ? rc : target;
} // parseCommonTargets } // parseCommonTargets
int parsePreviewNumbers(Params::PreviewNumbers& previewNumbers,
const std::string& optarg,
int j)
{
size_t k = j;
for (size_t i = j; i < optarg.size(); ++i) {
std::ostringstream os;
for (k = i; k < optarg.size() && isdigit(optarg[k]); ++k) {
os << optarg[k];
}
if (k > i) {
bool ok = false;
int num = Exiv2::stringTo<int>(os.str(), ok);
if (ok && num >= 0) {
previewNumbers.insert(num);
}
else {
std::cerr << Params::instance().progname() << ": "
<< _("Invalid preview number") << ": " << num << "\n";
}
i = k;
}
if (!(k < optarg.size() && optarg[i] == ',')) break;
}
int ret = static_cast<int>(k - j);
if (ret == 0) {
previewNumbers.insert(0);
}
#ifdef DEBUG
std::cout << "\nThe set now contains: ";
for (Params::PreviewNumbers::const_iterator i = previewNumbers.begin();
i != previewNumbers.end();
++i) {
std::cout << *i << ", ";
}
std::cout << std::endl;
#endif
return k - j;
} // parsePreviewNumbers
bool parseCmdFiles(ModifyCmds& modifyCmds, bool parseCmdFiles(ModifyCmds& modifyCmds,
const Params::CmdFiles& cmdFiles) const Params::CmdFiles& cmdFiles)
{ {

@ -37,6 +37,7 @@
// + standard includes // + standard includes
#include <string> #include <string>
#include <vector> #include <vector>
#include <set>
#include <iostream> #include <iostream>
// ***************************************************************************** // *****************************************************************************
@ -72,7 +73,7 @@ struct CmdIdAndString {
@brief Implements the command line handling for the program. @brief Implements the command line handling for the program.
Derives from Util::Getopt to use the command line argument parsing Derives from Util::Getopt to use the command line argument parsing
functionalty provided there. This class is implemented as a Singleton, functionality provided there. This class is implemented as a singleton,
i.e., there is only one global instance of it, which can be accessed i.e., there is only one global instance of it, which can be accessed
from everywhere. from everywhere.
@ -113,6 +114,8 @@ public:
typedef std::vector<std::string> CmdLines; typedef std::vector<std::string> CmdLines;
//! Container to store filenames. //! Container to store filenames.
typedef std::vector<std::string> Files; typedef std::vector<std::string> Files;
//! Container for preview image numbers
typedef std::set<int> PreviewNumbers;
/*! /*!
@brief Controls all access to the global Params instance. @brief Controls all access to the global Params instance.
@ -123,7 +126,7 @@ public:
void cleanup(); void cleanup();
//! Enumerates print modes //! Enumerates print modes
enum PrintMode { pmSummary, pmList, pmIptc, pmXmp, pmComment }; enum PrintMode { pmSummary, pmList, pmIptc, pmXmp, pmComment, pmPreview };
//! Individual items to print //! Individual items to print
enum PrintItem { enum PrintItem {
@ -147,7 +150,8 @@ public:
ctComment = 4, ctComment = 4,
ctThumb = 8, ctThumb = 8,
ctXmp = 16, ctXmp = 16,
ctXmpSidecar = 32 ctXmpSidecar = 32,
ctPreview = 64
}; };
//! Enumerates the policies to handle existing files in rename action //! Enumerates the policies to handle existing files in rename action
@ -191,6 +195,7 @@ public:
std::string directory_; //!< Location for files to extract/insert std::string directory_; //!< Location for files to extract/insert
std::string suffix_; //!< File extension of the file to insert std::string suffix_; //!< File extension of the file to insert
Files files_; //!< List of non-option arguments. Files files_; //!< List of non-option arguments.
PreviewNumbers previewNumbers_; //!< List of preview numbers
private: private:
//! Pointer to the global Params object. //! Pointer to the global Params object.

@ -490,7 +490,7 @@ namespace Exiv2
if (writeXmpFromPacket() == false) if (writeXmpFromPacket() == false)
{ {
if (XmpParser::encode(xmpPacket_, xmpData_)) if (XmpParser::encode(xmpPacket_, xmpData_) > 1)
{ {
#ifndef SUPPRESS_WARNINGS #ifndef SUPPRESS_WARNINGS
std::cerr << "Error: Failed to encode XMP metadata.\n"; std::cerr << "Error: Failed to encode XMP metadata.\n";

@ -633,7 +633,7 @@ namespace Exiv2 {
} }
} }
if (writeXmpFromPacket() == false) { if (writeXmpFromPacket() == false) {
if (XmpParser::encode(xmpPacket_, xmpData_)) { if (XmpParser::encode(xmpPacket_, xmpData_) > 1) {
#ifndef SUPPRESS_WARNINGS #ifndef SUPPRESS_WARNINGS
std::cerr << "Error: Failed to encode XMP metadata.\n"; std::cerr << "Error: Failed to encode XMP metadata.\n";
#endif #endif

@ -309,7 +309,7 @@ namespace Exiv2 {
if (writeXmpFromPacket() == false) if (writeXmpFromPacket() == false)
{ {
if (XmpParser::encode(xmpPacket_, xmpData_)) if (XmpParser::encode(xmpPacket_, xmpData_) > 1)
{ {
#ifndef SUPPRESS_WARNINGS #ifndef SUPPRESS_WARNINGS
std::cerr << "Error: Failed to encode XMP metadata.\n"; std::cerr << "Error: Failed to encode XMP metadata.\n";

@ -695,6 +695,16 @@ namespace Exiv2 {
return DataBuf(pData_, size_); return DataBuf(pData_, size_);
} }
const byte* PreviewImage::pData() const
{
return pData_;
}
uint32_t PreviewImage::size() const
{
return size_;
}
std::string PreviewImage::mimeType() const std::string PreviewImage::mimeType() const
{ {
return properties_.mimeType_; return properties_.mimeType_;
@ -705,9 +715,19 @@ namespace Exiv2 {
return properties_.extension_; return properties_.extension_;
} }
uint32_t PreviewImage::size() const uint32_t PreviewImage::width() const
{ {
return size_; return properties_.width_;
}
uint32_t PreviewImage::height() const
{
return properties_.height_;
}
PreviewId PreviewImage::id() const
{
return properties_.id_;
} }
PreviewManager::PreviewManager(const Image& image) PreviewManager::PreviewManager(const Image& image)

@ -100,6 +100,10 @@ namespace Exiv2 {
@brief Return a pointer to the image data for read-only access. @brief Return a pointer to the image data for read-only access.
*/ */
const byte* pData() const; const byte* pData() const;
/*!
@brief Return the size of the preview image in bytes.
*/
uint32_t size() const;
/*! /*!
@brief Write the thumbnail image to a file. @brief Write the thumbnail image to a file.
@ -122,9 +126,17 @@ namespace Exiv2 {
*/ */
std::string extension() const; std::string extension() const;
/*! /*!
@brief Return the size of the preview image in bytes. @brief Return the width of the preview image in pixels.
*/ */
uint32_t size() const; uint32_t width() const;
/*!
@brief Return the height of the preview image in pixels.
*/
uint32_t height() const;
/*!
@brief Return the preview image type identifier.
*/
PreviewId id() const;
//@} //@}
private: private:

@ -477,7 +477,7 @@ namespace Exiv2 {
exifData_.erase(pos); exifData_.erase(pos);
} }
std::string xmpPacket; std::string xmpPacket;
if (XmpParser::encode(xmpPacket, xmpData_)) { if (XmpParser::encode(xmpPacket, xmpData_) > 1) {
#ifndef SUPPRESS_WARNINGS #ifndef SUPPRESS_WARNINGS
std::cerr << "Error: Failed to encode XMP metadata.\n"; std::cerr << "Error: Failed to encode XMP metadata.\n";
#endif #endif

@ -119,7 +119,7 @@ namespace Exiv2 {
copyExifToXmp(exifData_, xmpData_); copyExifToXmp(exifData_, xmpData_);
copyIptcToXmp(iptcData_, xmpData_); copyIptcToXmp(iptcData_, xmpData_);
if (XmpParser::encode(xmpPacket_, xmpData_, if (XmpParser::encode(xmpPacket_, xmpData_,
XmpParser::omitPacketWrapper|XmpParser::useCompactFormat)) { XmpParser::omitPacketWrapper|XmpParser::useCompactFormat) > 1) {
#ifndef SUPPRESS_WARNINGS #ifndef SUPPRESS_WARNINGS
std::cerr << "Error: Failed to encode XMP metadata.\n"; std::cerr << "Error: Failed to encode XMP metadata.\n";
#endif #endif

Loading…
Cancel
Save