* 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 "iptc.hpp"
#include "xmp.hpp"
#include "preview.hpp"
#include "futils.hpp"
#include "i18n.h" // NLS support.
@ -228,6 +229,7 @@ namespace Action {
case Params::pmIptc: rc = printIptc(); break;
case Params::pmXmp: rc = printXmp(); break;
case Params::pmComment: rc = printComment(); break;
case Params::pmPreview: rc = printPreviewList(); break;
}
return rc;
}
@ -770,7 +772,7 @@ namespace Action {
}
Exiv2::IptcData::const_iterator end = iptcData.end();
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) {
std::cout << std::setfill(' ') << std::left;
if (manyFiles) {
@ -808,7 +810,7 @@ namespace Action {
}
Exiv2::XmpData::const_iterator end = xmpData.end();
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) {
std::cout << std::setfill(' ') << std::left;
if (manyFiles) {
@ -845,6 +847,36 @@ namespace Action {
return 0;
} // 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
{
return AutoPtr(clone_());
@ -1061,8 +1093,12 @@ namespace Action {
if (dontOverwrite(xmpPath)) return 0;
rc = metacopy(path_, xmpPath, Exiv2::ImageType::xmp, false);
}
if (Params::instance().target_ & Params::ctPreview) {
rc = writePreviews();
}
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");
if (dontOverwrite(exvPath)) return 0;
rc = metacopy(path_, exvPath, Exiv2::ImageType::exv, false);
@ -1118,6 +1154,61 @@ namespace Action {
return rc;
} // 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
{
return AutoPtr(clone_());

@ -48,6 +48,7 @@
namespace Exiv2 {
class ExifData;
class Image;
class PreviewImage;
}
// *****************************************************************************
@ -162,6 +163,8 @@ namespace Action {
//! Print the Jpeg comment
int printComment();
//! Print list of available preview images
int printPreviewList();
//! Print uninterpreted Iptc information
int printIptc();
//! print uninterpreted XMP information
@ -278,6 +281,17 @@ namespace Action {
on the format of the Exif thumbnail image.
*/
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:
virtual Extract* clone_() const;

@ -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 "Sept 10th, 2008"
.TH EXIV2 1 "Dec 7th, 2008"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@ -152,6 +152,8 @@ i : IPTC data values
x : XMP properties
.br
c : JPEG comment
.br
p : list available image previews, sorted by preview image size in pixels
.TP
.B \-P \fIcols\fP
Print columns for the Exif taglist ('print' action), allows detailed
@ -209,7 +211,13 @@ be named \fIfile\fP\-thumb.jpg.
.TP
.B \-e \fItgt\fP
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
X : Extract metadata to an XMP sidecar file <file>.xmp. The remaining
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
to img2.jpg.
.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
Extracts IPTC datasets into an XMP sidecar file image.xmp and in the
process converts them to "IPTC Core" XMP schema.

@ -19,7 +19,7 @@
* 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
Version: $Rev$
@ -50,6 +50,7 @@ EXIV2_RCSID("@(#) $Id$")
#include <iomanip>
#include <cstring>
#include <cassert>
#include <cctype>
// *****************************************************************************
// local declarations
@ -80,6 +81,17 @@ namespace {
int parseCommonTargets(const std::string& optarg,
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
@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")
<< _(" x : XMP properties\n")
<< _(" c : JPEG comment\n")
<< _(" p : list available previews\n")
<< _(" -P cols Print columns for the Exif taglist ('print' action). Valid are:\n")
<< _(" x : print a column with the tag value\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"
" <file>-thumb.jpg\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")
<< _(" -r fmt Filename format for the 'rename' action. The format string\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 'x': printMode_ = pmXmp; break;
case 'c': printMode_ = pmComment; break;
case 'p': printMode_ = pmPreview; break;
default:
std::cerr << progname() << ": " << _("Unrecognized print mode") << " `"
<< optarg << "'\n";
@ -850,6 +866,15 @@ namespace {
target |= Params::ctXmpSidecar;
if (optarg == "X") target |= Params::ctExif | Params::ctIptc | Params::ctXmp;
break;
case 'p':
{
if (strcmp(action.c_str(), "extract") == 0) {
i += parsePreviewNumbers(Params::instance().previewNumbers_, optarg, i + 1);
target |= Params::ctPreview;
break;
}
// fallthrough
}
default:
std::cerr << Params::instance().progname() << ": " << _("Unrecognized ")
<< action << " " << _("target") << " `" << optarg[i] << "'\n";
@ -860,6 +885,46 @@ namespace {
return rc ? rc : target;
} // 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,
const Params::CmdFiles& cmdFiles)
{

@ -37,6 +37,7 @@
// + standard includes
#include <string>
#include <vector>
#include <set>
#include <iostream>
// *****************************************************************************
@ -72,7 +73,7 @@ struct CmdIdAndString {
@brief Implements the command line handling for the program.
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
from everywhere.
@ -113,6 +114,8 @@ public:
typedef std::vector<std::string> CmdLines;
//! Container to store filenames.
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.
@ -123,7 +126,7 @@ public:
void cleanup();
//! Enumerates print modes
enum PrintMode { pmSummary, pmList, pmIptc, pmXmp, pmComment };
enum PrintMode { pmSummary, pmList, pmIptc, pmXmp, pmComment, pmPreview };
//! Individual items to print
enum PrintItem {
@ -147,7 +150,8 @@ public:
ctComment = 4,
ctThumb = 8,
ctXmp = 16,
ctXmpSidecar = 32
ctXmpSidecar = 32,
ctPreview = 64
};
//! 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 suffix_; //!< File extension of the file to insert
Files files_; //!< List of non-option arguments.
PreviewNumbers previewNumbers_; //!< List of preview numbers
private:
//! Pointer to the global Params object.

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

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

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

@ -695,6 +695,16 @@ namespace Exiv2 {
return DataBuf(pData_, size_);
}
const byte* PreviewImage::pData() const
{
return pData_;
}
uint32_t PreviewImage::size() const
{
return size_;
}
std::string PreviewImage::mimeType() const
{
return properties_.mimeType_;
@ -705,9 +715,19 @@ namespace Exiv2 {
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)

@ -100,6 +100,10 @@ namespace Exiv2 {
@brief Return a pointer to the image data for read-only access.
*/
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.
@ -122,9 +126,17 @@ namespace Exiv2 {
*/
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:

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

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

Loading…
Cancel
Save