Added metacopy and the ability to extract/insert different types of metadata

Added option to print the Jpeg comment
Changed semantics of print option `i' to Iptc (`t' for translated Exif data)
v0.27.3
Andreas Huggel 21 years ago
parent 79721d04e0
commit 41fbe55ebd

@ -20,13 +20,13 @@
*/ */
/* /*
File: actions.cpp File: actions.cpp
Version: $Name: $ $Revision: 1.36 $ Version: $Name: $ $Revision: 1.37 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 08-Dec-03, ahu: created History: 08-Dec-03, ahu: created
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.36 $ $RCSfile: actions.cpp,v $"); EXIV2_RCSID("@(#) $Name: $ $Revision: 1.37 $ $RCSfile: actions.cpp,v $");
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
@ -79,6 +79,15 @@ namespace {
// Convert a UTC time to a string "YYYY:MM:DD HH:MI:SS", "" on error // Convert a UTC time to a string "YYYY:MM:DD HH:MI:SS", "" on error
std::string time2Str(time_t time); std::string time2Str(time_t time);
/*!
@brief Copy metadata from source to target according to Params::copyXyz
@param source Source file path
@param target Target file path. An *.exv file is created if target doesn't
exist.
@return 0 if successful, else an error code
*/
int metacopy(const std::string& source, const std::string& target);
} }
// ***************************************************************************** // *****************************************************************************
@ -135,11 +144,12 @@ namespace Action {
path_ = path; path_ = path;
int rc = 0; int rc = 0;
switch (Params::instance().printMode_) { switch (Params::instance().printMode_) {
case Params::summary: rc = printSummary(); break; case Params::pmSummary: rc = printSummary(); break;
case Params::interpreted: rc = printInterpreted(); break; case Params::pmInterpreted: rc = printInterpreted(); break;
case Params::values: rc = printValues(); break; case Params::pmValues: rc = printValues(); break;
case Params::hexdump: rc = printHexdump(); break; case Params::pmHexdump: rc = printHexdump(); break;
case Params::iptc: rc = printIptc(); break; case Params::pmIptc: rc = printIptc(); break;
case Params::pmComment: rc = printComment(); break;
} }
return rc; return rc;
} }
@ -509,7 +519,6 @@ namespace Action {
return 0; return 0;
} // Print::printIptc } // Print::printIptc
int Print::printHexdump() int Print::printHexdump()
{ {
Exiv2::ExifData exifData; Exiv2::ExifData exifData;
@ -543,6 +552,34 @@ namespace Action {
return 0; return 0;
} // Print::printHexdump } // Print::printHexdump
int Print::printComment()
{
if (!Util::fileExists(path_, true)) {
std::cerr << path_
<< ": Failed to open the file\n";
return -1;
}
Exiv2::Image* pImage = Exiv2::ImageFactory::instance().open(path_);
if (!pImage) {
std::cerr << path_
<< ": The file contains data of an unknown image type\n";
return -2;
}
int rc = pImage->readMetadata();
pImage->detach();
if (rc) {
std::cerr << path_
<< ": Could not read metadata\n";
return 1;
}
if (Params::instance().verbose_) {
std::cout << "Jpeg comment: ";
}
std::cout << pImage->comment() << "\n";
delete pImage;
return 0;
} // Print::printComment
Print::AutoPtr Print::clone() const Print::AutoPtr Print::clone() const
{ {
return AutoPtr(clone_()); return AutoPtr(clone_());
@ -645,9 +682,9 @@ namespace Action {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n"; std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
return rc; return rc;
} }
switch (Params::instance().delTarget_) { switch (Params::instance().target_) {
case Params::delExif: rc = eraseExifData(exifData); break; case Params::ctExif: rc = eraseExifData(exifData); break;
case Params::delThumb: rc = eraseThumbnail(exifData); break; case Params::ctThumb: rc = eraseThumbnail(exifData); break;
} }
return rc; return rc;
} }
@ -703,32 +740,12 @@ namespace Action {
int Extract::run(const std::string& path) int Extract::run(const std::string& path)
try { try {
path_ = path; path_ = path;
Exiv2::ExifData exifData; int rc = 0;
int rc = exifData.read(path); if (Params::instance().target_ & Params::ctThumb) {
if (rc) { rc = writeThumbnail();
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
return rc;
}
switch (Params::instance().extractTarget_) {
case Params::extExif: rc = writeExifData(exifData); break;
case Params::extThumb: rc = writeThumbnail(exifData); break;
} }
return rc;
}
catch(const Exiv2::Error& e)
{
std::cerr << "Exif exception in extract action for file " << path
<< ":\n" << e << "\n";
return 1;
} // Extract::run
int Extract::writeExifData(Exiv2::ExifData& exifData) const
{
std::string exvPath = Util::dirname(path_) + SEPERATOR_STR std::string exvPath = Util::dirname(path_) + SEPERATOR_STR
+ Util::basename(path_, true) + ".exv"; + Util::basename(path_, true) + ".exv";
if (Params::instance().verbose_) {
std::cout << "Writing Exif data to " << exvPath << "\n";
}
if (!Params::instance().force_ && Util::fileExists(exvPath)) { if (!Params::instance().force_ && Util::fileExists(exvPath)) {
std::cout << Params::instance().progname() std::cout << Params::instance().progname()
<< ": Overwrite `" << exvPath << "'? "; << ": Overwrite `" << exvPath << "'? ";
@ -736,16 +753,23 @@ namespace Action {
std::cin >> s; std::cin >> s;
if (s[0] != 'y' && s[0] != 'Y') return 0; if (s[0] != 'y' && s[0] != 'Y') return 0;
} }
int rc = exifData.writeExifData(exvPath); return metacopy(path_, exvPath);
if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, exvPath) << "\n";
}
return rc;
} }
catch(const Exiv2::Error& e)
{
std::cerr << "Exif exception in extract action for file " << path
<< ":\n" << e << "\n";
return 1;
} // Extract::run
int Extract::writeThumbnail(const Exiv2::ExifData& exifData) const int Extract::writeThumbnail() const
{ {
int rc = 0; Exiv2::ExifData exifData;
int rc = exifData.read(path_);
if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path_) << "\n";
return rc;
}
std::string thumb = Util::dirname(path_) + SEPERATOR_STR std::string thumb = Util::dirname(path_) + SEPERATOR_STR
+ Util::basename(path_, true) + "-thumb"; + Util::basename(path_, true) + "-thumb";
std::string thumbExt = exifData.thumbnailExtension(); std::string thumbExt = exifData.thumbnailExtension();
@ -772,7 +796,7 @@ namespace Action {
} }
} }
return rc; return rc;
} } // Extract::writeThumbnail
Extract::AutoPtr Extract::clone() const Extract::AutoPtr Extract::clone() const
{ {
@ -788,20 +812,7 @@ namespace Action {
try { try {
std::string exvPath = Util::dirname(path) + SEPERATOR_STR std::string exvPath = Util::dirname(path) + SEPERATOR_STR
+ Util::basename(path, true) + ".exv"; + Util::basename(path, true) + ".exv";
Exiv2::ExifData exifData; return metacopy(exvPath, path);
int rc = exifData.read(exvPath);
if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, exvPath) << "\n";
return rc;
}
if (Params::instance().verbose_) {
std::cout << "Inserting metadata from " << exvPath << "\n";
}
rc = exifData.write(path);
if (rc) {
std::cerr << Exiv2::ExifData::strError(rc, path) << "\n";
}
return rc;
} }
catch(const Exiv2::Error& e) catch(const Exiv2::Error& e)
{ {
@ -949,4 +960,69 @@ namespace {
return os.str(); return os.str();
} // time2Str } // time2Str
int metacopy(const std::string& source, const std::string& target)
{
if (!Util::fileExists(source, true)) {
std::cerr << source
<< ": Failed to open the file\n";
return -1;
}
Exiv2::Image* pSource = Exiv2::ImageFactory::instance().open(source);
if (!pSource) {
std::cerr << source
<< ": The file contains data of an unknown image type\n";
return -2;
}
int rc = pSource->readMetadata();
pSource->detach();
if (rc) {
std::cerr << source
<< ": Could not read metadata\n";
return 1;
}
Exiv2::Image* pTarget = Exiv2::ImageFactory::instance().open(target);
if (!pTarget) {
pTarget = Exiv2::ImageFactory::instance().create(Exiv2::Image::exv,
target);
}
if (!pTarget) {
std::cerr << target
<< ": Could not open nor create the file\n";
return 2;
}
if ( Params::instance().target_ & Params::ctExif
&& pSource->sizeExifData() > 0) {
if (Params::instance().verbose_) {
std::cout << "Writing Exif data from " << source
<< " to " << target << "\n";
}
pTarget->setExifData(pSource->exifData(), pSource->sizeExifData());
}
if ( Params::instance().target_ & Params::ctIptc
&& pSource->sizeIptcData() > 0) {
if (Params::instance().verbose_) {
std::cout << "Writing Iptc data from " << source
<< " to " << target << "\n";
}
pTarget->setIptcData(pSource->iptcData(), pSource->sizeIptcData());
}
if ( Params::instance().target_ & Params::ctComment
&& !pSource->comment().empty()) {
if (Params::instance().verbose_) {
std::cout << "Writing Jpeg comment from " << source
<< " to " << target << "\n";
}
pTarget->setComment(pSource->comment());
}
rc = pTarget->writeMetadata();
if (rc) {
std::cerr << target <<
": Could not write metadata to file, rc = " << rc << "\n";
}
delete pSource;
delete pTarget;
return rc;
} // metacopy
} }

@ -22,7 +22,7 @@
@file actions.hpp @file actions.hpp
@brief Implements base class Task, TaskFactory and the various supported @brief Implements base class Task, TaskFactory and the various supported
actions (derived from Task). actions (derived from Task).
@version $Name: $ $Revision: 1.11 $ @version $Name: $ $Revision: 1.12 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 11-Dec-03, ahu: created @date 11-Dec-03, ahu: created
@ -149,6 +149,8 @@ namespace Action {
typedef std::auto_ptr<Print> AutoPtr; typedef std::auto_ptr<Print> AutoPtr;
AutoPtr clone() const; AutoPtr clone() const;
//! Print the Jpeg comment
int printComment();
//! Print uninterpreted Iptc information //! Print uninterpreted Iptc information
int printIptc(); int printIptc();
//! Print Exif summary information //! Print Exif summary information
@ -248,12 +250,7 @@ namespace Action {
"-thumb" and the appropriate suffix (".jpg" or ".tif"), depending "-thumb" and the appropriate suffix (".jpg" or ".tif"), depending
on the format of the Exif thumbnail image. on the format of the Exif thumbnail image.
*/ */
int writeThumbnail(const Exiv2::ExifData& exifData) const; int writeThumbnail() const;
/*!
@brief Write the Exif data to a file. The filename is composed by
replacing the suffix of the image filename with ".exf".
*/
int writeExifData(Exiv2::ExifData& exifData) const;
private: private:
virtual Extract* clone_() const; virtual Extract* clone_() const;

@ -22,13 +22,13 @@
Abstract: Command line program to display and manipulate image %Exif data Abstract: Command line program to display and manipulate image %Exif data
File: exiv2.cpp File: exiv2.cpp
Version: $Name: $ $Revision: 1.14 $ Version: $Name: $ $Revision: 1.15 $
Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net> Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
History: 10-Dec-03, ahu: created History: 10-Dec-03, ahu: created
*/ */
// ***************************************************************************** // *****************************************************************************
#include "rcsid.hpp" #include "rcsid.hpp"
EXIV2_RCSID("@(#) $Name: $ $Revision: 1.14 $ $RCSfile: exiv2.cpp,v $"); EXIV2_RCSID("@(#) $Name: $ $Revision: 1.15 $ $RCSfile: exiv2.cpp,v $");
// ***************************************************************************** // *****************************************************************************
// included header files // included header files
@ -57,6 +57,15 @@ namespace {
// Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value // Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value
// in seconds if successful, else returns false. // in seconds if successful, else returns false.
bool parseTime(const std::string& ts, long& time); bool parseTime(const std::string& ts, long& time);
/*!
@brief Parse the oparg string into a bitmap of common targets.
@param optarg Option arguments
@param action Action being processed
@return A bitmap of common targets or -1 in case of a parse error
*/
int parseCommonTargets(const std::string& optarg,
const std::string& action);
} }
// ***************************************************************************** // *****************************************************************************
@ -131,12 +140,12 @@ void Params::help(std::ostream& os) const
{ {
usage(os); usage(os);
os << "\nActions:\n" os << "\nActions:\n"
<< " ad | adjust Adjust the metadata timestamp by the given time. This\n" << " ad | adjust Adjust Exif timestamps by the given time. This\n"
<< " action requires the option -a time.\n" << " action requires the option -a time.\n"
<< " pr | print Print the Exif (or other) image metadata.\n" << " pr | print Print Exif or Iptc image metadata.\n"
<< " rm | delete Delete the Exif section or thumbnail from the files.\n" << " rm | delete Delete the Exif section or thumbnail from the files.\n"
<< " ex | extract Extract the Exif data or Exif thumbnail to files.\n" << " in | insert Insert metadata from corresponding *.exv files.\n"
<< " in | insert Insert the Exif data from corresponding *.exv files.\n" << " ex | extract Extract metadata to *.exv and thumbnail image files.\n"
<< " mv | rename Rename files according to the Exif create timestamp.\n" << " mv | rename Rename files according to the Exif create timestamp.\n"
<< " The filename format can be set with -r format.\n" << " The filename format can be set with -r format.\n"
<< "\nOptions:\n" << "\nOptions:\n"
@ -146,16 +155,24 @@ void Params::help(std::ostream& os) const
<< " -f Do not prompt before overwriting existing files (force).\n" << " -f Do not prompt before overwriting existing files (force).\n"
<< " -a time Time adjustment in the format [-]HH[:MM[:SS]]. This option\n" << " -a time Time adjustment in the format [-]HH[:MM[:SS]]. This option\n"
<< " is only used with the `adjust' action.\n" << " is only used with the `adjust' action.\n"
<< " -p mode Print mode for the `print' action. Possible modes are `s'\n" << " -p mode Print mode for the `print' action. Possible modes are:\n"
<< " for a summary (the default), `i' for interpreted Exif data,\n" << " s : print a summary of the Exif metadata (the default)\n"
<< " `v' for plain Exif data values, `h' for a hexdump of the\n" << " t : interpreted (translated) Exif data\n"
<< " Exif data and `I' for Iptc data values.\n" << " v : plain Exif data values\n"
<< " h : hexdump of the Exif data\n"
<< " i : Iptc data values\n"
<< " c : Jpeg comment\n"
<< " -d tgt Delete target for the `delete' action. Possible targets are\n" << " -d tgt Delete target for the `delete' action. Possible targets are\n"
<< " `e' to delete the whole Exif section (the default) and `t'\n" << " `e' to delete the whole Exif section (the default) and `t'\n"
<< " to delete only the Exif thumbnail from the files.\n" << " to delete only the Exif thumbnail from the files.\n"
<< " -i tgt Insert target for the `insert' action. Possible targets are:\n"
<< " a : all supported metadata (default)\n"
<< " e : Exif data\n"
<< " i : Iptc data\n"
<< " c : Jpeg comment\n"
<< " -e tgt Extract target for the `extract' action. Possible targets\n" << " -e tgt Extract target for the `extract' action. Possible targets\n"
<< " are `e' to extract the Exif data to a binary file (the\n" << " are the same as those for the -i option plus:\n"
<< " default) and `t' to extract only the Exif thumbnail.\n" << " t : extract the Exif thumbnail to an image file\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). Default filename format is " << " follows strftime(3). Default filename format is "
<< format_ << ".\n\n"; << format_ << ".\n\n";
@ -213,11 +230,12 @@ int Params::option(int opt, const std::string& optarg, int optopt)
case Action::none: case Action::none:
action_ = Action::print; action_ = Action::print;
switch (optarg[0]) { switch (optarg[0]) {
case 's': printMode_ = summary; break; case 's': printMode_ = pmSummary; break;
case 'i': printMode_ = interpreted; break; case 't': printMode_ = pmInterpreted; break;
case 'v': printMode_ = values; break; case 'v': printMode_ = pmValues; break;
case 'h': printMode_ = hexdump; break; case 'h': printMode_ = pmHexdump; break;
case 'I': printMode_ = iptc; break; case 'i': printMode_ = pmIptc; break;
case 'c': printMode_ = pmComment; break;
default: default:
std::cerr << progname() << ": Unrecognized print mode `" std::cerr << progname() << ": Unrecognized print mode `"
<< optarg << "'\n"; << optarg << "'\n";
@ -241,8 +259,8 @@ int Params::option(int opt, const std::string& optarg, int optopt)
case Action::none: case Action::none:
action_ = Action::erase; action_ = Action::erase;
switch (optarg[0]) { switch (optarg[0]) {
case 'e': delTarget_ = delExif; break; case 'e': target_ = ctExif; break;
case 't': delTarget_ = delThumb; break; case 't': target_ = ctThumb; break;
default: default:
std::cerr << progname() << ": Unrecognized delete target `" std::cerr << progname() << ": Unrecognized delete target `"
<< optarg << "'\n"; << optarg << "'\n";
@ -265,23 +283,44 @@ int Params::option(int opt, const std::string& optarg, int optopt)
switch (action_) { switch (action_) {
case Action::none: case Action::none:
action_ = Action::extract; action_ = Action::extract;
switch (optarg[0]) { target_ = 0;
case 'e': extractTarget_ = extExif; break; // fallthrough
case 't': extractTarget_ = extThumb; break; case Action::extract:
default: rc = parseCommonTargets(optarg, "extract");
std::cerr << progname() << ": Unrecognized extract target `" if (rc > 0) {
<< optarg << "'\n"; target_ |= rc;
rc = 0;
}
else {
rc = 1; rc = 1;
break;
} }
break; break;
case Action::extract: default:
std::cerr << progname() std::cerr << progname()
<< ": Ignoring surplus option -e" << optarg << "\n"; << ": Option -e is not compatible with a previous option\n";
rc = 1;
break;
}
break;
case 'i':
switch (action_) {
case Action::none:
action_ = Action::insert;
target_ = 0;
// fallthrough
case Action::insert:
rc = parseCommonTargets(optarg, "insert");
if (rc > 0) {
target_ |= rc;
rc = 0;
}
else {
rc = 1;
}
break; break;
default: default:
std::cerr << progname() std::cerr << progname()
<< ": Option -e is not compatible with a previous option\n"; << ": Option -i is not compatible with a previous option\n";
rc = 1; rc = 1;
break; break;
} }
@ -444,4 +483,28 @@ namespace {
return true; return true;
} // parseTime } // parseTime
int parseCommonTargets(const std::string& optarg,
const std::string& action)
{
int rc = 0;
int target = 0;
for (size_t i = 0; i < optarg.size(); ++i) {
switch (optarg[i]) {
case 'e': target |= Params::ctExif; break;
case 'i': target |= Params::ctIptc; break;
case 'c': target |= Params::ctComment; break;
case 't': target |= Params::ctThumb; break;
case 'a': target |= Params::ctExif
| Params::ctIptc
| Params::ctComment; break;
default:
std::cerr << Params::instance().progname() << ": Unrecognized "
<< action << " target `" << optarg << "'\n";
rc = -1;
break;
}
}
return rc ? rc : target;
} // parseCommonTargets
} }

@ -21,7 +21,7 @@
/*! /*!
@file exiv2.hpp @file exiv2.hpp
@brief Defines class Params, used for the command line handling of exiv2 @brief Defines class Params, used for the command line handling of exiv2
@version $Name: $ $Revision: 1.4 $ @version $Name: $ $Revision: 1.5 $
@author Andreas Huggel (ahu) @author Andreas Huggel (ahu)
<a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a> <a href="mailto:ahuggel@gmx.net">ahuggel@gmx.net</a>
@date 08-Dec-03, ahu: created @date 08-Dec-03, ahu: created
@ -86,11 +86,10 @@ public:
static Params& instance(); static Params& instance();
//! Enumerates print modes //! Enumerates print modes
enum PrintMode { summary, interpreted, values, hexdump, iptc }; enum PrintMode { pmSummary, pmInterpreted, pmValues, pmHexdump, pmIptc,
//! Enumerates delete targets pmComment };
enum DelTarget { delExif, delThumb }; //! Enumerates common targets, bitmap
//! Enumerates extract targets enum commonTarget { ctExif = 1, ctIptc = 2, ctComment = 4, ctThumb = 8 };
enum ExtractTarget { extExif, extThumb };
bool help_; //!< Help option flag. bool help_; //!< Help option flag.
bool version_; //!< Version option flag. bool version_; //!< Version option flag.
@ -98,10 +97,9 @@ public:
bool force_; //!< Force overwrites flag. bool force_; //!< Force overwrites flag.
bool adjust_; //!< Adjustment flag. bool adjust_; //!< Adjustment flag.
PrintMode printMode_; //!< Print mode. PrintMode printMode_; //!< Print mode.
DelTarget delTarget_; //!< What to delete.
ExtractTarget extractTarget_; //!< What to extract.
//! %Action (integer rather than TaskType to avoid dependency). //! %Action (integer rather than TaskType to avoid dependency).
int action_; int action_;
int target_; //!< What common target to process.
long adjustment_; //!< Adjustment in seconds. long adjustment_; //!< Adjustment in seconds.
std::string format_; //!< Filename format (-r option arg). std::string format_; //!< Filename format (-r option arg).
@ -114,18 +112,17 @@ public:
private: private:
/*! /*!
@brief Default constructor. Note that optstring_ is initialized here. @brief Default constructor. Note that optstring_ is initialized here.
Private to force instantiation through instance(). The c'tor is private to force instantiation through instance().
*/ */
Params() : optstring_(":hVvfa:r:p:d:e:"), Params() : optstring_(":hVvfa:r:p:d:e:i:"),
help_(false), help_(false),
version_(false), version_(false),
verbose_(false), verbose_(false),
force_(false), force_(false),
adjust_(false), adjust_(false),
printMode_(summary), printMode_(pmSummary),
delTarget_(delExif),
extractTarget_(extExif),
action_(0), action_(0),
target_(ctExif|ctIptc|ctComment),
adjustment_(0), adjustment_(0),
format_("%Y%m%d_%H%M%S"), format_("%Y%m%d_%H%M%S"),
first_(true) {} first_(true) {}

Loading…
Cancel
Save