From e4c96433d44250b97ddee64a56b559ccca56084d Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Sat, 8 Mar 2008 17:10:50 +0000 Subject: [PATCH] Added support for XMP sidecar files. --- src/Makefile | 7 +- src/basicio.cpp | 2 +- src/image.cpp | 2 + src/jpgimage.cpp | 9 +-- src/orfimage.hpp | 8 +- src/xmpsidecar.cpp | 180 +++++++++++++++++++++++++++++++++++++++++++++ src/xmpsidecar.hpp | 127 ++++++++++++++++++++++++++++++++ 7 files changed, 319 insertions(+), 16 deletions(-) create mode 100644 src/xmpsidecar.cpp create mode 100644 src/xmpsidecar.hpp diff --git a/src/Makefile b/src/Makefile index 91a6db4a..68370e62 100644 --- a/src/Makefile +++ b/src/Makefile @@ -105,8 +105,9 @@ CCSRC += rafimage.cpp \ types.cpp \ value.cpp \ version.cpp \ - properties.cpp \ - xmp.cpp + properties.cpp \ + xmp.cpp \ + xmpsidecar.cpp # Add library C source files to this list ifndef HAVE_TIMEGM @@ -119,7 +120,7 @@ BINSRC = taglist.cpp # Source files for the Exiv2 application EXIV2MAIN = exiv2.cpp EXIV2SRC = actions.cpp \ - utils.cpp + utils.cpp # C source files for the Exiv2 application ifndef HAVE_TIMEGM EXIVCSRC = localtime.c diff --git a/src/basicio.cpp b/src/basicio.cpp index 59d2ddea..20797fca 100644 --- a/src/basicio.cpp +++ b/src/basicio.cpp @@ -380,7 +380,7 @@ namespace Exiv2 { bool FileIo::eof() const { assert(fp_ != 0); - return feof(fp_) != 0; + return feof(fp_) != 0; } std::string FileIo::path() const diff --git a/src/image.cpp b/src/image.cpp index 33049bac..61bd8c14 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -54,6 +54,7 @@ EXIV2_RCSID("@(#) $Id$") #include "rafimage.hpp" #include "tiffimage.hpp" #include "orfimage.hpp" +#include "xmpsidecar.hpp" // + standard includes #include @@ -87,6 +88,7 @@ namespace Exiv2 { { ImageType::png, newPngInstance, isPngType, amRead, amRead, amRead, amNone }, #endif // EXV_HAVE_LIBZ { ImageType::raf, newRafInstance, isRafType, amRead, amRead, amRead, amNone }, + { ImageType::xmp, newXmpInstance, isXmpType, amNone, amNone, amReadWrite, amNone }, // End of list marker { ImageType::none, 0, 0, amNone, amNone, amNone, amNone } }; diff --git a/src/jpgimage.cpp b/src/jpgimage.cpp index ee2482bc..b09df6ef 100644 --- a/src/jpgimage.cpp +++ b/src/jpgimage.cpp @@ -746,12 +746,7 @@ namespace Exiv2 { Image::AutoPtr newExvInstance(BasicIo::AutoPtr io, bool create) { Image::AutoPtr image; - if (create) { - image = Image::AutoPtr(new ExvImage(io, true)); - } - else { - image = Image::AutoPtr(new ExvImage(io, false)); - } + image = Image::AutoPtr(new ExvImage(io, create)); if (!image->good()) image.reset(); return image; } @@ -767,7 +762,7 @@ namespace Exiv2 { || memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) { result = false; } - if (!advance || !result ) iIo.seek(-7, BasicIo::cur); + if (!advance || !result) iIo.seek(-7, BasicIo::cur); return result; } diff --git a/src/orfimage.hpp b/src/orfimage.hpp index e29a98df..8640cd30 100644 --- a/src/orfimage.hpp +++ b/src/orfimage.hpp @@ -95,7 +95,7 @@ namespace Exiv2 { */ void setIptcData(const IptcData& iptcData); /*! - @brief Not supported. MRW format does not contain a comment. + @brief Not supported. ORF format does not contain a comment. Calling this function will throw an Error(32). */ void setComment(const std::string& comment); @@ -141,21 +141,19 @@ namespace Exiv2 { //@} }; // class OrfHeader - - // ***************************************************************************** // template, inline and free functions // These could be static private functions on Image subclasses but then // ImageFactory needs to be made a friend. /*! - @brief Create a new MrwImage instance and return an auto-pointer to it. + @brief Create a new OrfImage instance and return an auto-pointer to it. Caller owns the returned object and the auto-pointer ensures that it will be deleted. */ Image::AutoPtr newOrfInstance(BasicIo::AutoPtr io, bool create); - //! Check if the file iIo is a MRW image. + //! Check if the file iIo is an ORF image. bool isOrfType(BasicIo& iIo, bool advance); } // namespace Exiv2 diff --git a/src/xmpsidecar.cpp b/src/xmpsidecar.cpp new file mode 100644 index 00000000..8d234e14 --- /dev/null +++ b/src/xmpsidecar.cpp @@ -0,0 +1,180 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2008 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: xmpsidecar.cpp + Version: $Rev$ + Author(s): Andreas Huggel (ahu) + History: 07-Mar-08, ahu: created + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id$") + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "xmpsidecar.hpp" +#include "image.hpp" +#include "basicio.hpp" +#include "error.hpp" +#include "xmp.hpp" +#include "futils.hpp" + +// + standard includes +#include +#include +#include + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + XmpSidecar::XmpSidecar(BasicIo::AutoPtr io) + : Image(ImageType::xmp, mdXmp, io) + { + } // XmpSidecar::XmpSidecar + + void XmpSidecar::setExifData(const ExifData& /*exifData*/) + { + // Todo: implement me! + throw(Error(32, "Exif metadata", "XMP")); + } + + void XmpSidecar::setIptcData(const IptcData& /*iptcData*/) + { + // Todo: implement me! + throw(Error(32, "IPTC metadata", "XMP")); + } + + void XmpSidecar::setComment(const std::string& /*comment*/) + { + // not supported + throw(Error(32, "Image comment", "XMP")); + } + + void XmpSidecar::readMetadata() + { +#ifdef DEBUG + std::cerr << "Reading XMP file " << io_->path() << "\n"; +#endif + if (io_->open() != 0) { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (!isXmpType(*io_, false)) { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "XMP"); + } + // Read the XMP packet from the IO stream + std::string xmpPacket; + const long len = 64 * 1024; + byte buf[len]; + long l; + while ((l = io_->read(buf, len)) > 0) { + xmpPacket.append(reinterpret_cast(buf), l); + } + if (io_->error()) throw Error(14); + clearMetadata(); + xmpPacket_ = xmpPacket; + if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_)) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Warning: Failed to decode XMP metadata.\n"; +#endif + } + } // XmpSidecar::readMetadata + + void XmpSidecar::writeMetadata() + { + if (io_->open() != 0) { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + + if (writeXmpFromPacket() == false) { + if (XmpParser::encode(xmpPacket_, xmpData_)) { +#ifndef SUPPRESS_WARNINGS + std::cerr << "Error: Failed to encode XMP metadata.\n"; +#endif + } + } + if (xmpPacket_.size() > 0) { + BasicIo::AutoPtr tempIo(io_->temporary()); // may throw + assert(tempIo.get() != 0); + // Write XMP packet + if ( tempIo->write(reinterpret_cast(xmpPacket_.data()), + static_cast(xmpPacket_.size())) + != static_cast(xmpPacket_.size())) throw Error(21); + if (tempIo->error()) throw Error(21); + io_->close(); + io_->transfer(*tempIo); // may throw + } + } // XmpSidecar::writeMetadata + + // ************************************************************************* + // free functions + Image::AutoPtr newXmpInstance(BasicIo::AutoPtr io, bool /*create*/) + { + Image::AutoPtr image(new XmpSidecar(io)); + if (!image->good()) { + image.reset(); + } + return image; + } + + bool isXmpType(BasicIo& iIo, bool advance) + { + /* + Make sure the file starts with and (optional) XML declaration, + followed by an XMP header () or an + element. That doesn't cover all cases, since also x:xmpmeta is + optional, but let's wait and see. + */ + + // Todo: Proper implementation + + const int32_t len = 10; + byte buf[len]; + iIo.read(buf, len); + if (iIo.error() || iIo.eof()) { + return false; + } + bool rc = false; + const std::string head(reinterpret_cast(buf), len); + if ( head.substr(0, 5) == " + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file xmpsidecar.hpp + @brief An Image subclass to support XMP sidecar files + @version $Rev$ + @author Andreas Huggel + ahuggel@gmx.net + @date 07-Mar-08, ahu: created + */ +#ifndef XMPSIDECAR_HPP_ +#define XMPSIDECAR_HPP_ + +// ***************************************************************************** +// included header files +#include "image.hpp" +#include "basicio.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + // Add XMP to the supported image formats + namespace ImageType { + const int xmp = 10; //!< XMP sidecar files (see class XmpSidecar) + } + + /*! + @brief Class to access XMP sidecar files. They contain only XMP metadata. + */ + class XmpSidecar : public Image { + public: + //! @name Creators + //@{ + /*! + @brief Constructor for an XMP sidecar file. Since the constructor + can not return a result, callers should check the good() method + after object construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + */ + XmpSidecar(BasicIo::AutoPtr io); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + /*! + @brief Todo: Not supported yet, requires conversion from Exif to XMP. + Calling this function will throw an instance of Error(32). + */ + void setExifData(const ExifData& exifData); + /*! + @brief Todo: Not supported yet, requires conversion from IPTC to XMP. + Calling this function will throw an instance of Error(32). + */ + void setIptcData(const IptcData& iptcData); + /*! + @brief Not supported. XMP sidecar files do not contain a comment. + Calling this function will throw an instance of Error(32). + */ + void setComment(const std::string& comment); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const { return "application/rdf+xml"; } + //@} + + private: + //! @name NOT Implemented + //@{ + //! Copy constructor + XmpSidecar(const XmpSidecar& rhs); + //! Assignment operator + XmpSidecar& operator=(const XmpSidecar& rhs); + //@} + + }; // class XmpSidecar + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new XmpSidecar instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + Image::AutoPtr newXmpInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is an XMP sidecar file. + bool isXmpType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // #ifndef XMPSIDECAR_HPP_