diff --git a/src/actions.cpp b/src/actions.cpp index 57aef606..99dac679 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -20,13 +20,13 @@ */ /* File: actions.cpp - Version: $Name: $ $Revision: 1.36 $ + Version: $Name: $ $Revision: 1.37 $ Author(s): Andreas Huggel (ahu) History: 08-Dec-03, ahu: created */ // ***************************************************************************** #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 @@ -79,6 +79,15 @@ namespace { // Convert a UTC time to a string "YYYY:MM:DD HH:MI:SS", "" on error 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; int rc = 0; switch (Params::instance().printMode_) { - case Params::summary: rc = printSummary(); break; - case Params::interpreted: rc = printInterpreted(); break; - case Params::values: rc = printValues(); break; - case Params::hexdump: rc = printHexdump(); break; - case Params::iptc: rc = printIptc(); break; + case Params::pmSummary: rc = printSummary(); break; + case Params::pmInterpreted: rc = printInterpreted(); break; + case Params::pmValues: rc = printValues(); break; + case Params::pmHexdump: rc = printHexdump(); break; + case Params::pmIptc: rc = printIptc(); break; + case Params::pmComment: rc = printComment(); break; } return rc; } @@ -509,7 +519,6 @@ namespace Action { return 0; } // Print::printIptc - int Print::printHexdump() { Exiv2::ExifData exifData; @@ -543,6 +552,34 @@ namespace Action { return 0; } // 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 { return AutoPtr(clone_()); @@ -645,9 +682,9 @@ namespace Action { std::cerr << Exiv2::ExifData::strError(rc, path) << "\n"; return rc; } - switch (Params::instance().delTarget_) { - case Params::delExif: rc = eraseExifData(exifData); break; - case Params::delThumb: rc = eraseThumbnail(exifData); break; + switch (Params::instance().target_) { + case Params::ctExif: rc = eraseExifData(exifData); break; + case Params::ctThumb: rc = eraseThumbnail(exifData); break; } return rc; } @@ -703,32 +740,12 @@ namespace Action { int Extract::run(const std::string& path) try { path_ = path; - Exiv2::ExifData exifData; - int rc = exifData.read(path); - if (rc) { - 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; + int rc = 0; + if (Params::instance().target_ & Params::ctThumb) { + rc = writeThumbnail(); } - 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 + Util::basename(path_, true) + ".exv"; - if (Params::instance().verbose_) { - std::cout << "Writing Exif data to " << exvPath << "\n"; - } if (!Params::instance().force_ && Util::fileExists(exvPath)) { std::cout << Params::instance().progname() << ": Overwrite `" << exvPath << "'? "; @@ -736,16 +753,23 @@ namespace Action { std::cin >> s; if (s[0] != 'y' && s[0] != 'Y') return 0; } - int rc = exifData.writeExifData(exvPath); - if (rc) { - std::cerr << Exiv2::ExifData::strError(rc, exvPath) << "\n"; - } - return rc; + return metacopy(path_, exvPath); } + 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 + Util::basename(path_, true) + "-thumb"; std::string thumbExt = exifData.thumbnailExtension(); @@ -772,7 +796,7 @@ namespace Action { } } return rc; - } + } // Extract::writeThumbnail Extract::AutoPtr Extract::clone() const { @@ -788,20 +812,7 @@ namespace Action { try { std::string exvPath = Util::dirname(path) + SEPERATOR_STR + Util::basename(path, true) + ".exv"; - Exiv2::ExifData exifData; - 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; + return metacopy(exvPath, path); } catch(const Exiv2::Error& e) { @@ -949,4 +960,69 @@ namespace { return os.str(); } // 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 } diff --git a/src/actions.hpp b/src/actions.hpp index 56871c40..77bf971d 100644 --- a/src/actions.hpp +++ b/src/actions.hpp @@ -22,7 +22,7 @@ @file actions.hpp @brief Implements base class Task, TaskFactory and the various supported actions (derived from Task). - @version $Name: $ $Revision: 1.11 $ + @version $Name: $ $Revision: 1.12 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 11-Dec-03, ahu: created @@ -149,6 +149,8 @@ namespace Action { typedef std::auto_ptr AutoPtr; AutoPtr clone() const; + //! Print the Jpeg comment + int printComment(); //! Print uninterpreted Iptc information int printIptc(); //! Print Exif summary information @@ -248,12 +250,7 @@ namespace Action { "-thumb" and the appropriate suffix (".jpg" or ".tif"), depending on the format of the Exif thumbnail image. */ - int writeThumbnail(const Exiv2::ExifData& exifData) 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; + int writeThumbnail() const; private: virtual Extract* clone_() const; diff --git a/src/exiv2.cpp b/src/exiv2.cpp index c42bd1a5..68865f72 100644 --- a/src/exiv2.cpp +++ b/src/exiv2.cpp @@ -22,13 +22,13 @@ Abstract: Command line program to display and manipulate image %Exif data File: exiv2.cpp - Version: $Name: $ $Revision: 1.14 $ + Version: $Name: $ $Revision: 1.15 $ Author(s): Andreas Huggel (ahu) History: 10-Dec-03, ahu: created */ // ***************************************************************************** #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 @@ -57,6 +57,15 @@ namespace { // Evaluate [-]HH[:MM[:SS]], returns true and sets time to the value // in seconds if successful, else returns false. 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); 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" - << " 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" - << " ex | extract Extract the Exif data or Exif thumbnail to files.\n" - << " in | insert Insert the Exif data from corresponding *.exv files.\n" + << " in | insert Insert metadata 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" << " The filename format can be set with -r format.\n" << "\nOptions:\n" @@ -146,16 +155,24 @@ void Params::help(std::ostream& os) const << " -f Do not prompt before overwriting existing files (force).\n" << " -a time Time adjustment in the format [-]HH[:MM[:SS]]. This option\n" << " is only used with the `adjust' action.\n" - << " -p mode Print mode for the `print' action. Possible modes are `s'\n" - << " for a summary (the default), `i' for interpreted Exif data,\n" - << " `v' for plain Exif data values, `h' for a hexdump of the\n" - << " Exif data and `I' for Iptc data values.\n" + << " -p mode Print mode for the `print' action. Possible modes are:\n" + << " s : print a summary of the Exif metadata (the default)\n" + << " t : interpreted (translated) Exif data\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" << " `e' to delete the whole Exif section (the default) and `t'\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" - << " are `e' to extract the Exif data to a binary file (the\n" - << " default) and `t' to extract only the Exif thumbnail.\n" + << " are the same as those for the -i option plus:\n" + << " t : extract the Exif thumbnail to an image file\n" << " -r fmt Filename format for the `rename' action. The format string\n" << " follows strftime(3). Default filename format is " << format_ << ".\n\n"; @@ -213,11 +230,12 @@ int Params::option(int opt, const std::string& optarg, int optopt) case Action::none: action_ = Action::print; switch (optarg[0]) { - case 's': printMode_ = summary; break; - case 'i': printMode_ = interpreted; break; - case 'v': printMode_ = values; break; - case 'h': printMode_ = hexdump; break; - case 'I': printMode_ = iptc; break; + case 's': printMode_ = pmSummary; break; + case 't': printMode_ = pmInterpreted; break; + case 'v': printMode_ = pmValues; break; + case 'h': printMode_ = pmHexdump; break; + case 'i': printMode_ = pmIptc; break; + case 'c': printMode_ = pmComment; break; default: std::cerr << progname() << ": Unrecognized print mode `" << optarg << "'\n"; @@ -241,8 +259,8 @@ int Params::option(int opt, const std::string& optarg, int optopt) case Action::none: action_ = Action::erase; switch (optarg[0]) { - case 'e': delTarget_ = delExif; break; - case 't': delTarget_ = delThumb; break; + case 'e': target_ = ctExif; break; + case 't': target_ = ctThumb; break; default: std::cerr << progname() << ": Unrecognized delete target `" << optarg << "'\n"; @@ -265,23 +283,44 @@ int Params::option(int opt, const std::string& optarg, int optopt) switch (action_) { case Action::none: action_ = Action::extract; - switch (optarg[0]) { - case 'e': extractTarget_ = extExif; break; - case 't': extractTarget_ = extThumb; break; - default: - std::cerr << progname() << ": Unrecognized extract target `" - << optarg << "'\n"; + target_ = 0; + // fallthrough + case Action::extract: + rc = parseCommonTargets(optarg, "extract"); + if (rc > 0) { + target_ |= rc; + rc = 0; + } + else { rc = 1; - break; } break; - case Action::extract: + default: 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; default: 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; break; } @@ -444,4 +483,28 @@ namespace { return true; } // 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 + } diff --git a/src/exiv2.hpp b/src/exiv2.hpp index 34235205..f209d5a7 100644 --- a/src/exiv2.hpp +++ b/src/exiv2.hpp @@ -21,7 +21,7 @@ /*! @file exiv2.hpp @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) ahuggel@gmx.net @date 08-Dec-03, ahu: created @@ -86,11 +86,10 @@ public: static Params& instance(); //! Enumerates print modes - enum PrintMode { summary, interpreted, values, hexdump, iptc }; - //! Enumerates delete targets - enum DelTarget { delExif, delThumb }; - //! Enumerates extract targets - enum ExtractTarget { extExif, extThumb }; + enum PrintMode { pmSummary, pmInterpreted, pmValues, pmHexdump, pmIptc, + pmComment }; + //! Enumerates common targets, bitmap + enum commonTarget { ctExif = 1, ctIptc = 2, ctComment = 4, ctThumb = 8 }; bool help_; //!< Help option flag. bool version_; //!< Version option flag. @@ -98,10 +97,9 @@ public: bool force_; //!< Force overwrites flag. bool adjust_; //!< Adjustment flag. PrintMode printMode_; //!< Print mode. - DelTarget delTarget_; //!< What to delete. - ExtractTarget extractTarget_; //!< What to extract. //! %Action (integer rather than TaskType to avoid dependency). int action_; + int target_; //!< What common target to process. long adjustment_; //!< Adjustment in seconds. std::string format_; //!< Filename format (-r option arg). @@ -114,18 +112,17 @@ public: private: /*! @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), version_(false), verbose_(false), force_(false), adjust_(false), - printMode_(summary), - delTarget_(delExif), - extractTarget_(extExif), + printMode_(pmSummary), action_(0), + target_(ctExif|ctIptc|ctComment), adjustment_(0), format_("%Y%m%d_%H%M%S"), first_(true) {}