From 9d72b7d1ec8b43518da3a4a074630f663af7e6fb Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Mon, 26 Jul 2004 08:11:38 +0000 Subject: [PATCH] Revamped image and introduced byte* interface (Brad, bs_1.patch with minor modifications by ahu) --- config.h.in | 9 + src/Makefile | 28 +- src/actions.cpp | 12 +- src/canonmn.cpp | 9 +- src/canonmn.hpp | 6 +- src/error.hpp | 4 +- src/exif.cpp | 112 ++--- src/exif.hpp | 30 +- src/exifcomment.cpp | 5 +- src/fujimn.cpp | 11 +- src/fujimn.hpp | 6 +- src/getopt_win32.c | 2 +- src/ifd-test.cpp | 4 +- src/ifd.cpp | 36 +- src/ifd.hpp | 20 +- src/image.cpp | 1018 ++++++++++++++++++++++++++----------------- src/image.hpp | 780 +++++++++++++++++++-------------- src/imagetest.sh | 101 +++++ src/makernote.cpp | 18 +- src/makernote.hpp | 18 +- src/metacopy.cpp | 193 ++++++++ src/metacopy.hpp | 86 ++++ src/nikonmn.cpp | 19 +- src/nikonmn.hpp | 10 +- src/sigmamn.cpp | 14 +- src/sigmamn.hpp | 6 +- src/tags.cpp | 6 +- src/tags.hpp | 4 +- src/types.cpp | 104 ++--- src/types.hpp | 38 +- src/utils.cpp | 20 +- src/value.cpp | 26 +- src/value.hpp | 50 +-- 33 files changed, 1801 insertions(+), 1004 deletions(-) create mode 100755 src/imagetest.sh create mode 100644 src/metacopy.cpp create mode 100644 src/metacopy.hpp diff --git a/config.h.in b/config.h.in index 114c49b4..62c35f47 100644 --- a/config.h.in +++ b/config.h.in @@ -104,3 +104,12 @@ /* Define to `unsigned' if does not define. */ #undef size_t + +/* File path seperator */ +#ifdef _MSC_VER +#define SEPERATOR_STR "\\" +#define SEPERATOR_CHR '\\' +#else +#define SEPERATOR_STR "/" +#define SEPERATOR_CHR '/' +#endif diff --git a/src/Makefile b/src/Makefile index 97b470e8..2022000a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,13 +61,19 @@ BINSRC = addmoddel.cpp exifcomment.cpp exifprint.cpp ifd-test.cpp \ # State the main source file of the Exiv2 application here EXIV2MAIN = exiv2.cpp -# Add additional source files of the real application to this list +# Add additional source files of the Exiv2 application to this list EXIV2SRC = actions.cpp utils.cpp -# C source files of the application +# C source files of the Exiv2 application ifndef HAVE_TIMEGM EXIVCSRC = localtime.c endif +# State the main source file of the metacopy application here +MCMAIN = metacopy.cpp + +# Add additional source files of the metacopy application to this list +MCSRC = utils.cpp + # ****************************************************************************** # Library @@ -101,7 +107,8 @@ HDR = $(CCHDR) OBJ = $(CCOBJ) SOBJ = $(CCSOBJ) DEP = $(CCSRC:%.cpp=.%.d) $(BINSRC:%.cpp=.%.d) \ - $(EXIV2MAIN:%.cpp=.%.d) $(EXIV2SRC:%.cpp=.%.d) $(EXIVCSRC:%.c=.%.d) + $(EXIV2MAIN:%.cpp=.%.d) $(EXIV2SRC:%.cpp=.%.d) $(EXIVCSRC:%.c=.%.d) \ + $(MCMAIN:%.cpp=.%.d) $(MCSRC:%.cpp=.%.d) BINOBJ = $(BINSRC:.cpp=.o) BINARY = $(BINSRC:.cpp=) @@ -109,6 +116,10 @@ BINARY = $(BINSRC:.cpp=) EXIV2OBJ = $(EXIV2MAIN:.cpp=.o) $(EXIV2SRC:.cpp=.o) $(EXIVCSRC:.c=.o) EXIV2BIN = $(EXIV2MAIN:.cpp=) +MCOBJ = $(MCMAIN:.cpp=.o) $(MCSRC:.cpp=.o) +MCBIN = $(MCMAIN:.cpp=) + + ARCHIVE = lib$(LIBNAME)$(ARCHIVE_SUFFIX) SHAREDLIB = lib$(LIBNAME)$(SHAREDLIB_SUFFIX) @@ -179,6 +190,9 @@ $(BINARY): %: %.o $(EXIV2BIN): %: %.o $(CXX) $(CXXFLAGS) $(EXIV2OBJ) $(LDLIBS) $(LDFLAGS_BIN) -o $@ +$(MCBIN): %: %.o + $(CXX) $(CXXFLAGS) $(MCOBJ) $(LDLIBS) $(LDFLAGS_BIN) -o $@ + mn.cpp: ./mn.sh ./mn.sh @@ -217,7 +231,9 @@ endif $(EXIV2BIN): lib $(EXIV2OBJ) -bin: lib $(BINARY) $(EXIV2BIN) +$(MCBIN): lib $(MCOBJ) + +bin: lib $(BINARY) $(EXIV2BIN) $(MCBIN) install: $(INSTALL) $(INSTALL_DIRS) $(bindir) @@ -278,7 +294,7 @@ check: mostlyclean: $(RM) core $(RM) $(CCSRC:.cpp=.ii) - $(RM) $(OBJ) $(SOBJ) $(BINOBJ) $(EXIV2OBJ) + $(RM) $(OBJ) $(SOBJ) $(BINOBJ) $(EXIV2OBJ) $(MCOBJ) $(RM) mn.o @if test -n "$(CXX_REPOSITORY)"; then \ echo "rm -rf $(CXX_REPOSITORY)"; \ @@ -287,7 +303,7 @@ mostlyclean: clean: mostlyclean $(RM) $(ARCHIVE) $(SHAREDLIB) - $(RM) $(BINARY) $(EXIV2BIN) + $(RM) $(BINARY) $(EXIV2BIN) $(MCBIN) # Run `make distclean' from the top source directory to also remove # files created by configuring the program. diff --git a/src/actions.cpp b/src/actions.cpp index f23afa1e..646f4306 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -20,13 +20,13 @@ */ /* File: actions.cpp - Version: $Name: $ $Revision: 1.30 $ + Version: $Name: $ $Revision: 1.31 $ Author(s): Andreas Huggel (ahu) History: 08-Dec-03, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.30 $ $RCSfile: actions.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.31 $ $RCSfile: actions.cpp,v $") // ***************************************************************************** // included header files @@ -513,7 +513,7 @@ namespace Action { return 1; } std::string newPath - = Util::dirname(path) + "/" + basename + Util::suffix(path); + = Util::dirname(path) + SEPERATOR_STR + basename + Util::suffix(path); if ( Util::dirname(newPath) == Util::dirname(path) && Util::basename(newPath) == Util::basename(path)) { if (Params::instance().verbose_) { @@ -647,7 +647,7 @@ namespace Action { int Extract::writeExifData(Exiv2::ExifData& exifData) const { - std::string exvPath = Util::dirname(path_) + "/" + 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"; @@ -669,7 +669,7 @@ namespace Action { int Extract::writeThumbnail(const Exiv2::ExifData& exifData) const { int rc = 0; - std::string thumb = Util::dirname(path_) + "/" + std::string thumb = Util::dirname(path_) + SEPERATOR_STR + Util::basename(path_, true) + "-thumb"; std::string thumbExt = exifData.thumbnailExtension(); if (thumbExt.empty()) { @@ -709,7 +709,7 @@ namespace Action { int Insert::run(const std::string& path) try { - std::string exvPath = Util::dirname(path) + "/" + std::string exvPath = Util::dirname(path) + SEPERATOR_STR + Util::basename(path, true) + ".exv"; Exiv2::ExifData exifData; int rc = exifData.read(exvPath); diff --git a/src/canonmn.cpp b/src/canonmn.cpp index 4c37b697..1d8c7d8b 100644 --- a/src/canonmn.cpp +++ b/src/canonmn.cpp @@ -20,7 +20,7 @@ */ /* File: canonmn.cpp - Version: $Name: $ $Revision: 1.10 $ + Version: $Name: $ $Revision: 1.11 $ Author(s): Andreas Huggel (ahu) History: 18-Feb-04, ahu: created 07-Mar-04, ahu: isolated as a separate component @@ -30,7 +30,7 @@ */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.10 $ $RCSfile: canonmn.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.11 $ $RCSfile: canonmn.cpp,v $") // ***************************************************************************** // included header files @@ -553,7 +553,8 @@ namespace Exiv2 { } if (value.count() < 26) return os; - float fu = value.toLong(25); + // Todo: why not use toFloat()? + float fu = static_cast(value.toLong(25)); float len1 = value.toLong(23) / fu; float len2 = value.toLong(24) / fu; std::ostringstream oss; @@ -656,7 +657,7 @@ namespace Exiv2 { // free functions MakerNote* createCanonMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset) diff --git a/src/canonmn.hpp b/src/canonmn.hpp index 32ed4d76..789eb161 100644 --- a/src/canonmn.hpp +++ b/src/canonmn.hpp @@ -23,10 +23,10 @@ @brief Canon MakerNote implemented according to the specification EXIF MakerNote of Canon by David Burren - @version $Name: $ $Revision: 1.8 $ + @version $Name: $ $Revision: 1.9 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 18-Feb-04, ahu: created + @date 18-Feb-04, ahu: created
07-Mar-04, ahu: isolated as a separate component */ #ifndef CANONMN_HPP_ @@ -73,7 +73,7 @@ namespace Exiv2 { this copy and is responsible to delete it! */ MakerNote* createCanonMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset); diff --git a/src/error.hpp b/src/error.hpp index 303b50c6..116c2470 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -21,10 +21,10 @@ /*! @file error.hpp @brief Error class for exceptions - @version $Name: $ $Revision: 1.2 $ + @version $Name: $ $Revision: 1.3 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 15-Jan-04, ahu: created + @date 15-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component */ #ifndef ERROR_HPP_ diff --git a/src/exif.cpp b/src/exif.cpp index 597c970f..0b7dc984 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -20,14 +20,14 @@ */ /* File: exif.cpp - Version: $Name: $ $Revision: 1.48 $ + Version: $Name: $ $Revision: 1.49 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.48 $ $RCSfile: exif.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.49 $ $RCSfile: exif.cpp,v $") // Define DEBUG_MAKERNOTE to output debug information to std::cerr #undef DEBUG_MAKERNOTE @@ -173,7 +173,7 @@ namespace Exiv2 { ifd_(ifd1, 0, false) { if (rhs.pImage_ && rhs.size_ > 0) { - pImage_ = new char[rhs.size_]; + pImage_ = new byte[rhs.size_]; memcpy(pImage_, rhs.pImage_, rhs.size_); tiffHeader_.read(pImage_); ifd_.read(pImage_ + tiffHeader_.offset(), @@ -184,9 +184,9 @@ namespace Exiv2 { TiffThumbnail& TiffThumbnail::operator=(const TiffThumbnail& rhs) { - char* pNewImage = 0; + byte* pNewImage = 0; if (rhs.pImage_ && rhs.size_ > 0) { - pNewImage = new char[rhs.size_]; + pNewImage = new byte[rhs.size_]; memcpy(pNewImage, rhs.pImage_, rhs.size_); tiffHeader_.read(rhs.pImage_); ifd_.read(pNewImage + tiffHeader_.offset(), @@ -200,7 +200,7 @@ namespace Exiv2 { return *this; } - int TiffThumbnail::read(const char* buf, + int TiffThumbnail::read(const byte* buf, long len, const ExifData& exifData, ByteOrder byteOrder) @@ -276,7 +276,7 @@ namespace Exiv2 { ifd1.copy(img.pData_ + ifdOffset, tiffHeader.byteOrder(), ifdOffset); delete[] pImage_; - pImage_ = new char[buflen]; + pImage_ = new byte[buflen]; memcpy(pImage_, img.pData_, buflen); size_ = buflen; offset_ = minOffset; @@ -302,11 +302,12 @@ namespace Exiv2 { int TiffThumbnail::write(const std::string& path) const { std::string name = path + extension(); - std::ofstream file(name.c_str(), std::ios::binary); - if (!file) return -1; - file.write(pImage_, size_); - if (!file.good()) return 4; - return 0; + FILE *ofp = fopen(name.c_str(), "wb"); + if (!ofp) return -1; + int rc = 0; + if (fwrite(pImage_, 1, size_, ofp) != (size_t)size_) rc = 4; + fclose(ofp); + return rc; } // TiffThumbnail::write void TiffThumbnail::update(ExifData& exifData) const @@ -338,7 +339,7 @@ namespace Exiv2 { } // TiffThumbnail::update - long TiffThumbnail::copy(char* buf) const + long TiffThumbnail::copy(byte* buf) const { long offset = ifd_.offset() + ifd_.size() + ifd_.dataSize(); long size = size_ - offset; @@ -402,16 +403,16 @@ namespace Exiv2 { : offset_(rhs.offset_), size_(rhs.size_), pImage_(0) { if (rhs.pImage_ && rhs.size_ > 0) { - pImage_ = new char[rhs.size_]; + pImage_ = new byte[rhs.size_]; memcpy(pImage_, rhs.pImage_, rhs.size_); } } JpegThumbnail& JpegThumbnail::operator=(const JpegThumbnail& rhs) { - char* pNewImage = 0; + byte* pNewImage = 0; if (rhs.pImage_ && rhs.size_ > 0) { - pNewImage = new char[rhs.size_]; + pNewImage = new byte[rhs.size_]; memcpy(pNewImage, rhs.pImage_, rhs.size_); } offset_ = rhs.offset_; @@ -421,7 +422,7 @@ namespace Exiv2 { return *this; } - int JpegThumbnail::read(const char* buf, + int JpegThumbnail::read(const byte* buf, long len, const ExifData& exifData, ByteOrder byteOrder) @@ -436,7 +437,7 @@ namespace Exiv2 { long size = pos->toLong(); if (len < offset + size) return 1; delete[] pImage_; - pImage_ = new char[size]; + pImage_ = new byte[size]; memcpy(pImage_, buf + offset, size); size_ = size; offset_ = offset; @@ -456,11 +457,12 @@ namespace Exiv2 { int JpegThumbnail::write(const std::string& path) const { std::string name = path + extension(); - std::ofstream file(name.c_str(), std::ios::binary); - if (!file) return -1; - file.write(pImage_, size_); - if (!file.good()) return 4; - return 0; + FILE *ofp = fopen(name.c_str(), "wb"); + if (!ofp) return -1; + int rc = 0; + if (fwrite(pImage_, 1, size_, ofp) != (size_t)size_) rc = 4; + fclose(ofp); + return rc; } // JpegThumbnail::write void JpegThumbnail::update(ExifData& exifData) const @@ -487,7 +489,7 @@ namespace Exiv2 { } // JpegThumbnail::update - long JpegThumbnail::copy(char* buf) const + long JpegThumbnail::copy(byte* buf) const { memcpy(buf, pImage_, size_); return size_; @@ -533,30 +535,25 @@ namespace Exiv2 { int ExifData::read(const std::string& path) { - std::ifstream file(path.c_str(), std::ios::binary); - if (!file) return -1; - Image* pImage = ImageFactory::instance().create(file); + Image* pImage = ImageFactory::instance().open(path); + // Todo: if (!pImage) return -1; if (pImage) { - int rc = pImage->readExifData(file); - if (rc == 0) rc = read(pImage->exifData(), pImage->sizeExifData()); + int rc = pImage->readMetadata(); + if (rc == 0 && pImage->sizeExifData() > 0 ) { + rc = read(pImage->exifData(), pImage->sizeExifData()); + } delete pImage; return rc; } - if (ExvFile::isThisType(file)) { - ExvFile exvFile; - int rc = exvFile.readExifData(file); - if (rc == 0) rc = read(exvFile.exifData(), exvFile.sizeExifData()); - return rc; - } // We don't know this type of file return -2; } - int ExifData::read(const char* buf, long len) + int ExifData::read(const byte* buf, long len) { // Copy the data buffer delete[] pData_; - pData_ = new char[len]; + pData_ = new byte[len]; memcpy(pData_, buf, len); size_ = len; @@ -581,8 +578,9 @@ namespace Exiv2 { if (pos != exifIfd_.end() && make != ifd0_.end() && model != ifd0_.end()) { MakerNoteFactory& mnf = MakerNoteFactory::instance(); // Todo: The conversion to string assumes that there is a \0 at the end - pMakerNote_ = mnf.create(make->data(), - model->data(), + // Todo: How to avoid the cast (is that a MSVC thing?) + pMakerNote_ = mnf.create(reinterpret_cast(make->data()), + reinterpret_cast(model->data()), false, pos->data(), pos->size(), @@ -654,13 +652,15 @@ namespace Exiv2 { int ExifData::erase(const std::string& path) const { - std::ifstream is(path.c_str(), std::ios::binary); - if (!is) return -1; - Image* pImage = ImageFactory::instance().create(is); - is.close(); + Image* pImage = ImageFactory::instance().open(path); if (pImage == 0) return -2; - int rc = pImage->eraseExifData(path); + // Read all metadata then erase only Exif data + int rc = pImage->readMetadata(); + if (rc == 0) { + pImage->clearExifData(); + rc = pImage->writeMetadata(); + } delete pImage; return rc; } // ExifData::erase @@ -670,23 +670,24 @@ namespace Exiv2 { // Remove the Exif section from the file if there is no metadata if (count() == 0 && !pThumbnail_) return erase(path); - std::ifstream is(path.c_str(), std::ios::binary); - if (!is) return -1; - Image* pImage = ImageFactory::instance().create(is); - is.close(); + Image* pImage = ImageFactory::instance().open(path); if (pImage == 0) return -2; DataBuf buf(size()); long actualSize = copy(buf.pData_); assert(actualSize <= buf.size_); - pImage->setExifData(buf.pData_, actualSize); - int rc = pImage->writeExifData(path); + // Read all metadata to preserve non-Exif data + int rc = pImage->readMetadata(); + if (rc == 0) { + pImage->setExifData(buf.pData_, actualSize); + rc = pImage->writeMetadata(); + } delete pImage; return rc; } // ExifData::write - long ExifData::copy(char* buf) + long ExifData::copy(byte* buf) { long size = 0; // If we can update the internal IFDs and the underlying data buffer @@ -709,7 +710,7 @@ namespace Exiv2 { return size; } - long ExifData::copyFromMetadata(char* buf) + long ExifData::copyFromMetadata(byte* buf) { // Build IFD0 Ifd ifd0(ifd0); @@ -873,9 +874,10 @@ namespace Exiv2 { long actualSize = copy(buf.pData_); assert(actualSize <= buf.size_); - ExvFile exvFile; - exvFile.setExifData(buf.pData_, actualSize); - return exvFile.writeExifData(path); + ExvImage exvImage(path, true); + if (!exvImage.good()) return -2; + exvImage.setExifData(buf.pData_, actualSize); + return exvImage.writeMetadata(); } // ExifData::writeExifData void ExifData::add(Entries::const_iterator begin, diff --git a/src/exif.hpp b/src/exif.hpp index e4d50ba7..9692a99b 100644 --- a/src/exif.hpp +++ b/src/exif.hpp @@ -21,7 +21,7 @@ /*! @file exif.hpp @brief Encoding and decoding of Exif data - @version $Name: $ $Revision: 1.47 $ + @version $Name: $ $Revision: 1.48 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created @@ -126,7 +126,7 @@ namespace Exiv2 { @param byteOrder Applicable byte order (little or big endian). @return Number of characters written. */ - long copy(char* buf, ByteOrder byteOrder) const + long copy(byte* buf, ByteOrder byteOrder) const { return pValue_ == 0 ? 0 : pValue_->copy(buf, byteOrder); } /*! @brief Return the key of the metadatum. The key is of the form @@ -274,7 +274,7 @@ namespace Exiv2 { 1 in case of inconsistent JPEG thumbnail Exif data
2 in case of inconsistent TIFF thumbnail Exif data */ - virtual int read(const char* buf, + virtual int read(const byte* buf, long len, const ExifData& exifData, ByteOrder byteOrder =littleEndian) =0; @@ -319,7 +319,7 @@ namespace Exiv2 { enough memory. Otherwise the call results in undefined behaviour. Return the number of characters written. */ - virtual long copy(char* buf) const =0; + virtual long copy(byte* buf) const =0; /*! @brief Update the Exif data according to the actual thumbnail image. @@ -374,7 +374,7 @@ namespace Exiv2 { //@{ //! Assignment operator. TiffThumbnail& operator=(const TiffThumbnail& rhs); - int read(const char* buf, + int read(const byte* buf, long len, const ExifData& exifData, ByteOrder byteOrder =littleEndian); @@ -386,7 +386,7 @@ namespace Exiv2 { int write(const std::string& path) const; const char* format() const; const char* extension() const; - long copy(char* buf) const; + long copy(byte* buf) const; void update(ExifData& exifData) const; long offset() const; long size() const; @@ -398,7 +398,7 @@ namespace Exiv2 { long offset_; // Original offset of the thumbnail data // from the start of the TIFF header long size_; //!< Size of the image data - char* pImage_; //!< Thumbnail image data + byte* pImage_; //!< Thumbnail image data TiffHeader tiffHeader_; //!< Thumbnail TIFF Header Ifd ifd_; //!< Thumbnail IFD (IFD1 of the Exif data) @@ -421,7 +421,7 @@ namespace Exiv2 { //@{ //! Assignment operator. JpegThumbnail& operator=(const JpegThumbnail& rhs); - int read(const char* buf, + int read(const byte* buf, long len, const ExifData& exifData, ByteOrder byteOrder =littleEndian); @@ -433,7 +433,7 @@ namespace Exiv2 { int write(const std::string& path) const; const char* format() const; const char* extension() const; - long copy(char* buf) const; + long copy(byte* buf) const; void update(ExifData& exifData) const; long offset() const; long size() const; @@ -445,7 +445,7 @@ namespace Exiv2 { long offset_; // Original offset of the thumbnail data // from the start of the TIFF header long size_; // Size of the image data - char* pImage_; // Thumbnail image data + byte* pImage_; // Thumbnail image data }; // class JpegThumbnail @@ -543,13 +543,13 @@ namespace Exiv2 { @param len Number of bytes in the data buffer @return 0 if successful. */ - int read(const char* buf, long len); + int read(const byte* buf, long len); /*! @brief Write the Exif data to file path. If an Exif data section already exists in the file, it is replaced. If there is no metadata and no thumbnail to write, the Exif data section is deleted from the file. Otherwise, an Exif data section is - created. See copy(char* buf) for further details. + created. See copy(byte* buf) for further details. @return 0 if successful. */ @@ -585,7 +585,7 @@ namespace Exiv2 { undefined behaviour. @return Number of characters written to the buffer. */ - long copy(char* buf); + long copy(byte* buf); /*! @brief Add all (IFD) entries in the range from iterator position begin to iterator position end to the Exif metadata. No duplicate @@ -782,7 +782,7 @@ namespace Exiv2 { the Exif data with the metadata from the actual thumbnail image (overriding existing metadata). */ - long copyFromMetadata(char* buf); + long copyFromMetadata(byte* buf); //@} //! @name Accessors @@ -829,7 +829,7 @@ namespace Exiv2 { Ifd ifd1_; long size_; //!< Size of the Exif raw data in bytes - char* pData_; //!< Exif raw data buffer + byte* pData_; //!< Exif raw data buffer /*! Can be set to false to indicate that non-intrusive writing is not diff --git a/src/exifcomment.cpp b/src/exifcomment.cpp index 3125f393..61c93d7b 100644 --- a/src/exifcomment.cpp +++ b/src/exifcomment.cpp @@ -3,7 +3,7 @@ Abstract : Sample program showing how to set the Exif comment of an image File: exifcomment.cpp - Version : $Name: $ $Revision: 1.2 $ + Version : $Name: $ $Revision: 1.3 $ Author(s): Andreas Huggel (ahu) History : 10-May-04, ahu: created */ @@ -56,7 +56,8 @@ try { std::string charset("ASCII\0\0\0", 8); std::string comment("A comment added to the Exif metadata through Exiv2"); Exiv2::DataValue value; - value.read((charset + comment).data(), 8 + comment.size()); + value.read(reinterpret_cast((charset + comment).data()), + 8 + static_cast(comment.size())); // Set the Exif comment std::string key = "Image.UserInfo.UserComment"; diff --git a/src/fujimn.cpp b/src/fujimn.cpp index 73b1b934..9b49e269 100644 --- a/src/fujimn.cpp +++ b/src/fujimn.cpp @@ -20,7 +20,7 @@ */ /* File: fujimn.cpp - Version: $Name: $ $Revision: 1.7 $ + Version: $Name: $ $Revision: 1.8 $ Author(s): Andreas Huggel (ahu) History: 18-Feb-04, ahu: created 07-Mar-04, ahu: isolated as a separate component @@ -31,7 +31,7 @@ */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.7 $ $RCSfile: fujimn.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.8 $ $RCSfile: fujimn.cpp,v $") // ***************************************************************************** // included header files @@ -84,7 +84,7 @@ namespace Exiv2 { absOffset_ = false; } - int FujiMakerNote::readHeader(const char* buf, + int FujiMakerNote::readHeader(const byte* buf, long len, ByteOrder byteOrder) { @@ -103,7 +103,8 @@ namespace Exiv2 { int rc = 0; // Check the FUJIFILM prefix if ( header_.size_ < 12 - || std::string(header_.pData_, 8) != std::string("FUJIFILM", 8)) { + || std::string(reinterpret_cast(header_.pData_), 8) + != std::string("FUJIFILM", 8)) { rc = 2; } return rc; @@ -255,7 +256,7 @@ namespace Exiv2 { // free functions MakerNote* createFujiMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset) diff --git a/src/fujimn.hpp b/src/fujimn.hpp index 09e78582..e6ce75a4 100644 --- a/src/fujimn.hpp +++ b/src/fujimn.hpp @@ -24,7 +24,7 @@ in Appendix 4: Makernote of Fujifilm of the document Exif file format by TsuruZoh Tachibanaya - @version $Name: $ $Revision: 1.5 $ + @version $Name: $ $Revision: 1.6 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 11-Feb-04, ahu: created @@ -73,7 +73,7 @@ namespace Exiv2 { this copy and is responsible to delete it! */ MakerNote* createFujiMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset); @@ -97,7 +97,7 @@ namespace Exiv2 { //! @name Manipulators //@{ - int readHeader(const char* buf, + int readHeader(const byte* buf, long len, ByteOrder byteOrder); //@} diff --git a/src/getopt_win32.c b/src/getopt_win32.c index d79967d2..5adc39a7 100644 --- a/src/getopt_win32.c +++ b/src/getopt_win32.c @@ -109,7 +109,7 @@ char *alloca (); GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ -#include "getopt.h" +#include "getopt_win32.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, diff --git a/src/ifd-test.cpp b/src/ifd-test.cpp index 8c7504d8..85272eba 100644 --- a/src/ifd-test.cpp +++ b/src/ifd-test.cpp @@ -16,7 +16,7 @@ int main() try { long len = 76; - char buf[] + Exiv2::byte buf[] = { // No 0x00,0x04, // Tag Type Components Offset/Data @@ -45,7 +45,7 @@ try { std::cout << "Tag not found!\n"; return 1; } - char data[] = { 'T', 'H', 'R', 'E', 'E', '\0' }; + Exiv2::byte data[] = { 'T', 'H', 'R', 'E', 'E', '\0' }; std::cout << "Setting value of entry 3...\n"; pos->setValue(2, 6, data, 6); diff --git a/src/ifd.cpp b/src/ifd.cpp index 244aa5ba..32088d3a 100644 --- a/src/ifd.cpp +++ b/src/ifd.cpp @@ -20,14 +20,14 @@ */ /* File: ifd.cpp - Version: $Name: $ $Revision: 1.23 $ + Version: $Name: $ $Revision: 1.24 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.23 $ $RCSfile: ifd.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.24 $ $RCSfile: ifd.cpp,v $") // ***************************************************************************** // included header files @@ -68,7 +68,7 @@ namespace Exiv2 { { if (alloc_) { if (rhs.pData_) { - pData_ = new char[rhs.size()]; + pData_ = new byte[rhs.size()]; memcpy(pData_, rhs.pData_, rhs.size()); } } @@ -93,7 +93,7 @@ namespace Exiv2 { delete[] pData_; pData_ = 0; if (rhs.pData_) { - pData_ = new char[rhs.size()]; + pData_ = new byte[rhs.size()]; memcpy(pData_, rhs.pData_, rhs.size()); } } @@ -109,7 +109,7 @@ namespace Exiv2 { assert(alloc_); size_ = 4; delete[] pData_; - pData_ = new char[size_]; + pData_ = new byte[size_]; } ul2Data(pData_, data, byteOrder); // do not change size_ @@ -117,7 +117,7 @@ namespace Exiv2 { count_ = 1; } - void Entry::setValue(uint16 type, uint32 count, const char* buf, long len) + void Entry::setValue(uint16 type, uint32 count, const byte* buf, long len) { long dataSize = count * TypeInfo::typeSize(TypeId(type)); // No minimum size requirement, but make sure the buffer can hold the data @@ -126,7 +126,7 @@ namespace Exiv2 { } if (alloc_) { delete[] pData_; - pData_ = new char[len]; + pData_ = new byte[len]; memset(pData_, 0x0, len); memcpy(pData_, buf, dataSize); size_ = len; @@ -134,7 +134,7 @@ namespace Exiv2 { else { if (size_ == 0) { // Set the data pointer of a virgin entry - pData_ = const_cast(buf); + pData_ = const_cast(buf); size_ = len; } else { @@ -149,7 +149,7 @@ namespace Exiv2 { count_ = count; } // Entry::setValue - const char* Entry::component(uint32 n) const + const byte* Entry::component(uint32 n) const { if (n >= count()) return 0; return data() + n * typeSize(); @@ -159,7 +159,7 @@ namespace Exiv2 { : alloc_(true), ifdId_(ifdId), offset_(0), dataOffset_(0), pNext_(0), next_(0) { - pNext_ = new char[4]; + pNext_ = new byte[4]; memset(pNext_, 0x0, 4); } @@ -167,7 +167,7 @@ namespace Exiv2 { : alloc_(true), ifdId_(ifdId), offset_(offset), dataOffset_(0), pNext_(0), next_(0) { - pNext_ = new char[4]; + pNext_ = new byte[4]; memset(pNext_, 0x0, 4); } @@ -176,7 +176,7 @@ namespace Exiv2 { pNext_(0), next_(0) { if (alloc_) { - pNext_ = new char[4]; + pNext_ = new byte[4]; memset(pNext_, 0x0, 4); } } @@ -192,12 +192,12 @@ namespace Exiv2 { pNext_(rhs.pNext_), next_(rhs.next_) { if (alloc_ && rhs.pNext_) { - pNext_ = new char[4]; + pNext_ = new byte[4]; memcpy(pNext_, rhs.pNext_, 4); } } - int Ifd::read(const char* buf, long len, ByteOrder byteOrder, long offset) + int Ifd::read(const byte* buf, long len, ByteOrder byteOrder, long offset) { int rc = 0; long o = 0; @@ -242,7 +242,7 @@ namespace Exiv2 { memcpy(pNext_, buf + o, 4); } else { - pNext_ = const_cast(buf + o); + pNext_ = const_cast(buf + o); } next_ = getULong(buf + o, byteOrder); } @@ -356,7 +356,7 @@ namespace Exiv2 { } int Ifd::readSubIfd( - Ifd& dest, const char* buf, long len, ByteOrder byteOrder, uint16 tag + Ifd& dest, const byte* buf, long len, ByteOrder byteOrder, uint16 tag ) const { int rc = 0; @@ -373,7 +373,7 @@ namespace Exiv2 { return rc; } // Ifd::readSubIfd - long Ifd::copy(char* buf, ByteOrder byteOrder, long offset) + long Ifd::copy(byte* buf, ByteOrder byteOrder, long offset) { if (offset != 0) offset_ = offset; @@ -508,7 +508,7 @@ namespace Exiv2 { << std::hex << std::right << i->offset(); } else { - unsigned char* data = (unsigned char*)i->data(); + const byte* data = i->data(); for (int k = 0; k < i->size(); ++k) { offset << std::setw(2) << std::setfill('0') << std::hex << (int)data[k] << " "; diff --git a/src/ifd.hpp b/src/ifd.hpp index 4a096b08..6d01f16b 100644 --- a/src/ifd.hpp +++ b/src/ifd.hpp @@ -21,10 +21,10 @@ /*! @file ifd.hpp @brief Encoding and decoding of IFD (%Image File Directory) data - @version $Name: $ $Revision: 1.19 $ + @version $Name: $ $Revision: 1.20 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 09-Jan-04, ahu: created + @date 09-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component */ #ifndef IFD_HPP_ @@ -126,7 +126,7 @@ namespace Exiv2 { @throw Error ("Size too small") if size is not large enough to hold count components of the given type. */ - void setValue(uint16 type, uint32 count, const char* data, long size); + void setValue(uint16 type, uint32 count, const byte* data, long size); //@} //! @name Accessors @@ -161,12 +161,12 @@ namespace Exiv2 { @brief Return a pointer to the data area. Do not attempt to write to this pointer. */ - const char* data() const { return pData_; } + const byte* data() const { return pData_; } /*! @brief Return a pointer to the n-th component, 0 if there is no n-th component. Do not attempt to write to this pointer. */ - const char* component(uint32 n) const; + const byte* component(uint32 n) const; //! Get the memory allocation mode bool alloc() const { return alloc_; } //@} @@ -198,7 +198,7 @@ namespace Exiv2 { */ long size_; //! Pointer to the data buffer - char* pData_; + byte* pData_; }; // class Entry @@ -323,7 +323,7 @@ namespace Exiv2 { beyond the provided buffer. The IFD is cleared in this case. */ - int read(const char* buf, long len, ByteOrder byteOrder, long offset =0); + int read(const byte* buf, long len, ByteOrder byteOrder, long offset =0); /*! @brief Read a sub-IFD from the location pointed to by the directory entry with the given tag. @@ -344,7 +344,7 @@ namespace Exiv2 { IFD. 0 is returned and no action is taken in this case. */ int readSubIfd( - Ifd& dest, const char* buf, long len, ByteOrder byteOrder, uint16 tag + Ifd& dest, const byte* buf, long len, ByteOrder byteOrder, uint16 tag ) const; /*! @brief Copy the IFD to a data array, update the offsets of the IFD and @@ -371,7 +371,7 @@ namespace Exiv2 { original position, i.e., the offset of the IFD will be used. @return Returns the number of characters written. */ - long copy(char* buf, ByteOrder byteOrder, long offset =0); + long copy(byte* buf, ByteOrder byteOrder, long offset =0); /*! @brief Reset the IFD. Delete all IFD entries from the class and put the object in a state where it can accept completely new @@ -489,7 +489,7 @@ namespace Exiv2 { //! Offset of the first data entry outside of the IFD directory long dataOffset_; //! Pointer to the offset of next IFD from the start of the TIFF header - char* pNext_; + byte* pNext_; //! The offset of the next IFD as data value (always in sync with *pNext_) uint32 next_; diff --git a/src/image.cpp b/src/image.cpp index 58376f40..7e8cb3fd 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -20,14 +20,16 @@ */ /* File: image.cpp - Version: $Name: $ $Revision: 1.18 $ + Version: $Name: $ $Revision: 1.19 $ Author(s): Andreas Huggel (ahu) + Brad Schick (brad) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component + 19-Jul-04, brad: revamped to be more flexible and support IPTC */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: image.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.19 $ $RCSfile: image.cpp,v $") // ***************************************************************************** // included header files @@ -37,10 +39,10 @@ EXIV2_RCSID("@(#) $Name: $ $Revision: 1.18 $ $RCSfile: image.cpp,v $") #include "types.hpp" // + standard includes -#include -#include +#include #include #include // for rename, remove +#include #ifdef _MSC_VER #include typedef int pid_t; @@ -51,10 +53,19 @@ typedef int pid_t; #endif #endif + // ***************************************************************************** // class member definitions namespace Exiv2 { + // Local functions. These could be static private functions on Image + // subclasses but then ImageFactory needs to be made a friend. + Image* newExvInstance(const std::string& path, FILE* fp); + bool isExvType(FILE* ifp, bool advance); + Image* newJpegInstance(const std::string& path, FILE* fp); + bool isJpegType(FILE* ifp, bool advance); + + ImageFactory* ImageFactory::pInstance_ = 0; ImageFactory& ImageFactory::instance() @@ -65,376 +76,715 @@ namespace Exiv2 { return *pInstance_; } // ImageFactory::instance - void ImageFactory::registerImage(Image::Type type, Image* pImage) + void ImageFactory::registerImage(Image::Type type, + NewInstanceFct newInst, IsThisTypeFct isType) { - Registry::iterator i = registry_.find(type); - if (i != registry_.end()) { - delete i->second; - } - registry_[type] = pImage; + assert (newInst && isType); + registry_[type] = ImageFcts(newInst, isType); } // ImageFactory::registerImage ImageFactory::ImageFactory() { // Register a prototype of each known image - registerImage(Image::none, 0); - registerImage(Image::jpeg, new JpegImage); + registerImage(Image::jpeg, newJpegInstance, isJpegType); + registerImage(Image::exv, newExvInstance, isExvType); } // ImageFactory c'tor - Image* ImageFactory::create(std::istream& is) const + Image::Type ImageFactory::getType(const std::string& path) const { + FILE* ifp = fopen(path.c_str(), "rb"); + if (!ifp) return Image::none; + + Image::Type type = Image::none; Registry::const_iterator b = registry_.begin(); Registry::const_iterator e = registry_.end(); for (Registry::const_iterator i = b; i != e; ++i) { - if (i->second != 0 && i->second->isThisType(is)) { - Image* pImage = i->second; - return pImage->clone(); + if (i->second.isThisType(ifp, false)) { + type = i->first; + break; } } - return 0; - } // ImageFactory::create + fclose(ifp); + return type; + } // ImageFactory::getType + + Image* ImageFactory::open(const std::string& path) const + { + FILE* ifp = fopen(path.c_str(), "rb"); + if (!ifp) return 0; - Image* ImageFactory::create(Image::Type type) const + Image* pImage = 0; + Registry::const_iterator b = registry_.begin(); + Registry::const_iterator e = registry_.end(); + for (Registry::const_iterator i = b; i != e; ++i) + { + if (i->second.isThisType(ifp, false)) { + pImage = i->second.newInstance(path, ifp); + break; + } + } + return pImage; + } // ImageFactory::open + + Image* ImageFactory::create(Image::Type type, const std::string& path) const { Registry::const_iterator i = registry_.find(type); - if (i != registry_.end() && i->second != 0) { - Image* pImage = i->second; - return pImage->clone(); + if (i != registry_.end()) { + return i->second.newInstance(path, 0); } return 0; } // ImageFactory::create - JpegImage::JpegImage() - : sizeExifData_(0), pExifData_(0) - { - } - JpegImage::JpegImage(const JpegImage& rhs) - : Image(rhs), sizeExifData_(0), pExifData_(0) + const byte JpegBase::sos_ = 0xda; + const byte JpegBase::eoi_ = 0xd9; + const byte JpegBase::app0_ = 0xe0; + const byte JpegBase::app1_ = 0xe1; + const byte JpegBase::app13_ = 0xed; + const byte JpegBase::com_ = 0xfe; + const uint16 JpegBase::iptc_ = 0x0404; + const char JpegBase::exifId_[] = "Exif\0\0"; + const char JpegBase::jfifId_[] = "JFIF\0"; + const char JpegBase::ps3Id_[] = "Photoshop 3.0\0"; + const char JpegBase::bimId_[] = "8BIM"; + + JpegBase::JpegBase(const std::string& path, const bool create, + const byte initData[], const size_t dataSize) : fp_(0), path_(path), + sizeExifData_(0), pExifData_(0), sizeIptcData_(0), pIptcData_(0) { - char* newExifData = 0; - if (rhs.sizeExifData_ > 0) { - char* newExifData = new char[rhs.sizeExifData_]; - memcpy(newExifData, rhs.pExifData_, rhs.sizeExifData_); + if (create) { + fp_ = fopen(path.c_str(), "w+b"); + if (fp_) initFile(initData, dataSize); + } + else { + fp_ = fopen(path.c_str(), "rb"); } - pExifData_ = newExifData; - sizeExifData_ = rhs.sizeExifData_; + } + + JpegBase::JpegBase(const std::string& path, FILE* fp) + : fp_(fp), path_(path), sizeExifData_(0), + pExifData_(0), sizeIptcData_(0), pIptcData_(0) + { + assert(fp_); } - JpegImage& JpegImage::operator=(const JpegImage& rhs) + int JpegBase::initFile(const byte initData[], const size_t dataSize) { - if (this == &rhs) return *this; - char* newExifData = 0; - if (rhs.sizeExifData_ > 0) { - char* newExifData = new char[rhs.sizeExifData_]; - memcpy(newExifData, rhs.pExifData_, rhs.sizeExifData_); + if (!fp_ || ferror(fp_)) return 3; + if (fwrite(initData, 1, dataSize, fp_) != dataSize) { + return 3; + } + fseek(fp_, 0, SEEK_SET); + if (ferror(fp_)) { + return 3; } - Image::operator=(rhs); - pExifData_ = newExifData; - sizeExifData_ = rhs.sizeExifData_; - return *this; + return 0; } - JpegImage::~JpegImage() + JpegBase::~JpegBase() { + if (fp_) fclose(fp_); delete[] pExifData_; + delete[] pIptcData_; } - const uint16 JpegImage::soi_ = 0xffd8; - const uint16 JpegImage::app0_ = 0xffe0; - const uint16 JpegImage::app1_ = 0xffe1; - const char JpegImage::exifId_[] = "Exif\0\0"; - const char JpegImage::jfifId_[] = "JFIF\0"; + int JpegBase::detach() + { + if (fp_) fclose(fp_); + fp_ = 0; + return 0; + } - int JpegImage::readExifData(const std::string& path) + bool JpegBase::good() const { - std::ifstream file(path.c_str(), std::ios::binary); - if (!file) return -1; - return readExifData(file); + if (fp_ == 0) return false; + rewind(fp_); + return isThisType(fp_, false); } - // Todo: implement this properly: skip unknown APP0 and APP1 segments - int JpegImage::readExifData(std::istream& is) + void JpegBase::clearMetadata() { - // Check if this is a JPEG image in the first place - if (!isThisType(is, true)) { - if (!is.good()) return 1; - return 2; + clearIptcData(); + clearExifData(); + clearComment(); + } + + void JpegBase::clearIptcData() + { + delete[] pIptcData_; + pIptcData_ = 0; + sizeIptcData_ = 0; + } + + void JpegBase::clearExifData() + { + delete[] pExifData_; + pExifData_ = 0; + sizeExifData_ = 0; + } + + void JpegBase::clearComment() + { + comment_.erase(); + } + + void JpegBase::setExifData(const byte* buf, long size) + { + clearExifData(); + if (size) { + sizeExifData_ = size; + pExifData_ = new byte[size]; + memcpy(pExifData_, buf, size); } + } - // Read and check section marker and size - char tmpbuf[10]; - is.read(tmpbuf, 10); - if (!is.good()) return 1; - uint16 marker = getUShort(tmpbuf, bigEndian); - uint16 size = getUShort(tmpbuf + 2, bigEndian); - if (size < 8) return 3; - if (marker == app0_ && memcmp(tmpbuf + 4, jfifId_, 5) == 0) { - // Skip the remainder of the JFIF APP0 segment - is.seekg(size - 8, std::ios::cur); - // Read the beginning of the next segment - is.read(tmpbuf, 10); - if (!is.good()) return 1; - marker = getUShort(tmpbuf, bigEndian); - size = getUShort(tmpbuf + 2, bigEndian); - } - if (!(marker == app1_ && memcmp(tmpbuf + 4, exifId_, 6) == 0)) return 3; - - // Read the rest of the APP1 field (Exif data) - long sizeExifData = size - 8; - pExifData_ = new char[sizeExifData]; - is.read(pExifData_, sizeExifData); - if (!is.good()) { - delete[] pExifData_; - pExifData_ = 0; - return 1; + void JpegBase::setIptcData(const byte* buf, long size) + { + clearIptcData(); + if (size) { + sizeIptcData_ = size; + pIptcData_ = new byte[size]; + memcpy(pIptcData_, buf, size); } - // Finally, set the size and offset of the Exif data buffer - sizeExifData_ = sizeExifData; + } - return 0; - } // JpegImage::readExifData + void JpegBase::setComment(const std::string& comment) + { + comment_ = comment; + } - int JpegImage::eraseExifData(const std::string& path) const + void JpegBase::setMetadata(const Image& image) { - // Open input file - std::ifstream is(path.c_str(), std::ios::binary); - if (!is) return -1; + setIptcData(image.iptcData(), image.sizeIptcData()); + setExifData(image.exifData(), image.sizeExifData()); + setComment(image.comment()); + } - // Write the output to a temporary file - pid_t pid = getpid(); - std::string tmpname = path + toString(pid); - std::ofstream os(tmpname.c_str(), std::ios::binary); - if (!os) return -3; - - int rc = eraseExifData(os, is); - os.close(); - is.close(); - if (rc == 0) { - // Workaround for MinGW rename that does not overwrite existing files - if (remove(path.c_str()) != 0) rc = -4; - } - if (rc == 0) { - // rename temporary file - if (rename(tmpname.c_str(), path.c_str()) == -1) rc = -4; + int JpegBase::advanceToMarker() const + { + int c = -1; + // Skips potential padding between markers + while ((c=fgetc(fp_)) != 0xff) { + if (c == EOF) return -1; } - if (rc != 0) { - // remove temporary file - remove(tmpname.c_str()); + + // Markers can start with any number of 0xff + while ((c=fgetc(fp_)) == 0xff) { + if (c == EOF) return -1; } + return c; + } - return rc; - } // JpegImage::eraseExifData - - // Todo: implement this properly: skip unknown APP0 and APP1 segments - int JpegImage::eraseExifData(std::ostream& os, std::istream& is) const + int JpegBase::readMetadata() { - // Check if this is a JPEG image in the first place - if (!isThisType(is, true)) { - if (!is.good()) return 1; + if (!fp_) return 1; + rewind(fp_); + + // Ensure that this is the correct image type + if (!isThisType(fp_, true)) { + if (ferror(fp_) || feof(fp_)) return 1; return 2; } - // Read and check section marker and size - char tmpbuf[11]; - is.read(tmpbuf, 10); - if (!is.good()) return 1; - uint16 marker = getUShort(tmpbuf, bigEndian); - uint16 size = getUShort(tmpbuf + 2, bigEndian); - if (size < 8) return 3; - - const long defaultJfifSize = 9; - static const char defaultJfifData[] = - { 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00 }; - DataBuf jfif; - - if (marker == app0_ && memcmp(tmpbuf + 4, jfifId_, 5) == 0) { - // Read the remainder of the JFIF APP0 segment - is.seekg(-1, std::ios::cur); - jfif.alloc(size - 7); - is.read(jfif.pData_, jfif.size_); - if (!is.good()) return 1; + clearMetadata(); + int search = 3; + const long bufMinSize = 16; + long bufRead = 0; + DataBuf buf(bufMinSize); + + // Read section marker + int marker = advanceToMarker(); + if (marker < 0) return 1; + + while (marker != sos_ && marker != eoi_ && search > 0) { + // Read size and signature (ok if this hits EOF) + bufRead = (long)fread(buf.pData_, 1, bufMinSize, fp_); + if (ferror(fp_)) return 1; + uint16 size = getUShort(buf.pData_, bigEndian); + + if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) { + if (size < 8) return 2; + // Seek to begining and read the Exif data + fseek(fp_, 8-bufRead, SEEK_CUR); + long sizeExifData = size - 8; + pExifData_ = new byte[sizeExifData]; + fread(pExifData_, 1, sizeExifData, fp_); + if (ferror(fp_) || feof(fp_)) { + delete[] pExifData_; + pExifData_ = 0; + return 1; + } + // Set the size and offset of the Exif data buffer + sizeExifData_ = sizeExifData; + --search; + } + else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) { + if (size < 16) return 2; + // Read the rest of the APP13 segment + // needed if bufMinSize!=16: fseek(fp_, 16-bufRead, SEEK_CUR); + DataBuf psData(size - 16); + fread(psData.pData_, 1, psData.size_, fp_); + if (ferror(fp_) || feof(fp_)) return 1; + const byte *record = 0; + uint16 sizeIptc = 0; + uint16 sizeHdr = 0; + // Find actual Iptc data within the APP13 segment + if (!locateIptcData(psData.pData_, psData.size_, &record, + &sizeHdr, &sizeIptc)) { + assert(sizeIptc); + sizeIptcData_ = sizeIptc; + pIptcData_ = new byte[sizeIptc]; + memcpy( pIptcData_, record + sizeHdr, sizeIptc ); + } + --search; + } + else if (marker == com_ && comment_.empty()) + { + if (size < 2) return 2; + // Jpegs can have multiple comments, but for now only read + // the first one (most jpegs only have one anyway). Comments + // are simple single byte ISO-8859-1 strings. + fseek(fp_, 2-bufRead, SEEK_CUR); + buf.alloc(size-2); + fread(buf.pData_, 1, size-2, fp_); + if (ferror(fp_) || feof(fp_)) return 1; + comment_.assign(reinterpret_cast(buf.pData_), size-2); + while (comment_.length() && comment_.at(comment_.length()-1) == '\0') { + comment_.erase(comment_.length()-1); + } + --search; + } + else { + if (size < 2) return 2; + // Skip the remainder of the unknown segment + if (fseek(fp_, size-bufRead, SEEK_CUR)) return 2; + } // Read the beginning of the next segment - is.read(tmpbuf, 10); - if (!is.good()) return 1; - marker = getUShort(tmpbuf, bigEndian); - size = getUShort(tmpbuf + 2, bigEndian); - } - // No Exif data found - if (!(marker == app1_ && memcmp(tmpbuf + 4, exifId_, 6) == 0)) return 3; - // Skip the rest of the Exif APP1 segment - is.seekg(size - 8, std::ios::cur); - if (!is.good()) return 1; - - // Write SOI and APP0 markers, size of APP0 field - us2Data(tmpbuf, soi_, bigEndian); - us2Data(tmpbuf + 2, app0_, bigEndian); - if (jfif.pData_) { - us2Data(tmpbuf + 4, static_cast(7 + jfif.size_), bigEndian); - } - else { - us2Data(tmpbuf + 4, static_cast(7 + defaultJfifSize), bigEndian); + marker = advanceToMarker(); + if (marker < 0) return 1; } - memcpy(tmpbuf + 6, jfifId_, 5); - os.write(tmpbuf, 11); - // Write JFIF APP0 data, use that from the input stream if available - if (jfif.pData_) { - os.write(jfif.pData_, jfif.size_); - } - else { - os.write(defaultJfifData, defaultJfifSize); - } - if (!os.good()) return 4; + return 0; + } // JpegBase::readMetadata - // Copy the rest of the input stream - os.flush(); - is >> os.rdbuf(); - if (!os.good()) return 4; - return 0; - } // JpegImage::eraseExifData + // Operates on raw data (rather than file streams) to simplify reuse + int JpegBase::locateIptcData(const byte *pPSData, + long sizePSData, + const byte **record, + uint16 *const sizeHdr, + uint16 *const sizeIptc) const + { + assert(record); + assert(sizeHdr); + assert(sizeIptc); + // Used for error checking + long position = 0; + + // Data should follow Photoshop format, if not exit + while (position <= (sizePSData - 14) && + memcmp(pPSData + position, bimId_, 4)==0) { + const byte *hrd = pPSData + position; + position += 4; + uint16 type = getUShort(pPSData+ position, bigEndian); + position += 2; + + // Pascal string is padded to have an even size (including size byte) + byte psSize = pPSData[position] + 1; + psSize += (psSize & 1); + position += psSize; + if (position >= sizePSData) return 1; + + // Data is also padded to be even + long dataSize = getULong(pPSData + position, bigEndian); + position += 4; + if (dataSize > sizePSData - position) return 1; + + if (type == iptc_) { + *sizeIptc = static_cast(dataSize); + *sizeHdr = psSize + 10; + *record = hrd; + return 0; + } + position += dataSize + (dataSize & 1); + } + return 2; + } // JpegBase::locateIptcData - int JpegImage::writeExifData(const std::string& path) const + int JpegBase::writeMetadata() { - // Open the input file - std::ifstream is(path.c_str(), std::ios::binary); - if (!is) return -1; + if (!fp_) return 1; + rewind(fp_); // Write the output to a temporary file - pid_t pid = getpid(); - std::string tmpname = path + toString(pid); - std::ofstream os(tmpname.c_str(), std::ios::binary); - if (!os) return -3; - - int rc = writeExifData(os, is); - os.close(); - is.close(); + std::string tmpname(tmpnam(0)); + FILE* ofl = fopen(tmpname.c_str(), "wb"); + if (!ofl) return 3; + + int rc = doWriteMetadata(ofl); + fclose(ofl); + fclose(fp_); if (rc == 0) { - // Workaround for MinGW rename that does not overwrite existing files - if (remove(path.c_str()) != 0) rc = -4; + // Workaround for MSVCRT rename that does not overwrite existing files + if (remove(path_.c_str()) != 0) rc = 4; } if (rc == 0) { // rename temporary file - if (rename(tmpname.c_str(), path.c_str()) == -1) rc = -4; + if (rename(tmpname.c_str(), path_.c_str()) == -1) rc = 4; } if (rc != 0) { // remove temporary file remove(tmpname.c_str()); } + // Reopen the file + fp_ = fopen(path_.c_str(), "rb"); + if (!fp_) return 1; return rc; - } // JpegImage::writeExifData + } // JpegBase::writeMetadata - // Todo: implement this properly: skip unknown APP0 and APP1 segments - int JpegImage::writeExifData(std::ostream& os, std::istream& is) const + int JpegBase::doWriteMetadata(FILE* ofp) const { - // Check if this is a JPEG image in the first place - if (!isThisType(is, true)) { - if (!is.good()) return 1; + if (!fp_) return 1; + // Ensure that this is the correct image type + if (!isThisType(fp_, true)) { + if (ferror(fp_) || feof(fp_)) return 1; return 2; } - - // Read and check section marker and size - char tmpbuf[10]; - is.read(tmpbuf, 10); - if (!is.good()) return 1; - uint16 marker = getUShort(tmpbuf, bigEndian); - uint16 size = getUShort(tmpbuf + 2, bigEndian); - if (size < 8) return 3; - - bool validFile = false; - DataBuf jfif; - if (marker == app0_ && memcmp(tmpbuf + 4, jfifId_, 5) == 0) { - // Read the remainder of the JFIF APP0 segment - is.seekg(-1, std::ios::cur); - jfif.alloc(size -7); - is.read(jfif.pData_, jfif.size_); - if (!is.good()) return 1; - // Read the beginning of the next segment - is.read(tmpbuf, 10); - if (!is.good()) return 1; - marker = getUShort(tmpbuf, bigEndian); - size = getUShort(tmpbuf + 2, bigEndian); - validFile = true; - } - if (marker == app1_ && memcmp(tmpbuf + 4, exifId_, 6) == 0) { - // Skip the rest of the Exif APP1 segment - is.seekg(size - 8, std::ios::cur); - if (!is.good()) return 1; - validFile = true; - } - else { - is.seekg(-10, std::ios::cur); - } - if (!validFile) return 5; - // Write SOI marker - us2Data(tmpbuf, soi_, bigEndian); - os.write(tmpbuf, 2); - if (!os.good()) return 4; - if (jfif.pData_) { - // Write APP0 marker, size of APP0 field and JFIF data - us2Data(tmpbuf, app0_, bigEndian); - us2Data(tmpbuf + 2, static_cast(7 + jfif.size_), bigEndian); - memcpy(tmpbuf + 4, jfifId_, 5); - os.write(tmpbuf, 9); - os.write(jfif.pData_, jfif.size_); - if (!os.good()) { - return 4; + + const long bufMinSize = 16; + long bufRead = 0; + DataBuf buf(bufMinSize); + const long seek = ftell(fp_); + int count = 0; + int search = 0; + int insertPos = 0; + int skipApp1Exif = -1; + int skipApp13Ps3 = -1; + int skipCom = -1; + DataBuf psData; + + // Write image header + if (writeHeader(ofp)) return 3; + + // Read section marker + int marker = advanceToMarker(); + if (marker < 0) return 1; + + // First find segments of interest. Normally app0 is first and we want + // to insert after it. But if app0 comes after com, app1 and app13 then + // don't bother. + while (marker != sos_ && marker != eoi_ && search < 3) { + // Read size and signature (ok if this hits EOF) + bufRead = (long)fread(buf.pData_, 1, bufMinSize, fp_); + if (ferror(fp_)) return 1; + uint16 size = getUShort(buf.pData_, bigEndian); + + if (marker == app0_) { + if (size < 2) return 2; + insertPos = count + 1; + if (fseek(fp_, size-bufRead, SEEK_CUR)) return 2; + } + else if (marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) { + if (size < 8) return 2; + skipApp1Exif = count; + ++search; + if (fseek(fp_, size-bufRead, SEEK_CUR)) return 2; + } + else if (marker == app13_ && memcmp(buf.pData_ + 2, ps3Id_, 14) == 0) { + if (size < 16) return 2; + skipApp13Ps3 = count; + ++search; + // needed if bufMinSize!=16: fseek(fp_, 16-bufRead, SEEK_CUR); + psData.alloc(size - 16); + // Load PS data now to allow reinsertion at any point + fread(psData.pData_, 1, psData.size_, fp_); + if (ferror(fp_) || feof(fp_)) return 1; + } + else if (marker == com_ && skipCom == -1) + { + if (size < 2) return 2; + // Jpegs can have multiple comments, but for now only handle + // the first one (most jpegs only have one anyway). + skipCom = count; + ++search; + if (fseek(fp_, size-bufRead, SEEK_CUR)) return 2; + } + else { + if (size < 2) return 2; + if (fseek(fp_, size-bufRead, SEEK_CUR)) return 2; + } + marker = advanceToMarker(); + if (marker < 0) return 1; + ++count; + } + + if (pExifData_) ++search; + if (pIptcData_) ++search; + if (!comment_.empty()) ++search; + + fseek(fp_, seek, SEEK_SET); + count = 0; + marker = advanceToMarker(); + if (marker < 0) return 1; + + // To simplify this a bit, new segments are inserts at either the start + // or right after app0. This is standard in most jpegs, but has the + // potential to change segment ordering (which is allowed). + // Segments are erased if there is no assigned metadata. + while (marker != sos_ && search > 0) { + // Read size and signature (ok if this hits EOF) + bufRead = (long)fread(buf.pData_, 1, bufMinSize, fp_); + if (ferror(fp_)) return 1; + // Careful, this can be a meaningless number for empty + // images with only an eoi_ marker + uint16 size = getUShort(buf.pData_, bigEndian); + + if (insertPos == count) { + byte tmpBuf[18]; + if (!comment_.empty()) { + // Write COM marker, size of comment, and string + tmpBuf[0] = 0xff; + tmpBuf[1] = com_; + us2Data(tmpBuf + 2, + static_cast(comment_.length()+3), bigEndian); + if (fwrite(tmpBuf, 1, 4, ofp) != 4) return 3; + if (fwrite(comment_.data(), 1, comment_.length(), ofp) != comment_.length()) return 3; + if (fputc(0, ofp)==EOF) return 3; + if (ferror(ofp)) return 3; + --search; + } + if (pExifData_) { + // Write APP1 marker, size of APP1 field, Exif id and Exif data + tmpBuf[0] = 0xff; + tmpBuf[1] = app1_; + us2Data(tmpBuf + 2, static_cast(sizeExifData_+8), bigEndian); + memcpy(tmpBuf + 4, exifId_, 6); + if (fwrite(tmpBuf, 1, 10, ofp) != 10) return 3; + if (fwrite(pExifData_, 1, sizeExifData_, ofp) != (size_t)sizeExifData_) return 3; + if (ferror(ofp)) return 3; + --search; + } + + const byte *record = psData.pData_; + uint16 sizeIptc = 0; + uint16 sizeHdr = 0; + // Safe to call with zero psData.size_ + locateIptcData(psData.pData_, psData.size_, &record, &sizeHdr, &sizeIptc); + + // Data is rounded to be even + const int sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1); + if (psData.size_ > sizeOldData || pIptcData_) { + // write app13 marker, new size, and ps3Id + tmpBuf[0] = 0xff; + tmpBuf[1] = app13_; + const int sizeNewData = sizeIptcData_ ? + sizeIptcData_+(sizeIptcData_&1)+12 : 0; + us2Data(tmpBuf + 2, + static_cast(psData.size_-sizeOldData+sizeNewData+16), + bigEndian); + memcpy(tmpBuf + 4, ps3Id_, 14); + if (fwrite(tmpBuf, 1, 18, ofp) != 18) return 3; + if (ferror(ofp)) return 3; + + const long sizeFront = (long)(record - psData.pData_); + const long sizeEnd = psData.size_ - sizeFront - sizeOldData; + // write data before old record. + if (fwrite(psData.pData_, 1, sizeFront, ofp) != (size_t)sizeFront) return 3; + + // write new iptc record if we have it + if (pIptcData_) { + memcpy(tmpBuf, bimId_, 4); + us2Data(tmpBuf+4, iptc_, bigEndian); + tmpBuf[6] = 0; + tmpBuf[7] = 0; + ul2Data(tmpBuf + 8, sizeIptcData_, bigEndian); + if (fwrite(tmpBuf, 1, 12, ofp) != 12) return 3; + if (fwrite(pIptcData_, 1, sizeIptcData_ , ofp) != (size_t)sizeIptcData_) return 3; + // data is padded to be even (but not included in size) + if (sizeIptcData_ & 1 ) { + if (fputc(0, ofp)==EOF) return 3; + } + if (ferror(ofp)) return 3; + --search; + } + + // write existing stuff after record + if (fwrite(record+sizeOldData, 1, sizeEnd, ofp) != (size_t)sizeEnd) return 3; + if (ferror(ofp)) return 3; + } + } + if( marker == eoi_ ) { + break; } + else if (skipApp1Exif==count || skipApp13Ps3==count || skipCom==count) { + --search; + fseek(fp_, size-bufRead, SEEK_CUR); + } + else { + if (size < 2) return 2; + buf.alloc(size+2); + fseek(fp_, -bufRead-2, SEEK_CUR); + fread(buf.pData_, 1, size+2, fp_); + if (ferror(fp_) || feof(fp_)) return 1; + if (fwrite(buf.pData_, 1, size+2, ofp) != (size_t)size+2) return 3; + if (ferror(ofp)) return 3; + } + + // Next marker + marker = advanceToMarker(); + if (marker < 0) return 1; + ++count; } - // Write APP1 marker, size of APP1 field, Exif id and Exif data - us2Data(tmpbuf, app1_, bigEndian); - us2Data(tmpbuf + 2, static_cast(sizeExifData_ + 8), bigEndian); - memcpy(tmpbuf + 4, exifId_, 6); - os.write(tmpbuf, 10); - os.write(pExifData_, sizeExifData_); - if (!os.good()) return 4; + // Copy rest of the stream - os.flush(); - is >> os.rdbuf(); - if (!os.good()) return 4; + fseek(fp_, -2, SEEK_CUR); + fflush( ofp ); + buf.alloc(4096); + size_t readSize = 0; + while ((readSize=fread(buf.pData_, 1, buf.size_, fp_))) { + if (fwrite(buf.pData_, 1, readSize, ofp) != readSize) return 3; + } + if (ferror(ofp)) return 3; + + return 0; + }// JpegBase::doWriteMetadata + + + const byte JpegImage::soi_ = 0xd8; + const byte JpegImage::blank_[] = { + 0xFF,0xD8,0xFF,0xDB,0x00,0x84,0x00,0x10,0x0B,0x0B,0x0B,0x0C,0x0B,0x10,0x0C,0x0C, + 0x10,0x17,0x0F,0x0D,0x0F,0x17,0x1B,0x14,0x10,0x10,0x14,0x1B,0x1F,0x17,0x17,0x17, + 0x17,0x17,0x1F,0x1E,0x17,0x1A,0x1A,0x1A,0x1A,0x17,0x1E,0x1E,0x23,0x25,0x27,0x25, + 0x23,0x1E,0x2F,0x2F,0x33,0x33,0x2F,0x2F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x01,0x11,0x0F,0x0F,0x11,0x13,0x11,0x15,0x12, + 0x12,0x15,0x14,0x11,0x14,0x11,0x14,0x1A,0x14,0x16,0x16,0x14,0x1A,0x26,0x1A,0x1A, + 0x1C,0x1A,0x1A,0x26,0x30,0x23,0x1E,0x1E,0x1E,0x1E,0x23,0x30,0x2B,0x2E,0x27,0x27, + 0x27,0x2E,0x2B,0x35,0x35,0x30,0x30,0x35,0x35,0x40,0x40,0x3F,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0xFF,0xC0,0x00,0x11,0x08,0x00,0x01,0x00, + 0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,0x01,0xFF,0xC4,0x00,0x4B,0x00, + 0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x07,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xDA,0x00,0x0C,0x03,0x01,0x00,0x02, + 0x11,0x03,0x11,0x00,0x3F,0x00,0xA0,0x00,0x0F,0xFF,0xD9 }; + + JpegImage::JpegImage(const std::string& path, const bool create) + : JpegBase(path, create, blank_, sizeof(blank_)) + { + } + int JpegImage::writeHeader(FILE* ofp) const + { + // Jpeg header + byte tmpBuf[2]; + tmpBuf[0] = 0xff; + tmpBuf[1] = soi_; + if (fwrite(tmpBuf, 1, 2, ofp) != 2) return 3; + if (ferror(ofp)) return 3; return 0; - } // JpegImage::writeExifData + } - void JpegImage::setExifData(const char* buf, long size) + bool JpegImage::isThisType(FILE* ifp, bool advance) const { - sizeExifData_ = size; - delete[] pExifData_; - pExifData_ = new char[size]; - memcpy(pExifData_, buf, size); + return isJpegType(ifp, advance); } - Image* JpegImage::clone() const + Image* newJpegInstance(const std::string& path, FILE* fp) { - return new JpegImage(*this); + Image* pImage = 0; + if (fp == 0) { + pImage = new JpegImage(path,true); + if (!pImage->good()) { + delete pImage; + pImage = 0; + } + } + else { + pImage = new JpegImage(path, fp); + } + return pImage; } - bool JpegImage::isThisType(std::istream& is, bool advance) const + bool isJpegType(FILE* ifp, bool advance) { - char c; - is.get(c); - if (!is.good()) return false; - if (static_cast((soi_ & 0xff00) >> 8) != c) { - is.unget(); - return false; + bool result = true; + byte tmpBuf[2]; + fread(tmpBuf, 1, 2, ifp); + if (ferror(ifp) || feof(ifp)) return false; + + if (0xff!=tmpBuf[0] || JpegImage::soi_!=tmpBuf[1]) { + result = false; } - is.get(c); - if (!is.good()) return false; - if (static_cast(soi_ & 0x00ff) != c) { - is.seekg(-2, std::ios::cur); - return false; + if (!advance || !result ) fseek(ifp, -2, SEEK_CUR); + return result; + } + + + const char ExvImage::exiv2Id_[] = "Exiv2"; + const byte ExvImage::blank_[] = { 0xff,0x01,'E','x','i','v','2',0xff,0xd9 }; + + ExvImage::ExvImage(const std::string& path, const bool create) + : JpegBase(path, create, blank_, sizeof(blank_)) + { + } + + int ExvImage::writeHeader(FILE* ofp) const + { + // Exv header + byte tmpBuf[7]; + tmpBuf[0] = 0xff; + tmpBuf[1] = 0x01; + memcpy(tmpBuf + 2, exiv2Id_, 5); + if (fwrite(tmpBuf, 1, 7, ofp) != 7) return 3; + if (ferror(ofp)) return 3; + return 0; + } + + bool ExvImage::isThisType(FILE* ifp, bool advance) const + { + return isExvType(ifp, advance); + } + + Image* newExvInstance(const std::string& path, FILE* fp) + { + Image * pImage = 0; + if (fp == 0) { + pImage = new ExvImage(path,true); + if (!pImage->good()) { + delete pImage; + pImage = 0; + } + } + else { + pImage = new ExvImage(path, fp); + } + return pImage; + } + + bool isExvType(FILE* ifp, bool advance) + { + bool result = true; + byte tmpBuf[7]; + fread(tmpBuf, 1, 7, ifp); + if (ferror(ifp) || feof(ifp)) return false; + + if (0xff!=tmpBuf[0] || 0x01!=tmpBuf[1] || + memcmp(tmpBuf + 2, ExvImage::exiv2Id_, 5) != 0) { + result = false; } - if (advance == false) is.seekg(-2, std::ios::cur); - return true; + if (!advance || !result ) fseek(ifp, -7, SEEK_CUR); + return result; } + + TiffHeader::TiffHeader(ByteOrder byteOrder) : byteOrder_(byteOrder), tag_(0x002a), offset_(0x00000008) { } - int TiffHeader::read(const char* buf) + int TiffHeader::read(const byte* buf) { if (buf[0] == 0x49 && buf[1] == 0x49) { byteOrder_ = littleEndian; @@ -450,7 +800,7 @@ namespace Exiv2 { return 0; } - long TiffHeader::copy(char* buf) const + long TiffHeader::copy(byte* buf) const { switch (byteOrder_) { case littleEndian: @@ -470,117 +820,5 @@ namespace Exiv2 { return size(); } // TiffHeader::copy - const uint16 ExvFile::app1_ = 0xffe1; - const char ExvFile::exifId_[] = "Exif\0\0"; - - ExvFile::ExvFile() - : sizeExifData_(0), pExifData_(0) - { - } // ExvFile default constructor - - ExvFile::ExvFile(const ExvFile& rhs) - : sizeExifData_(0), pExifData_(0) - { - char* newExifData = 0; - if (rhs.sizeExifData_ > 0) { - char* newExifData = new char[rhs.sizeExifData_]; - memcpy(newExifData, rhs.pExifData_, rhs.sizeExifData_); - } - pExifData_ = newExifData; - sizeExifData_ = rhs.sizeExifData_; - } // ExvFile copy constructor - - ExvFile& ExvFile::operator=(const ExvFile& rhs) - { - if (this == &rhs) return *this; - char* newExifData = 0; - if (rhs.sizeExifData_ > 0) { - char* newExifData = new char[rhs.sizeExifData_]; - memcpy(newExifData, rhs.pExifData_, rhs.sizeExifData_); - } - pExifData_ = newExifData; - sizeExifData_ = rhs.sizeExifData_; - return *this; - } // ExvFile::operator= - - ExvFile::~ExvFile() - { - delete[] pExifData_; - } // ExvFile destructor - - int ExvFile::readExifData(const std::string& path) - { - std::ifstream file(path.c_str(), std::ios::binary); - if (!file) return -1; - return readExifData(file); - } // ExvFile::readExifData - - int ExvFile::readExifData(std::istream& is) - { - // Check if this is an Exiv2 file in the first place - if (!isThisType(is, false)) { - if (!is.good()) return 1; - return 2; - } - // Read the first ten bytes and extract the size - char tmpbuf[10]; - is.read(tmpbuf, 10); - if (!is.good()) return 1; - uint16 size = getUShort(tmpbuf + 2, bigEndian); - if (size < 8) return 3; - - // Read the rest of the APP1 field (Exif data) - long sizeExifData = size - 8; - char* pExifData = new char[sizeExifData]; - is.read(pExifData, sizeExifData); - if (!is.good()) { - delete[] pExifData; - return 1; - } - sizeExifData_ = sizeExifData; - pExifData_ = pExifData; - - return 0; - } // ExvFile::readExifData - - void ExvFile::setExifData(const char* buf, long size) - { - sizeExifData_ = size; - delete[] pExifData_; - pExifData_ = new char[size]; - memcpy(pExifData_, buf, size); - } // ExvFile::setExifData - - bool ExvFile::isThisType(std::istream& is, bool advance) - { - // Read and check section marker and size - char tmpbuf[10]; - is.read(tmpbuf, 10); - if (!is.good()) return false; - bool rc = true; - uint16 marker = getUShort(tmpbuf, bigEndian); - uint16 size = getUShort(tmpbuf + 2, bigEndian); - if (size < 8) rc = false; - if (!(marker == app1_ && memcmp(tmpbuf + 4, exifId_, 6) == 0)) { - rc = false; - } - if (!advance) is.seekg(-10, std::ios::cur); - return rc; - } // ExvFile::isThisType - - int ExvFile::writeExifData(const std::string& path) const - { - std::ofstream os(path.c_str(), std::ios::binary); - if (!os) return -1; - // Write APP1 marker, size of APP1 field, Exif id and Exif data - char tmpbuf[10]; - us2Data(tmpbuf, app1_, bigEndian); - us2Data(tmpbuf + 2, static_cast(sizeExifData_ + 8), bigEndian); - memcpy(tmpbuf + 4, exifId_, 6); - os.write(tmpbuf, 10); - os.write(pExifData_, sizeExifData_); - if (!os.good()) return 4; - return 0; - } // ExvFile::writeExifData - } // namespace Exiv2 + diff --git a/src/image.hpp b/src/image.hpp index ce13ed77..e6db85e3 100644 --- a/src/image.hpp +++ b/src/image.hpp @@ -21,11 +21,14 @@ /*! @file image.hpp @brief Class JpegImage to access JPEG images - @version $Name: $ $Revision: 1.13 $ + @version $Name: $ $Revision: 1.14 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 09-Jan-04, ahu: created - 11-Feb-04, ahu: isolated as a component + @author Brad Schick (brad) + schick@robotbattle.com + @date 09-Jan-04, ahu: created
+ 11-Feb-04, ahu: isolated as a component
+ 19-Jul-04, brad: revamped to be more flexible and support IPTC */ #ifndef IMAGE_HPP_ #define IMAGE_HPP_ @@ -36,13 +39,20 @@ // + standard includes #include -#include #include + // ***************************************************************************** // namespace extensions namespace Exiv2 { + //! Type for function pointer that creates new Image instances + typedef class Image* (*NewInstanceFct)(const std::string& path, FILE* ifp); + + //! Type for function pointer that checks image types + typedef bool (*IsThisTypeFct)(FILE* ifp, bool advance); + + // ***************************************************************************** // class definitions @@ -52,12 +62,10 @@ namespace Exiv2 { class Image { public: //! Supported image formats - enum Type { none, jpeg }; + enum Type { none, jpeg, exv }; //! @name Creators //@{ - //! Default Constructor - Image() {} //! Virtual Destructor virtual ~Image() {} //@} @@ -65,99 +73,112 @@ namespace Exiv2 { //! @name Manipulators //@{ /*! - @brief Read the Exif data from the file path into the internal - data buffer. - @param path Path to the file. + @brief Read metadata from assigned image file into internal + buffers. @return 0 if successful. */ - virtual int readExifData(const std::string& path) =0; + virtual int readMetadata() =0; /*! - @brief Read the Exif data from the stream into the internal - data buffer. - @param is Input stream to read from. + @brief Write metadata from internal buffers into to the image fle. @return 0 if successful. */ - virtual int readExifData(std::istream& is) =0; + virtual int writeMetadata() =0; /*! - @brief Read the Exif data from the buffer buf which has size bytes. - @param buf Pointer to the data buffer. - @param size Number of characters in the data buffer. + @brief Set the Exif data. The data is copied into an internal data + buffer and is not written until writeMetadata is called. + @param buf Pointer to the new Exif data. + @param size Size in bytes of new Exif data. */ - virtual void setExifData(const char* buf, long size) =0; - //@} - - //! @name Accessors - //@{ - //! Virtual copy construction - virtual Image* clone() const =0; + virtual void setExifData(const byte* buf, long size) =0; /*! - @brief Determine if the content of the stream is an image of the type - of this class. - - The advance flag determines if the read position in the stream is - moved (see below). This applies only if the image type matches and the - function returns true. If the image type does not match, the stream - position is not changed. However, if reading from the stream fails, - the stream position is undefined. Consult the stream state to obtain - more information in this case. - - @param is Input stream with the image. - @param advance Flag indicating whether the read position in the stream - should be advanced by the number of characters read to - analyse the image stream (true) or left at its original - position (false). This applies only if the image type - matches. - @return true if the type of the image matches that of this;
- false if the type of the image does not match. + @brief Erase any buffered Exif data. Exif data is not removed + from the actual file until writeMetadata is called. */ - virtual bool isThisType(std::istream& is, bool advance =false) const =0; + virtual void clearExifData() =0; /*! - @brief Erase the Exif data from file path. - @param path Path to the file. - @return 0 if successful. + @brief Set the Iptc data. The data is copied into an internal data + buffer and is not written until writeMetadata is called. + @param buf Pointer to the new Iptc data. + @param size Size in bytes of new Iptc data. */ - virtual int eraseExifData(const std::string& path) const =0; + virtual void setIptcData(const byte* buf, long size) =0; /*! - @brief Read from the image input stream is, erase Exif data from the - image, if there is any, and write the resulting image to the - output stream os. - @param os Output stream to write to (e.g., a temporary file). - @param is Input stream with the image from which the Exif data - should be erased. - @return 0 if successful. + @brief Erase any buffered Iptc data. Iptc data is not removed + from the actual file until writeMetadata is called. */ - virtual int eraseExifData(std::ostream& os, std::istream& is) const =0; + virtual void clearIptcData() =0; /*! - @brief Write the Exif data to file path. - @param path Path to the file. - @return 0 if successful. + @brief Set the image comment. The data is copied into an internal data + buffer and is not written until writeMetadata is called. + @param comment String containing comment. */ - virtual int writeExifData(const std::string& path) const =0; + virtual void setComment(const std::string &comment) =0; /*! - @brief Read from the image input stream is, add Exif data to the - image, replacing existing Exif data, if there is any) and - write the resulting image to the output stream os. - @param os Output stream to write to (e.g., a temporary file). - @param is Input stream with the image to which the Exif data - should be copied. - @return 0 if successful. + @brief Erase any buffered comment. Comment is not removed + from the actual file until writeMetadata is called. + */ + virtual void clearComment() =0; + /*! + @brief Copy all existing metadata from source %Image. The data is + copied into internal buffers and is not written until + writeMetadata is called. + @param image Metadata source. All metadata types are copied. + */ + virtual void setMetadata(const Image& image) =0; + /*! + @brief Erase all buffered metadata. Metadata is not removed + from the actual file until writeMetadata is called. + */ + virtual void clearMetadata() =0; + /*! + @brief Close associated image file but preserve buffered metadata. + @return 0 if successful. + */ + virtual int detach() =0; + //@} + + //! @name Accessors + //@{ + /*! + @brief Check if the %Image instance is valid. Use after object + construction. + @return true if the %Image is in a valid state. */ - virtual int writeExifData(std::ostream& os, std::istream& is) const =0; + virtual bool good() const =0; //! Return the size of the Exif data in bytes. virtual long sizeExifData() const =0; /*! @brief Return a read-only pointer to an Exif data buffer. Do not attempt to write to this buffer. */ - virtual const char* exifData() const =0; + virtual const byte* exifData() const =0; + //! Return the size of the Iptc data in bytes. + virtual long sizeIptcData() const =0; + /*! + @brief Return a read-only pointer to an Iptc data buffer. Do not + attempt to write to this buffer. + */ + virtual const byte* iptcData() const =0; + /*! + @brief Return a read-only reference to the image comment. + Do not attempt to write to this string. May be an empty string. + */ + virtual std::string comment() const =0; //@} protected: - /*! - @brief Assignment operator. Protected so that it can only be used - by subclasses but not directly. - */ - Image& operator=(const Image& rhs) { return *this; } + //! @name Creators + //@{ + //! Default Constructor + Image() {} + //@} + + private: + // NOT Implemented + //! Copy constructor + Image(const Image& rhs); + //! Assignment operator + Image& operator=(const Image& rhs); }; // class Image @@ -173,50 +194,63 @@ namespace Exiv2 { //! @name Manipulators //@{ /*! - @brief Get access to the image factory. + @brief Register image type together with its function pointers. - Clients access the image factory exclusively through - this method. - */ - static ImageFactory& instance(); - - /*! - @brief Register an image prototype together with its type. - - The image factory creates new images by cloning their associated - prototypes. Additional images can be added by registering a prototype - and its type. If called for a type which already exists in the list, - the corresponding prototype is replaced. + The image factory creates new images calling their associated + function pointer. Additional images can be added by registering + new type and function pointers. If called for a type that already + exists in the list, the corresponding prototype is replaced. @param type Image type. - @param pImage Pointer to the prototype. Ownership is transfered to the - factory. + @param newInst Function pointer for creating image instances. + @param isType Function pointer to test for matching image types. */ - void registerImage(Image::Type type, Image* pImage); + void registerImage(Image::Type type, + NewInstanceFct newInst, + IsThisTypeFct isType); //@} //! @name Accessors //@{ /*! - @brief Create an %Image of the appropriate type, derived from the - contents of the stream is. - @param is Image stream. The contents of the stream are tested to - determine the image type to create. + @brief Create an %Image of the appropriate type by opening the + specified file. File type is derived from the contents of the + file. + @param path %Image file. The contents of the file are tested to + determine the image type to open. File extension is ignored. @return A pointer that owns an %Image of the type derived from the - stream. If no image type could be determined, the pointer is 0. + file. Caller must delete the returned object. If no image type + could be determined, the pointer is 0. */ - Image* create(std::istream& is) const; - + Image* open(const std::string& path) const; /*! - @brief Create an %Image of the requested type. - + @brief Create an %Image of the requested type by creating a new + file. If the file already exists, it will be overwritten. @param type Type of the image to be created. - @return A pointer that owns an %Image of the requested type. - If the image type is not supported, the pointer is 0. + @param path %Image file. The contents of the file are tested to + determine the image type to open. File extension is ignored. + @return A pointer that owns an %Image of the requested type. Caller + must delete the returned object. If the image type is not + supported, the pointer is 0. + */ + Image* create(Image::Type type, const std::string& path) const; + /*! + @brief Returns the image type of the provided file. + @param path %Image file. The contents of the file are tested to + determine the image type. File extension is ignored. + @return %Image type of Image::none if the type is not recognized. */ - Image* create(Image::Type type) const; + Image::Type getType(const std::string& path) const; //@} + /*! + @brief Get access to the image factory. + + Clients access the image factory exclusively through + this method. + */ + static ImageFactory& instance(); + private: //! @name Creators //@{ @@ -226,170 +260,362 @@ namespace Exiv2 { ImageFactory(const ImageFactory& rhs); //@} + //! Struct for storing image function pointers. + struct ImageFcts + { + NewInstanceFct newInstance; + IsThisTypeFct isThisType; + ImageFcts(NewInstanceFct newInst, IsThisTypeFct isType) + : newInstance(newInst), isThisType(isType) {} + ImageFcts() : newInstance(0), isThisType(0) {} + }; + // DATA //! Pointer to the one and only instance of this class. static ImageFactory* pInstance_; - //! Type used to store Image prototype classes - typedef std::map Registry; - //! List of image types and corresponding prototypes. + //! Type used to store Image creation functions + typedef std::map Registry; + //! List of image types and corresponding creation functions. Registry registry_; }; // class ImageFactory /*! - @brief Helper class to access JPEG images + @brief Abstract helper base class to access JPEG images */ - class JpegImage : public Image { + class JpegBase : public Image { public: //! @name Creators //@{ - //! Default constructor - JpegImage(); - //! Copy constructor - JpegImage(const JpegImage& rhs); - //! Destructor - ~JpegImage(); + //! Virtual destructor. + virtual ~JpegBase(); //@} - //! @name Manipulators //@{ - //! Assignment operator - JpegImage& operator=(const JpegImage& rhs); - /*! - @brief Read the Exif data from the file path into the internal - data buffer. - @param path Path to the file. - @return 0 if successful;
- -1 if the file cannot be opened; or
- the return code of readExifData(std::istream& is) - if the call to this function fails. - */ - int readExifData(const std::string& path); /*! - @brief Read the Exif data from the stream into the internal - data buffer. - @param is Input stream to read from. + @brief Read all metadata from the file into the internal + data buffers. This method returns success even when + no metadata is found in the image. Callers must therefore + check the size of indivdual metadata types before + accessing the data. @return 0 if successful;
- 1 if reading from the stream failed (consult the stream state - for more information);
- 2 if the stream does not contain a JPEG image;
- 3 if no Exif APP1 segment was found after SOI at the - beginning of the input stream. + 1 if reading from the file failed + (could be caused by invalid image);
+ 2 if the file does not contain a valid image;
*/ - int readExifData(std::istream& is); + int readMetadata(); /*! - @brief Set the Exif data. The data is copied into the internal - data buffer. - @param buf Pointer to the data buffer. - @param size Number of characters in the data buffer. + @brief Write all buffered metadata to associated file. All existing + metadata sections in the file are either replaced or erased. + If data for a given metadata type has not been assigned, + then that metadata type will be erased from the file. + @return 0 if successful;
+ 1 if reading from the file failed;
+ 2 if the associated file does not contain a valid image;
+ 3 if the temporary output file can not be written to;
+ 4 if renaming the temporary file fails;
*/ - void setExifData(const char* buf, long size); + int writeMetadata(); + void setExifData(const byte* buf, long size); + void clearExifData(); + void setIptcData(const byte* buf, long size); + void clearIptcData(); + void setComment(const std::string &comment); + void clearComment(); + void setMetadata(const Image& image); + void clearMetadata(); + int detach(); //@} //! @name Accessors //@{ - //! Virtual copy construction - Image* clone() const; + bool good() const; + long sizeExifData() const { return sizeExifData_; } + const byte* exifData() const { return pExifData_; } + long sizeIptcData() const { return sizeIptcData_; } + const byte* iptcData() const { return pIptcData_; } + std::string comment() const { return comment_; } + //@} + + protected: + //! @name Creators + //@{ + /*! + @brief Constructor for subclasses that have already opened a + file stream on the specified path. + @param path Full path to image file. + @param fp File pointer to open file. + */ + JpegBase(const std::string& path, FILE* fp); + /*! + @brief Constructor that can either open an existing image or create + a new image from scratch. If a new image is to be created, any + existing file is overwritten + @param path Full path to image file. + @param create Specifies if an existing file should be opened (false) + or if a new file should be created (true). + @param initData Data to initialize newly created files. Only used + when %create is true. Should contain the data for the smallest + valid image of the calling subclass. + @param dataSize Size of initData in bytes. + */ + JpegBase(const std::string& path, const bool create, + const byte initData[], const size_t dataSize); + //@} + //! @name Accessors + //@{ /*! - @brief Determine if the content of the stream is a JPEG image. - @param is Input stream to test. + @brief Writes the image header (aka signature) to the file stream. + @param ofp File stream that the header is written to. + @return 0 if successful;
+ 3 if the output file can not be written to;
+ */ + virtual int writeHeader(FILE* ofp) const =0; + /*! + @brief Determine if the content of the stream is of the type of this + class. + + The advance flag determines if the read position in the stream is + moved (see below). This applies only if the type matches and the + function returns true. If the type does not match, the stream + position is not changed. However, if reading from the stream fails, + the stream position is undefined. Consult the stream state to obtain + more information in this case. + + @param ifp Input file stream. @param advance Flag indicating whether the read position in the stream should be advanced by the number of characters read to - analyse the image stream (true) or left at its original - position (false). This applies only if the image type - matches. - @return true if the input stream starts with the JPEG SOI marker. - The stream is not advanced in this case.
- false if the input stream does not begin with the JPEG SOI - marker. The stream is not advanced.
- false if reading the first two bytes from the stream fails. - Consult the stream state for more information. In this case, - the stream may or may not have been advanced by 1 or 2 - characters. - */ - bool isThisType(std::istream& is, bool advance) const; - /*! - @brief Erase the Exif data from file path, which must contain a JPEG - image. If an Exif APP1 section exists in the file, it is - erased. - @param path Path to the file. - @return 0 if successful;
- -1 if the input file cannot be opened;
- -3 if the temporary file cannot be opened;
- -4 if renaming the temporary file fails; or
- the return code of - eraseExifData(std::ostream& os, std::istream& is) const - if the call to this function fails. - */ - int eraseExifData(const std::string& path) const; - /*! - @brief Erase Exif data from the JPEG image is, write the resulting - image to the output stream os. If an Exif APP1 section exists - in the input file, it is erased. - @param os Output stream to write to (e.g., a temporary file). - @param is Input stream with the JPEG image from which the Exif data - should be erased. + analyse the stream (true) or left at its original + position (false). This applies only if the type matches. + @return true if the stream data matches the type of this class;
+ false if the stream data does not match. + */ + virtual bool isThisType(FILE* ifp, bool advance) const =0; + //@} + + // Constant Data + static const byte sos_; //!< JPEG SOS marker + static const byte eoi_; //!< JPEG EOI marker + static const byte app0_; //!< JPEG APP0 marker + static const byte app1_; //!< JPEG APP1 marker + static const byte app13_; //!< JPEG APP13 marker + static const byte com_; //!< JPEG Comment marker + static const char exifId_[]; //!< Exif identifier + static const char jfifId_[]; //!< JFIF identifier + static const char ps3Id_[]; //!< Photoshop marker + static const char bimId_[]; //!< Photoshop marker + static const uint16 iptc_; //!< Photoshop IPTC marker + + private: + // DATA + FILE* fp_; //!< Image file (read write) + const std::string path_; //!< Image file name + long sizeExifData_; //!< Size of the Exif data buffer + byte* pExifData_; //!< Exif data buffer + long sizeIptcData_; //!< Size of the Iptc data buffer + byte* pIptcData_; //!< IPTC data buffer + std::string comment_; //!< JPEG comment + + // METHODS + /*! + @brief Advances file stream to one byte past the next Jpeg marker + and returns the marker. This method should be called when the + file stream is positioned one byte past the end of a Jpeg segment. + @return the next Jpeg segment marker if successful;
+ -1 if a maker was not found before EOF;
+ */ + int advanceToMarker() const; + /*! + @brief Locates Photoshop formated IPTC data in a memory buffer. + Operates on raw data (rather than file streams) to simplify reuse. + @param pPSData Pointer to buffer containing entire payload of + Photoshop formated APP13 Jpeg segment. + @param sizePSData Size in bytes of pPSData. + @param record Output value that is set to the start of the IPTC + data block within pPSData (may not be null). + @param sizeHdr Output value that is set to the size of the header + within the IPTC data block pointed to by record (may not + be null). + @param sizeIptc Output value that is set to the size of the actual + IPTC data within the IPTC data block pointed to by record + (may not be null). @return 0 if successful;
- 1 if reading from the input stream failed (consult the stream - state for more information);
- 2 if the input stream does not contain a JPEG image;
- 3 if neither a JFIF APP0 segment nor a Exif APP1 segment was - found after SOI at the beginning of the input stream;
- 4 if writing to the output stream failed (consult the stream - state for more information). - */ - int eraseExifData(std::ostream& os, std::istream& is) const; - /*! - @brief Write the Exif data to file path, which must contain a JPEG - image. If an Exif APP1 section exists in the file, it is - replaced. Otherwise, an Exif data section is created. - @param path Path to the file. - @return 0 if successful;
- -1 if the input file cannot be opened;
- -3 if the temporary file cannot be opened;
- -4 if renaming the temporary file fails; or
- the return code of - writeExifData(std::ostream& os, std::istream& is) const - if the call to this function fails. - */ - int writeExifData(const std::string& path) const; - /*! - @brief Copy Exif data into the JPEG image is, write the resulting - image to the output stream os. If an Exif APP1 section exists - in the input file, it is replaced. Otherwise, an Exif data - section is created. + 1 if the pPSData buffer does not contain valid data;
+ 2 if no IPTC data was found in pPSData;
+ */ + int locateIptcData(const byte *pPSData, + long sizePSData, + const byte **record, + uint16 *const sizeHdr, + uint16 *const sizeIptc) const; + /*! + @brief Write to the associated file stream with the provided data. + @param initData Data to be written to the associated file + @param dataSize Size in bytes of data to be written + @return 0 if successful;
+ 3 if the output file can not be written to;
+ */ + int initFile(const byte initData[], const size_t dataSize); + /*! + @brief Provides the main implementation of writeMetadata by + writing all buffered metadata to associated file. @param os Output stream to write to (e.g., a temporary file). - @param is Input stream with the JPEG image to which the Exif data - should be copied. + @return 0 if successful;
+ 1 if reading from associated file failed;
+ 2 if the file does not contain a valid image;
+ 3 if the temporary output file can not be written to;
+ */ + int doWriteMetadata(FILE* ofp) const; + + // NOT Implemented + //! Default constructor. + JpegBase(); + //! Copy constructor + JpegBase(const JpegBase& rhs); + //! Assignment operator + JpegBase& operator=(const JpegBase& rhs); + }; // class JpegBase + + /*! + @brief Helper class to access JPEG images + */ + class JpegImage : public JpegBase { + friend Image* newJpegInstance(const std::string& path, FILE* fp); + friend bool isJpegType(FILE* ifp, bool advance); + public: + //! @name Creators + //@{ + /*! + @brief Constructor that can either open an existing Jpeg image or create + a new image from scratch. If a new image is to be created, any + existing file is overwritten. Since the constructor can not return + a result, callers should check the %good method after object + construction to determine success or failure. + @param path Full path to image file. + @param create Specifies if an existing file should be opened (false) + or if a new file should be created (true). + */ + JpegImage(const std::string& path, const bool create); + //! Destructor + ~JpegImage() {} + //@} + protected: + //! @name Accessors + //@{ + /*! + @brief Writes a Jpeg header (aka signature) to the file stream. + @param ofp File stream that the header is written to. @return 0 if successful;
- 1 if reading from the input stream failed (consult the stream - state for more information);
- 2 if the input stream does not contain a JPEG image;
- 3 if neither a JFIF APP0 segment nor a Exif APP1 segment was - found after SOI at the beginning of the input stream;
- 4 if writing to the output stream failed (consult the stream - state for more information). - */ - int writeExifData(std::ostream& os, std::istream& is) const; - //! Return the size of the Exif data buffer - long sizeExifData() const { return sizeExifData_; } - //! Return a read-only pointer to the Exif data buffer - const char* exifData() const { return pExifData_; } + 3 if the output file can not be written to;
+ */ + int writeHeader(FILE* ofp) const; + /*! + @brief Determine if the content of the file stream is a Jpeg image. + See base class for more details. + @param ifp Input file stream. + @param advance Flag indicating whether the read position in the stream + should be advanced by the number of characters read to + analyse the stream (true) or left at its original + position (false). This applies only if the type matches. + @return true if the file stream data matches a Jpeg image;
+ false if the stream data does not match. + */ + bool isThisType(FILE* ifp, bool advance) const; //@} - private: - // DATA - static const uint16 soi_; // SOI marker - static const uint16 app0_; // APP0 marker - static const uint16 app1_; // APP1 marker - static const char exifId_[]; // Exif identifier - static const char jfifId_[]; // JFIF identifier + // Constant data + static const byte soi_; // SOI marker + static const byte blank_[]; // Minimal Jpeg image - long sizeExifData_; // Size of the Exif data buffer - char* pExifData_; // Exif data buffer + //! @name Creators + //@{ + /*! + @brief Constructor to be used when a Jpeg file has already + been opened. Meant for internal factory use. + @param path Full path to opened image file. + @param fp File pointer to open file. + */ + JpegImage(const std::string& path, FILE* fp) : JpegBase(path, fp) {} + //@} + // NOT Implemented + //! Default constructor + JpegImage(); + //! Copy constructor + JpegImage(const JpegImage& rhs); + //! Assignment operator + JpegImage& operator=(const JpegImage& rhs); }; // class JpegImage + //! Helper class to access %Exiv2 files + class ExvImage : public JpegBase { + friend Image* newExvInstance(const std::string& path, FILE* fp); + friend bool isExvType(FILE* ifp, bool advance); + public: + //! @name Creators + //@{ + /*! + @brief Constructor that can either open an existing Exv image or create + a new image from scratch. If a new image is to be created, any + existing file is overwritten. Since the constructor can not return + a result, callers should check the %good method after object + construction to determine success or failure. + @param path Full path to image file. + @param create Specifies if an existing file should be opened (false) + or if a new file should be created (true). + */ + ExvImage(const std::string& path, const bool create); + //! Destructor + ~ExvImage() {} + //@} + protected: + //! @name Accessors + //@{ + /*! + @brief Writes an Exv header (aka signature) to the file stream. + @param ofp File stream that the header is written to. + @return 0 if successful;
+ 3 if the output file can not be written to;
+ */ + int writeHeader(FILE* ofp) const; + /*! + @brief Determine if the content of the file stream is a Exv image. + See base class for more details. + @param ifp Input file stream. + @param advance Flag indicating whether the read position in the stream + should be advanced by the number of characters read to + analyse the stream (true) or left at its original + position (false). This applies only if the type matches. + @return true if the file stream data matches a Exv image;
+ false if the stream data does not match. + */ + virtual bool isThisType(FILE* ifp, bool advance) const; + //@} + private: + // Constant data + static const char exiv2Id_[]; // Exv identifier + static const byte blank_[]; // Minimal exiv file + + //! @name Creators + //@{ + /*! + @brief Constructor to be used when an Exv file has already + been opened. Meant for internal factory use. + @param path Full path to opened image file. + @param fp File pointer to open file. + */ + ExvImage(const std::string& path, FILE* fp) : JpegBase(path, fp) {} + //@} + + // NOT Implemented + //! Default constructor + ExvImage(); + //! Copy constructor + ExvImage(const ExvImage& rhs); + //! Assignment operator + ExvImage& operator=(const ExvImage& rhs); + }; // class ExvImage + //! Helper class modelling the TIFF header structure. class TiffHeader { public: @@ -405,7 +631,7 @@ namespace Exiv2 { //! @name Manipulators //@{ //! Read the TIFF header from a data buffer. Returns 0 if successful. - int read(const char* buf); + int read(const byte* buf); //@} //! @name Accessors @@ -422,7 +648,7 @@ namespace Exiv2 { @param buf The data buffer to write to. @return The number of bytes written. */ - long copy(char* buf) const; + long copy(byte* buf) const; //! Return the size of the TIFF header in bytes. long size() const { return 8; } //! Return the byte order (little or big endian). @@ -442,95 +668,7 @@ namespace Exiv2 { uint16 tag_; uint32 offset_; - }; // class TiffHeader - - //! Helper class to access %Exiv2 files - class ExvFile { - public: - //! @name Creators - //@{ - //! Default Constructor - ExvFile(); - //! Destructor - ~ExvFile(); - //! Copy constructor - ExvFile(const ExvFile& rhs); - //@} - - //! @name Manipulators - //@{ - //! Assignment operator - ExvFile& operator=(const ExvFile& rhs); - /*! - @brief Read the Exif data from the file path into the internal - data buffer. - @param path Path to the file. - @return 0 if successful. - */ - int readExifData(const std::string& path); - /*! - @brief Read the Exif data from the stream is into the internal - data buffer. - @param is Input stream to read from. - @return 0 if successful. - */ - int readExifData(std::istream& is); - /*! - @brief Read the Exif data from the buffer buf which has size bytes. - @param buf Pointer to the data buffer. - @param size Number of characters in the data buffer. - */ - void setExifData(const char* buf, long size); - //@} - - //! @name Accessors - //@{ - /*! - @brief Write the Exif data to file path. - @param path Path to the file. - @return 0 if successful. - */ - int writeExifData(const std::string& path) const; - //! Return the size of the Exif data in bytes. - long sizeExifData() const { return sizeExifData_; } - /*! - @brief Return a read-only pointer to an Exif data buffer. Do not - attempt to write to this buffer. - */ - const char* exifData() const { return pExifData_; } - //@} - - /*! - @brief Determine if the content of the stream is of the type of this - class. - - The advance flag determines if the read position in the stream is - moved (see below). This applies only if the type matches and the - function returns true. If the type does not match, the stream - position is not changed. However, if reading from the stream fails, - the stream position is undefined. Consult the stream state to obtain - more information in this case. - - @param is Input stream. - @param advance Flag indicating whether the read position in the stream - should be advanced by the number of characters read to - analyse the stream (true) or left at its original - position (false). This applies only if the type matches. - @return true if the stream data matches the type of this class;
- false if the stream data does not match. - */ - static bool isThisType(std::istream& is, bool advance =false); - - private: - // DATA - static const uint16 app1_; // APP1 marker - static const char exifId_[]; // Exif identifier - - long sizeExifData_; // Size of the Exif data buffer - char* pExifData_; // Exif data buffer - - }; // class ExvFile - + }; // class TiffHeader } // namespace Exiv2 #endif // #ifndef IMAGE_HPP_ diff --git a/src/imagetest.sh b/src/imagetest.sh new file mode 100755 index 00000000..b6d557b1 --- /dev/null +++ b/src/imagetest.sh @@ -0,0 +1,101 @@ +#! /bin/sh +# Test driver for image file i/o + +eraseTest() +{ + src=$1 + test=${src}.etst + good=${src}.egd + + #setup + cp $src $test + + #run tests + ../src/metacopy $test $test + + #check results + diffCheck $test $good +} + +copyTest() +{ + num=$1 + src=$2 + dst=$3 + test=${dst}.c${num}tst + good=${dst}.c${num}gd + + #setup + cp $dst $test + + #run tests + ../src/metacopy -a $src $test + + #check results + diffCheck $test $good +} + +iptcTest() +{ + num=$1 + src=$2 + dst=$3 + test=${dst}.i${num}tst + good=${dst}.i${num}gd + + #setup + cp $dst $test + + #run tests + ../src/metacopy -ip $src $test + + #check results + diffCheck $test $good +} + + +# Make sure to pass the test file first and the know good file second +diffCheck() { + + test=$1 + good=$2 + + #run diff and check results + diff -q --binary $test $good + if [ $? -ne 0 ]; then + let ++errors + else + rm $test + fi +} + +test_files=("table.jpg" "smiley1.jpg" "smiley2.jpg") + +let errors=0 +cd ../test +echo + +echo "Erase all tests..." +foreach i ($test_files); eraseTest $i; end +eraseTest "glider.exv" #extra test + +echo "Copy all tests..." +let c=0 +foreach src ($test_files) + let ++c + foreach dst ($test_files); copyTest $c $src $dst; end +end + +echo "Copy iptc tests..." +let c=0 +foreach src ($test_files) + let ++c + foreach dst ($test_files); iptcTest $c $src $dst; end +end + +echo '---------------------------------------------------------' +if [ $errors -eq 0 ]; then + echo 'All test cases passed' +else + echo $errors 'test case(s) failed!' +fi diff --git a/src/makernote.cpp b/src/makernote.cpp index 7844cd6f..4a70a986 100644 --- a/src/makernote.cpp +++ b/src/makernote.cpp @@ -20,13 +20,13 @@ */ /* File: makernote.cpp - Version: $Name: $ $Revision: 1.22 $ + Version: $Name: $ $Revision: 1.23 $ Author(s): Andreas Huggel (ahu) History: 18-Feb-04, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.22 $ $RCSfile: makernote.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.23 $ $RCSfile: makernote.cpp,v $") // Define DEBUG_* to output debug information to std::cerr #undef DEBUG_MAKERNOTE @@ -165,7 +165,7 @@ namespace Exiv2 { { } - int IfdMakerNote::read(const char* buf, + int IfdMakerNote::read(const byte* buf, long len, ByteOrder byteOrder, long offset) @@ -206,7 +206,7 @@ namespace Exiv2 { return rc; } // IfdMakerNote::read - long IfdMakerNote::copy(char* buf, ByteOrder byteOrder, long offset) + long IfdMakerNote::copy(byte* buf, ByteOrder byteOrder, long offset) { // Remember the new offset offset_ = offset; @@ -222,7 +222,7 @@ namespace Exiv2 { return len; } // IfdMakerNote::copy - int IfdMakerNote::readHeader(const char* buf, + int IfdMakerNote::readHeader(const byte* buf, long len, ByteOrder byteOrder) { @@ -236,7 +236,7 @@ namespace Exiv2 { return 0; } - long IfdMakerNote::copyHeader(char* buf) const + long IfdMakerNote::copyHeader(byte* buf) const { if (header_.size_ != 0) memcpy(buf, header_.pData_, header_.size_); return header_.size_; @@ -309,7 +309,7 @@ namespace Exiv2 { MakerNote* MakerNoteFactory::create(const std::string& make, const std::string& model, bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset) const @@ -386,7 +386,7 @@ namespace Exiv2 { #ifdef DEBUG_REGISTRY std::cerr << "Exact match (score: " << key.size() + 2 << ")\n"; #endif - return key.size() + 2; + return static_cast(key.size()) + 2; } std::string uKey = key; std::string uReg = regEntry; @@ -448,7 +448,7 @@ namespace Exiv2 { } if (found) { - count += ss.size(); + count += static_cast(ss.size()); } else { #ifdef DEBUG_REGISTRY diff --git a/src/makernote.hpp b/src/makernote.hpp index e78c51b5..86435fb7 100644 --- a/src/makernote.hpp +++ b/src/makernote.hpp @@ -22,7 +22,7 @@ @file makernote.hpp @brief Contains the Exif %MakerNote interface, IFD %MakerNote and a MakerNote factory - @version $Name: $ $Revision: 1.20 $ + @version $Name: $ $Revision: 1.21 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 18-Feb-04, ahu: created @@ -53,7 +53,7 @@ namespace Exiv2 { // type definitions //! Type for a pointer to a function creating a makernote - typedef MakerNote* (*CreateFct)(bool, const char*, long, ByteOrder, long); + typedef MakerNote* (*CreateFct)(bool, const byte*, long, ByteOrder, long); // ***************************************************************************** // class definitions @@ -137,7 +137,7 @@ namespace Exiv2 { start of the TIFF header) and encoded in byte order byteOrder. Return 0 if successful. */ - virtual int read(const char* buf, + virtual int read(const byte* buf, long len, ByteOrder byteOrder, long offset) =0; @@ -147,7 +147,7 @@ namespace Exiv2 { in byte order byteOrder. Update internal offsets if necessary. Return the number of bytes written. */ - virtual long copy(char* buf, ByteOrder byteOrder, long offset) =0; + virtual long copy(byte* buf, ByteOrder byteOrder, long offset) =0; /*! @brief Add the entry to the makernote. No duplicate-check is performed, i.e., it is possible to add multiple entries with the same tag. @@ -279,7 +279,7 @@ namespace Exiv2 { //! @name Manipulators //@{ - virtual int read(const char* buf, + virtual int read(const byte* buf, long len, ByteOrder byteOrder, long offset); @@ -291,10 +291,10 @@ namespace Exiv2 { @note The default implementation does nothing, assuming there is no header */ - virtual int readHeader(const char* buf, + virtual int readHeader(const byte* buf, long len, ByteOrder byteOrder); - virtual long copy(char* buf, ByteOrder byteOrder, long offset); + virtual long copy(byte* buf, ByteOrder byteOrder, long offset); void add(const Entry& entry) { ifd_.add(entry); } Entries::iterator begin() { return ifd_.begin(); } Entries::iterator end() { return ifd_.end(); } @@ -319,7 +319,7 @@ namespace Exiv2 { number of characters written. @note The default implementation copies the header_ buffer. */ - virtual long copyHeader(char* buf) const; + virtual long copyHeader(byte* buf) const; /*! @brief Return the size of the makernote header in bytes. @note The default implementation returns the size of the header_ @@ -445,7 +445,7 @@ namespace Exiv2 { MakerNote* create(const std::string& make, const std::string& model, bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset) const; diff --git a/src/metacopy.cpp b/src/metacopy.cpp new file mode 100644 index 00000000..e5b23d93 --- /dev/null +++ b/src/metacopy.cpp @@ -0,0 +1,193 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* + Abstract : Tester application for image file handling + + File : metacopy.cpp + Version : $Name: $ $Revision: 1.1 $ + Author(s): Brad Schick (brad) + History : 13-Jul-04, brad: created + */ +// ***************************************************************************** +// included header files +#include "exif.hpp" +#include "types.hpp" +#include "metacopy.hpp" +#include +#include + +// ***************************************************************************** +// Main +int main(int argc, char* const argv[]) +{ +try { + // Handle command line arguments + Params params; + if (params.getopt(argc, argv)) { + params.usage(); + return 1; + } + if (params.help_) { + params.help(); + return 2; + } + + Exiv2::Image* readImg = Exiv2::ImageFactory::instance().open(params.read_); + if (!readImg) { + std::cerr << params.progname() << + ": Could not read file (" << params.read_ << ")\n"; + return 4; + } + if (readImg->readMetadata()) { + std::cerr << params.progname() << + ": Could not read metadata from (" << params.read_ << ")\n"; + return 5; + } + readImg->detach(); + + Exiv2::Image* writeImg = Exiv2::ImageFactory::instance().open(params.write_); + if (!writeImg) { + std::cerr << params.progname() << + ": Could not read file (" << params.write_ << ")\n"; + return 6; + } + + if (params.preserve_) { + if (writeImg->readMetadata()) { + std::cerr << params.progname() << + ": Could not read metadata from (" << params.write_ << ")\n"; + return 7; + } + } + if (params.iptc_) { + writeImg->setIptcData(readImg->iptcData(), readImg->sizeIptcData()); + } + if (params.exif_) { + writeImg->setExifData(readImg->exifData(), readImg->sizeExifData()); + } + if (params.comment_) { + writeImg->setComment(readImg->comment()); + } + + if (writeImg->writeMetadata()) { + std::cerr << params.progname() << + ": Could not write metadata to (" << params.write_ << ")\n"; + return 8; + } + + delete readImg; + delete writeImg; + return 0; +} +catch (Exiv2::Error& e) { + std::cerr << "Caught Exiv2 exception '" << e << "'\n"; + return 10; +} +} + +int Params::option(int opt, const std::string& optarg, int optopt) +{ + int rc = 0; + switch (opt) { + case 'h': help_ = true; break; + case 'i': iptc_ = true; break; + case 'e': exif_ = true; break; + case 'c': comment_ = true; break; + case 'p': preserve_ = true; break; + case 'a': + iptc_ =true; + exif_ =true; + comment_ =true; + break; + case ':': + std::cerr << progname() << ": Option -" << static_cast(optopt) + << " requires an argument\n"; + rc = 1; + break; + case '?': + std::cerr << progname() << ": Unrecognized option -" + << static_cast(optopt) << "\n"; + rc = 1; + break; + default: + std::cerr << progname() + << ": getopt returned unexpected character code " + << std::hex << opt << "\n"; + rc = 1; + break; + } + + return rc; +} + +int Params::nonoption(const std::string& argv) +{ + if (!write_.empty()) { + std::cerr << progname() << ": Unexpected extra argument (" << argv << ")\n"; + return 1; + } + if (first_) read_ = argv; + else write_ = argv; + first_ = false; + return 0; +} + +int Params::getopt(int argc, char* const argv[]) +{ + int rc = Util::Getopt::getopt(argc, argv, optstring_); + // Further consistency checks + if (help_==false) { + if (rc==0 && read_.empty() ) { + std::cerr << progname() << ": Read and write files must be specified\n"; + rc = 1; + } + if (rc==0 && write_.empty() ) { + std::cerr << progname() << ": Write file must be specified\n"; + rc = 1; + } + if (preserve_ && iptc_ && exif_ && comment_ ) { + std::cerr << progname() << ": Option -p has no effect when all metadata types are specified.\n"; + rc = 1; + } + } + return rc; +} // Params::getopt + + +void Params::usage(std::ostream& os) const +{ + os << "\nReads and writes raw metadata. Use -h option for help.\n" + << "Usage: " << progname() + << " [-iecaph] readfile writefile\n"; +} + +void Params::help(std::ostream& os) const +{ + usage(os); + os << "\nOptions:\n" + << " -i Read Iptc data from readfile and write to writefile.\n" + << " -e Read Exif data from readfile and write to writefile.\n" + << " -c Read Jpeg comment from readfile and write to writefile.\n" + << " -a Read all metadata from readfile and write to writefile.\n" + << " -p Preserve existing metadata in writefile if not replaced.\n" + << " -h Display this help and exit.\n\n"; +} // Params::help + diff --git a/src/metacopy.hpp b/src/metacopy.hpp new file mode 100644 index 00000000..de410a01 --- /dev/null +++ b/src/metacopy.hpp @@ -0,0 +1,86 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/*! + @file metacopy.hpp + @brief Defines class Params, used for the command line handling + @version $Name: $ $Revision: 1.1 $ + @author Brad Schick (brad) + @date 13-Jul-04, brad: created + */ +#ifndef METACOPY_HPP_ +#define METACOPY_HPP_ + +#include "utils.hpp" + +class Params : public Util::Getopt { +private: + std::string optstring_; + bool first_; + +public: + bool help_; //!< Help option flag. + bool iptc_; //!< IPTC option flag. + bool exif_; //!< Exif option flag. + bool comment_; //!< JPEG comment option flag. + bool preserve_; //!< Preserve existing metadata option flag. + std::string read_; //!< Source file + std::string write_; //!< Destination file + +public: + /*! + @brief Default constructor. Note that optstring_ is initialized here. + */ + Params() : optstring_(":iecaph"), + first_(true), + help_(false), + iptc_(false), + exif_(false), + comment_(false), + preserve_(false) + {} + + /*! + @brief Call Getopt::getopt() with optstring, to initiate command line + argument parsing, perform consistency checks after all command line + arguments are parsed. + + @param argc Argument count as passed to main() on program invocation. + @param argv Argument array as passed to main() on program invocation. + + @return 0 if successful, >0 in case of errors. + */ + int getopt(int argc, char* const argv[]); + + //! Handle options and their arguments. + virtual int option(int opt, const std::string& optarg, int optopt); + + //! Handle non-option parameters. + virtual int nonoption(const std::string& argv); + + //! Print a minimal usage note to an output stream. + void usage(std::ostream& os =std::cout) const; + + //! Print further usage explanations to an output stream. + void help(std::ostream& os =std::cout) const; + +}; // class Params + +#endif // METACOPY_HPP_ diff --git a/src/nikonmn.cpp b/src/nikonmn.cpp index b78e724a..115d28a6 100644 --- a/src/nikonmn.cpp +++ b/src/nikonmn.cpp @@ -20,14 +20,14 @@ */ /* File: nikon1mn.cpp - Version: $Name: $ $Revision: 1.2 $ + Version: $Name: $ $Revision: 1.3 $ Author(s): Andreas Huggel (ahu) History: 17-May-04, ahu: created 25-May-04, ahu: combined all Nikon formats in one component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.2 $ $RCSfile: nikonmn.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.3 $ $RCSfile: nikonmn.cpp,v $") // ***************************************************************************** // included header files @@ -202,7 +202,7 @@ namespace Exiv2 { { } - int Nikon2MakerNote::readHeader(const char* buf, + int Nikon2MakerNote::readHeader(const byte* buf, long len, ByteOrder byteOrder) { @@ -219,7 +219,8 @@ namespace Exiv2 { int rc = 0; // Check the Nikon prefix if ( header_.size_ < 8 - || std::string(header_.pData_, 6) != std::string("Nikon\0", 6)) { + || std::string(reinterpret_cast(header_.pData_), 6) + != std::string("Nikon\0", 6)) { rc = 2; } return rc; @@ -387,7 +388,7 @@ namespace Exiv2 { absOffset_ = false; } - int Nikon3MakerNote::readHeader(const char* buf, + int Nikon3MakerNote::readHeader(const byte* buf, long len, ByteOrder byteOrder) { @@ -407,7 +408,8 @@ namespace Exiv2 { int rc = 0; // Check the Nikon prefix if ( header_.size_ < 18 - || std::string(header_.pData_, 6) != std::string("Nikon\0", 6)) { + || std::string(reinterpret_cast(header_.pData_), 6) + != std::string("Nikon\0", 6)) { rc = 2; } return rc; @@ -521,13 +523,14 @@ namespace Exiv2 { // free functions MakerNote* createNikonMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset) { // If there is no "Nikon" string it must be Nikon1 format - if (len < 6 || std::string(buf, 6) != std::string("Nikon\0", 6)) { + if (len < 6 || std::string(reinterpret_cast(buf), 6) + != std::string("Nikon\0", 6)) { return new Nikon1MakerNote(alloc); } // If the "Nikon" string is not followed by a TIFF header, we assume diff --git a/src/nikonmn.hpp b/src/nikonmn.hpp index 9477036e..9b740b42 100644 --- a/src/nikonmn.hpp +++ b/src/nikonmn.hpp @@ -28,10 +28,10 @@ Exif file format by TsuruZoh Tachibanaya.
Format 3: "EXIFutils Field Reference Guide". - @version $Name: $ $Revision: 1.2 $ + @version $Name: $ $Revision: 1.3 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 17-May-04, ahu: created + @date 17-May-04, ahu: created
25-May-04, ahu: combined all Nikon formats in one component */ #ifndef NIKONMN_HPP_ @@ -78,7 +78,7 @@ namespace Exiv2 { this copy and is responsible to delete it! */ MakerNote* createNikonMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset); @@ -173,7 +173,7 @@ namespace Exiv2 { //! @name Manipulators //@{ - int readHeader(const char* buf, + int readHeader(const byte* buf, long len, ByteOrder byteOrder); //@} @@ -227,7 +227,7 @@ namespace Exiv2 { //! @name Manipulators //@{ - int readHeader(const char* buf, + int readHeader(const byte* buf, long len, ByteOrder byteOrder); //@} diff --git a/src/sigmamn.cpp b/src/sigmamn.cpp index a3acb0de..63580cc6 100644 --- a/src/sigmamn.cpp +++ b/src/sigmamn.cpp @@ -20,7 +20,7 @@ */ /* File: sigmamn.cpp - Version: $Name: $ $Revision: 1.6 $ + Version: $Name: $ $Revision: 1.7 $ Author(s): Andreas Huggel (ahu) History: 02-Apr-04, ahu: created Credits: Sigma and Foveon MakerNote implemented according to the specification @@ -29,7 +29,7 @@ */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.6 $ $RCSfile: sigmamn.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.7 $ $RCSfile: sigmamn.cpp,v $") // ***************************************************************************** // included header files @@ -87,7 +87,7 @@ namespace Exiv2 { { } - int SigmaMakerNote::readHeader(const char* buf, + int SigmaMakerNote::readHeader(const byte* buf, long len, ByteOrder byteOrder) { @@ -108,8 +108,10 @@ namespace Exiv2 { int rc = 0; // Check the SIGMA or FOVEON prefix if ( header_.size_ < 10 - || std::string(header_.pData_, 8) != std::string("SIGMA\0\0\0", 8) - && std::string(header_.pData_, 8) != std::string("FOVEON\0\0", 8)) { + || std::string(reinterpret_cast(header_.pData_), 8) + != std::string("SIGMA\0\0\0", 8) + && std::string(reinterpret_cast(header_.pData_), 8) + != std::string("FOVEON\0\0", 8)) { rc = 2; } return rc; @@ -188,7 +190,7 @@ namespace Exiv2 { // free functions MakerNote* createSigmaMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset) diff --git a/src/sigmamn.hpp b/src/sigmamn.hpp index 110dfd4a..87e5cdd8 100644 --- a/src/sigmamn.hpp +++ b/src/sigmamn.hpp @@ -23,7 +23,7 @@ @brief Sigma and Foveon MakerNote implemented according to the specification SIGMA and FOVEON EXIF MakerNote Documentation by Foveon. - @version $Name: $ $Revision: 1.5 $ + @version $Name: $ $Revision: 1.6 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 02-Apr-04, ahu: created @@ -72,7 +72,7 @@ namespace Exiv2 { this copy and is responsible to delete it! */ MakerNote* createSigmaMakerNote(bool alloc, - const char* buf, + const byte* buf, long len, ByteOrder byteOrder, long offset); @@ -96,7 +96,7 @@ namespace Exiv2 { //! @name Manipulators //@{ - int readHeader(const char* buf, + int readHeader(const byte* buf, long len, ByteOrder byteOrder); //@} diff --git a/src/tags.cpp b/src/tags.cpp index 7b1ffd47..3a0ddec6 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -20,13 +20,13 @@ */ /* File: tags.cpp - Version: $Name: $ $Revision: 1.28 $ + Version: $Name: $ $Revision: 1.29 $ Author(s): Andreas Huggel (ahu) History: 15-Jan-04, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.28 $ $RCSfile: tags.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.29 $ $RCSfile: tags.cpp,v $") // ***************************************************************************** // included header files @@ -779,7 +779,7 @@ namespace Exiv2 { value.copy(buf.pData_, bigEndian); // Hack: Skip the leading 8-Byte character code, truncate // trailing '\0's and let the stream take care of the remainder - std::string userComment(buf.pData_ + 8, buf.size_ - 8); + std::string userComment(reinterpret_cast(buf.pData_) + 8, buf.size_ - 8); std::string::size_type pos = userComment.find_last_not_of('\0'); os << userComment.substr(0, pos + 1); } diff --git a/src/tags.hpp b/src/tags.hpp index 53e84f50..fea26d6a 100644 --- a/src/tags.hpp +++ b/src/tags.hpp @@ -21,10 +21,10 @@ /*! @file tags.hpp @brief Exif tag and type information - @version $Name: $ $Revision: 1.22 $ + @version $Name: $ $Revision: 1.23 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 15-Jan-04, ahu: created + @date 15-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component */ #ifndef TAGS_HPP_ diff --git a/src/types.cpp b/src/types.cpp index 8655ed29..c4e2c59f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -20,14 +20,14 @@ */ /* File: types.cpp - Version: $Name: $ $Revision: 1.9 $ + Version: $Name: $ $Revision: 1.10 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.9 $ $RCSfile: types.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.10 $ $RCSfile: types.cpp,v $") // ***************************************************************************** // included header files @@ -77,139 +77,139 @@ namespace Exiv2 { // ************************************************************************* // free functions - uint16 getUShort(const char* buf, ByteOrder byteOrder) + uint16 getUShort(const byte* buf, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - return (unsigned char)buf[1] << 8 | (unsigned char)buf[0]; + return (byte)buf[1] << 8 | (byte)buf[0]; } else { - return (unsigned char)buf[0] << 8 | (unsigned char)buf[1]; + return (byte)buf[0] << 8 | (byte)buf[1]; } } - uint32 getULong(const char* buf, ByteOrder byteOrder) + uint32 getULong(const byte* buf, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - return (unsigned char)buf[3] << 24 | (unsigned char)buf[2] << 16 - | (unsigned char)buf[1] << 8 | (unsigned char)buf[0]; + return (byte)buf[3] << 24 | (byte)buf[2] << 16 + | (byte)buf[1] << 8 | (byte)buf[0]; } else { - return (unsigned char)buf[0] << 24 | (unsigned char)buf[1] << 16 - | (unsigned char)buf[2] << 8 | (unsigned char)buf[3]; + return (byte)buf[0] << 24 | (byte)buf[1] << 16 + | (byte)buf[2] << 8 | (byte)buf[3]; } } - URational getURational(const char* buf, ByteOrder byteOrder) + URational getURational(const byte* buf, ByteOrder byteOrder) { uint32 nominator = getULong(buf, byteOrder); uint32 denominator = getULong(buf + 4, byteOrder); return std::make_pair(nominator, denominator); } - int16 getShort(const char* buf, ByteOrder byteOrder) + int16 getShort(const byte* buf, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - return (unsigned char)buf[1] << 8 | (unsigned char)buf[0]; + return (byte)buf[1] << 8 | (byte)buf[0]; } else { - return (unsigned char)buf[0] << 8 | (unsigned char)buf[1]; + return (byte)buf[0] << 8 | (byte)buf[1]; } } - int32 getLong(const char* buf, ByteOrder byteOrder) + int32 getLong(const byte* buf, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - return (unsigned char)buf[3] << 24 | (unsigned char)buf[2] << 16 - | (unsigned char)buf[1] << 8 | (unsigned char)buf[0]; + return (byte)buf[3] << 24 | (byte)buf[2] << 16 + | (byte)buf[1] << 8 | (byte)buf[0]; } else { - return (unsigned char)buf[0] << 24 | (unsigned char)buf[1] << 16 - | (unsigned char)buf[2] << 8 | (unsigned char)buf[3]; + return (byte)buf[0] << 24 | (byte)buf[1] << 16 + | (byte)buf[2] << 8 | (byte)buf[3]; } } - Rational getRational(const char* buf, ByteOrder byteOrder) + Rational getRational(const byte* buf, ByteOrder byteOrder) { int32 nominator = getLong(buf, byteOrder); int32 denominator = getLong(buf + 4, byteOrder); return std::make_pair(nominator, denominator); } - long us2Data(char* buf, uint16 s, ByteOrder byteOrder) + long us2Data(byte* buf, uint16 s, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - buf[0] = (char)(s & 0x00ff); - buf[1] = (char)((s & 0xff00) >> 8); + buf[0] = (byte)(s & 0x00ff); + buf[1] = (byte)((s & 0xff00) >> 8); } else { - buf[0] = (char)((s & 0xff00) >> 8); - buf[1] = (char)(s & 0x00ff); + buf[0] = (byte)((s & 0xff00) >> 8); + buf[1] = (byte)(s & 0x00ff); } return 2; } - long ul2Data(char* buf, uint32 l, ByteOrder byteOrder) + long ul2Data(byte* buf, uint32 l, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - buf[0] = (char)(l & 0x000000ff); - buf[1] = (char)((l & 0x0000ff00) >> 8); - buf[2] = (char)((l & 0x00ff0000) >> 16); - buf[3] = (char)((l & 0xff000000) >> 24); + buf[0] = (byte)(l & 0x000000ff); + buf[1] = (byte)((l & 0x0000ff00) >> 8); + buf[2] = (byte)((l & 0x00ff0000) >> 16); + buf[3] = (byte)((l & 0xff000000) >> 24); } else { - buf[0] = (char)((l & 0xff000000) >> 24); - buf[1] = (char)((l & 0x00ff0000) >> 16); - buf[2] = (char)((l & 0x0000ff00) >> 8); - buf[3] = (char)(l & 0x000000ff); + buf[0] = (byte)((l & 0xff000000) >> 24); + buf[1] = (byte)((l & 0x00ff0000) >> 16); + buf[2] = (byte)((l & 0x0000ff00) >> 8); + buf[3] = (byte)(l & 0x000000ff); } return 4; } - long ur2Data(char* buf, URational l, ByteOrder byteOrder) + long ur2Data(byte* buf, URational l, ByteOrder byteOrder) { long o = ul2Data(buf, l.first, byteOrder); o += ul2Data(buf+o, l.second, byteOrder); return o; } - long s2Data(char* buf, int16 s, ByteOrder byteOrder) + long s2Data(byte* buf, int16 s, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - buf[0] = (char)(s & 0x00ff); - buf[1] = (char)((s & 0xff00) >> 8); + buf[0] = (byte)(s & 0x00ff); + buf[1] = (byte)((s & 0xff00) >> 8); } else { - buf[0] = (char)((s & 0xff00) >> 8); - buf[1] = (char)(s & 0x00ff); + buf[0] = (byte)((s & 0xff00) >> 8); + buf[1] = (byte)(s & 0x00ff); } return 2; } - long l2Data(char* buf, int32 l, ByteOrder byteOrder) + long l2Data(byte* buf, int32 l, ByteOrder byteOrder) { if (byteOrder == littleEndian) { - buf[0] = (char)(l & 0x000000ff); - buf[1] = (char)((l & 0x0000ff00) >> 8); - buf[2] = (char)((l & 0x00ff0000) >> 16); - buf[3] = (char)((l & 0xff000000) >> 24); + buf[0] = (byte)(l & 0x000000ff); + buf[1] = (byte)((l & 0x0000ff00) >> 8); + buf[2] = (byte)((l & 0x00ff0000) >> 16); + buf[3] = (byte)((l & 0xff000000) >> 24); } else { - buf[0] = (char)((l & 0xff000000) >> 24); - buf[1] = (char)((l & 0x00ff0000) >> 16); - buf[2] = (char)((l & 0x0000ff00) >> 8); - buf[3] = (char)(l & 0x000000ff); + buf[0] = (byte)((l & 0xff000000) >> 24); + buf[1] = (byte)((l & 0x00ff0000) >> 16); + buf[2] = (byte)((l & 0x0000ff00) >> 8); + buf[3] = (byte)(l & 0x000000ff); } return 4; } - long r2Data(char* buf, Rational l, ByteOrder byteOrder) + long r2Data(byte* buf, Rational l, ByteOrder byteOrder) { long o = l2Data(buf, l.first, byteOrder); o += l2Data(buf+o, l.second, byteOrder); return o; } - void hexdump(std::ostream& os, const char* buf, long len, long offset) + void hexdump(std::ostream& os, const byte* buf, long len, long offset) { const std::string::size_type pos = 8 + 16 * 3 + 2; const std::string align(pos, ' '); @@ -221,7 +221,7 @@ namespace Exiv2 { << i + offset << " "; std::ostringstream ss; do { - unsigned char c = buf[i]; + byte c = buf[i]; os << std::setw(2) << std::setfill('0') << std::hex << (int)c << " "; ss << ((int)c >= 31 && (int)c < 127 ? buf[i] : '.'); diff --git a/src/types.hpp b/src/types.hpp index 7e4df2f7..2085d05d 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -21,10 +21,10 @@ /*! @file types.hpp @brief Type definitions for %Exiv2 and related functionality - @version $Name: $ $Revision: 1.15 $ + @version $Name: $ $Revision: 1.16 $ @author Andreas Huggel (ahu) ahuggel@gmx.net - @date 09-Jan-04, ahu: created + @date 09-Jan-04, ahu: created
11-Feb-04, ahu: isolated as a component */ #ifndef TYPES_HPP_ @@ -54,6 +54,8 @@ namespace Exiv2 { // ***************************************************************************** // type definitions + //! 1 byte unsigned integer type. + typedef unsigned char byte; //! 2 byte unsigned integer type. typedef unsigned short uint16; //! 4 byte unsigned integer type. @@ -132,33 +134,33 @@ namespace Exiv2 { //! Default constructor DataBuf() : size_(0), pData_(0) {} //! Constructor with an initial buffer size - DataBuf(long size) : size_(size), pData_(new char[size]) {} + DataBuf(long size) : size_(size), pData_(new byte[size]) {} //! Destructor, deletes the allocated buffer ~DataBuf() { delete[] pData_; } //! Allocate a data buffer of the given size void alloc(long size) - { delete[] pData_; size_ = size; pData_ = new char[size]; } + { if( size > size_ ){delete[] pData_; size_ = size; pData_ = new byte[size];} } //! The current size of the buffer long size_; //! Pointer to the buffer, 0 if none has been allocated - char* pData_; + byte* pData_; }; // class DataBuf // ***************************************************************************** // free functions //! Read a 2 byte unsigned short value from the data buffer - uint16 getUShort(const char* buf, ByteOrder byteOrder); + uint16 getUShort(const byte* buf, ByteOrder byteOrder); //! Read a 4 byte unsigned long value from the data buffer - uint32 getULong(const char* buf, ByteOrder byteOrder); + uint32 getULong(const byte* buf, ByteOrder byteOrder); //! Read an 8 byte unsigned rational value from the data buffer - URational getURational(const char* buf, ByteOrder byteOrder); + URational getURational(const byte* buf, ByteOrder byteOrder); //! Read a 2 byte signed short value from the data buffer - int16 getShort(const char* buf, ByteOrder byteOrder); + int16 getShort(const byte* buf, ByteOrder byteOrder); //! Read a 4 byte signed long value from the data buffer - int32 getLong(const char* buf, ByteOrder byteOrder); + int32 getLong(const byte* buf, ByteOrder byteOrder); //! Read an 8 byte signed rational value from the data buffer - Rational getRational(const char* buf, ByteOrder byteOrder); + Rational getRational(const byte* buf, ByteOrder byteOrder); //! Output operator for our fake rational std::ostream& operator<<(std::ostream& os, const Rational& r); @@ -173,39 +175,39 @@ namespace Exiv2 { @brief Convert an unsigned short to data, write the data to the buffer, return number of bytes written. */ - long us2Data(char* buf, uint16 s, ByteOrder byteOrder); + long us2Data(byte* buf, uint16 s, ByteOrder byteOrder); /*! @brief Convert an unsigned long to data, write the data to the buffer, return number of bytes written. */ - long ul2Data(char* buf, uint32 l, ByteOrder byteOrder); + long ul2Data(byte* buf, uint32 l, ByteOrder byteOrder); /*! @brief Convert an unsigned rational to data, write the data to the buffer, return number of bytes written. */ - long ur2Data(char* buf, URational l, ByteOrder byteOrder); + long ur2Data(byte* buf, URational l, ByteOrder byteOrder); /*! @brief Convert a signed short to data, write the data to the buffer, return number of bytes written. */ - long s2Data(char* buf, int16 s, ByteOrder byteOrder); + long s2Data(byte* buf, int16 s, ByteOrder byteOrder); /*! @brief Convert a signed long to data, write the data to the buffer, return number of bytes written. */ - long l2Data(char* buf, int32 l, ByteOrder byteOrder); + long l2Data(byte* buf, int32 l, ByteOrder byteOrder); /*! @brief Convert a signed rational to data, write the data to the buffer, return number of bytes written. */ - long r2Data(char* buf, Rational l, ByteOrder byteOrder); + long r2Data(byte* buf, Rational l, ByteOrder byteOrder); /*! @brief Print len bytes from buf in hex and ASCII format to the given stream, prefixed with the position in the buffer adjusted by offset. */ - void hexdump(std::ostream& os, const char* buf, long len, long offset =0); + void hexdump(std::ostream& os, const byte* buf, long len, long offset =0); /*! @brief Return the greatest common denominator of integers a and b. diff --git a/src/utils.cpp b/src/utils.cpp index 7df837dc..ac756225 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -20,13 +20,13 @@ */ /* File: utils.cpp - Version: $Name: $ $Revision: 1.8 $ + Version: $Name: $ $Revision: 1.9 $ Author(s): Andreas Huggel (ahu) History: 08-Dec-03, ahu: created */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.8 $ $RCSfile: utils.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.9 $ $RCSfile: utils.cpp,v $") // ***************************************************************************** // included header files @@ -96,15 +96,15 @@ int Getopt::getopt(int argc, char* const argv[], const std::string& optstring) if (path == "") return "."; // Strip trailing slashes std::string p = path; - while (p.length() > 1 && p[p.length()-1] == '/') { + while (p.length() > 1 && p[p.length()-1] == SEPERATOR_CHR) { p = p.substr(0, p.length()-1); } - if (p == "/") return "/"; - std::string::size_type idx = p.rfind('/'); + if (p == SEPERATOR_STR) return SEPERATOR_STR; + std::string::size_type idx = p.rfind(SEPERATOR_CHR); if (idx == std::string::npos) return "."; - if (idx == 0) return "/"; + if (idx == 0) return SEPERATOR_STR; p = p.substr(0, idx); - while (p.length() > 1 && p[p.length()-1] == '/') { + while (p.length() > 1 && p[p.length()-1] == SEPERATOR_CHR) { p = p.substr(0, p.length()-1); } return p; @@ -115,11 +115,11 @@ int Getopt::getopt(int argc, char* const argv[], const std::string& optstring) if (path == "") return "."; // Strip trailing slashes std::string p = path; - while (p.length() > 1 && p[p.length()-1] == '/') { + while (p.length() > 1 && p[p.length()-1] == SEPERATOR_CHR) { p = p.substr(0, p.length()-1); } - if (p == "/") return p; - std::string::size_type idx = p.rfind('/'); + if (p == SEPERATOR_STR) return p; + std::string::size_type idx = p.rfind(SEPERATOR_CHR); if (idx != std::string::npos) p = p.substr(idx+1); if (delsuffix) p = p.substr(0, p.length() - suffix(p).length()); return p; diff --git a/src/value.cpp b/src/value.cpp index 7f243022..2ceb23e0 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -20,14 +20,14 @@ */ /* File: value.cpp - Version: $Name: $ $Revision: 1.7 $ + Version: $Name: $ $Revision: 1.8 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.7 $ $RCSfile: value.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.8 $ $RCSfile: value.cpp,v $") // ***************************************************************************** // included header files @@ -108,10 +108,10 @@ namespace Exiv2 { return *this; } - void DataValue::read(const char* buf, long len, ByteOrder byteOrder) + void DataValue::read(const byte* buf, long len, ByteOrder byteOrder) { // byteOrder not needed - value_ = std::string(buf, len); + value_ = std::string(reinterpret_cast(buf), len); } void DataValue::read(const std::string& buf) @@ -124,10 +124,12 @@ namespace Exiv2 { } } - long DataValue::copy(char* buf, ByteOrder byteOrder) const + long DataValue::copy(byte* buf, ByteOrder byteOrder) const { // byteOrder not needed - return static_cast(value_.copy(buf, value_.size())); + return static_cast( + value_.copy(reinterpret_cast(buf), value_.size()) + ); } long DataValue::size() const @@ -144,7 +146,7 @@ namespace Exiv2 { { std::string::size_type end = value_.size(); for (std::string::size_type i = 0; i != end; ++i) { - os << static_cast(static_cast(value_[i])) + os << static_cast(static_cast(value_[i])) << " "; } return os; @@ -158,10 +160,10 @@ namespace Exiv2 { return *this; } - void AsciiValue::read(const char* buf, long len, ByteOrder byteOrder) + void AsciiValue::read(const byte* buf, long len, ByteOrder byteOrder) { // byteOrder not needed - value_ = std::string(buf, len); + value_ = std::string(reinterpret_cast(buf), len); } void AsciiValue::read(const std::string& buf) @@ -170,10 +172,12 @@ namespace Exiv2 { if (value_[value_.size()-1] != '\0') value_ += '\0'; } - long AsciiValue::copy(char* buf, ByteOrder byteOrder) const + long AsciiValue::copy(byte* buf, ByteOrder byteOrder) const { // byteOrder not needed - return static_cast(value_.copy(buf, value_.size())); + return static_cast( + value_.copy(reinterpret_cast(buf), value_.size()) + ); } long AsciiValue::size() const diff --git a/src/value.hpp b/src/value.hpp index e4752e8c..ebcc5d4b 100644 --- a/src/value.hpp +++ b/src/value.hpp @@ -21,7 +21,7 @@ /*! @file value.hpp @brief Value interface and concrete subclasses - @version $Name: $ $Revision: 1.11 $ + @version $Name: $ $Revision: 1.12 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created @@ -75,7 +75,7 @@ namespace Exiv2 { @param len Number of bytes in the data buffer @param byteOrder Applicable byte order (little or big endian). */ - virtual void read(const char* buf, long len, ByteOrder byteOrder) =0; + virtual void read(const byte* buf, long len, ByteOrder byteOrder) =0; /*! @brief Set the value from a string buffer. The format of the string corresponds to that of the write() method, i.e., a string @@ -106,7 +106,7 @@ namespace Exiv2 { @param byteOrder Applicable byte order (little or big endian). @return Number of characters written. */ - virtual long copy(char* buf, ByteOrder byteOrder) const =0; + virtual long copy(byte* buf, ByteOrder byteOrder) const =0; //! Return the number of components of the value virtual long count() const =0; //! Return the size of the value in bytes @@ -218,7 +218,7 @@ namespace Exiv2 { @param len Number of bytes in the data buffer @param byteOrder Byte order. Not needed. */ - virtual void read(const char* buf, + virtual void read(const byte* buf, long len, ByteOrder byteOrder =invalidByteOrder); //! Set the data from a string of integer values (e.g., "0 1 2 3") @@ -240,7 +240,7 @@ namespace Exiv2 { @param byteOrder Byte order. Not needed. @return Number of characters written. */ - virtual long copy(char* buf, ByteOrder byteOrder =invalidByteOrder) const; + virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) const; virtual long count() const { return size(); } virtual long size() const; virtual DataValue* clone() const; @@ -281,7 +281,7 @@ namespace Exiv2 { @param len Number of bytes in the data buffer @param byteOrder Byte order. Not needed. */ - virtual void read(const char* buf, + virtual void read(const byte* buf, long len, ByteOrder byteOrder =invalidByteOrder); /*! @@ -307,7 +307,7 @@ namespace Exiv2 { @param byteOrder Byte order. Not used. @return Number of characters written. */ - virtual long copy(char* buf, ByteOrder byteOrder =invalidByteOrder) const; + virtual long copy(byte* buf, ByteOrder byteOrder =invalidByteOrder) const; virtual long count() const { return size(); } virtual long size() const; virtual AsciiValue* clone() const; @@ -366,7 +366,7 @@ namespace Exiv2 { //@{ //! Assignment operator. ValueType& operator=(const ValueType& rhs); - virtual void read(const char* buf, long len, ByteOrder byteOrder); + virtual void read(const byte* buf, long len, ByteOrder byteOrder); /*! @brief Set the data from a string of values of type T (e.g., "0 1 2 3" or "1/2 1/3 1/4" depending on what T is). @@ -378,7 +378,7 @@ namespace Exiv2 { //! @name Accessors //@{ - virtual long copy(char* buf, ByteOrder byteOrder) const; + virtual long copy(byte* buf, ByteOrder byteOrder) const; virtual long count() const { return static_cast(value_.size()); } virtual long size() const; virtual ValueType* clone() const; @@ -432,40 +432,40 @@ namespace Exiv2 { @param byteOrder Applicable byte order (little or big endian). @return A value of type T. */ - template T getValue(const char* buf, ByteOrder byteOrder); + template T getValue(const byte* buf, ByteOrder byteOrder); // Specialization for a 2 byte unsigned short value. template<> - inline uint16 getValue(const char* buf, ByteOrder byteOrder) + inline uint16 getValue(const byte* buf, ByteOrder byteOrder) { return getUShort(buf, byteOrder); } // Specialization for a 4 byte unsigned long value. template<> - inline uint32 getValue(const char* buf, ByteOrder byteOrder) + inline uint32 getValue(const byte* buf, ByteOrder byteOrder) { return getULong(buf, byteOrder); } // Specialization for an 8 byte unsigned rational value. template<> - inline URational getValue(const char* buf, ByteOrder byteOrder) + inline URational getValue(const byte* buf, ByteOrder byteOrder) { return getURational(buf, byteOrder); } // Specialization for a 2 byte signed short value. template<> - inline int16 getValue(const char* buf, ByteOrder byteOrder) + inline int16 getValue(const byte* buf, ByteOrder byteOrder) { return getShort(buf, byteOrder); } // Specialization for a 4 byte signed long value. template<> - inline int32 getValue(const char* buf, ByteOrder byteOrder) + inline int32 getValue(const byte* buf, ByteOrder byteOrder) { return getLong(buf, byteOrder); } // Specialization for an 8 byte signed rational value. template<> - inline Rational getValue(const char* buf, ByteOrder byteOrder) + inline Rational getValue(const byte* buf, ByteOrder byteOrder) { return getRational(buf, byteOrder); } @@ -482,13 +482,13 @@ namespace Exiv2 { @param byteOrder Applicable byte order (little or big endian). @return The number of bytes written to the buffer. */ - template long toData(char* buf, T t, ByteOrder byteOrder); + template long toData(byte* buf, T t, ByteOrder byteOrder); /*! @brief Specialization to write an unsigned short to the data buffer. Return the number of bytes written. */ template<> - inline long toData(char* buf, uint16 t, ByteOrder byteOrder) + inline long toData(byte* buf, uint16 t, ByteOrder byteOrder) { return us2Data(buf, t, byteOrder); } @@ -497,7 +497,7 @@ namespace Exiv2 { Return the number of bytes written. */ template<> - inline long toData(char* buf, uint32 t, ByteOrder byteOrder) + inline long toData(byte* buf, uint32 t, ByteOrder byteOrder) { return ul2Data(buf, t, byteOrder); } @@ -506,7 +506,7 @@ namespace Exiv2 { Return the number of bytes written. */ template<> - inline long toData(char* buf, URational t, ByteOrder byteOrder) + inline long toData(byte* buf, URational t, ByteOrder byteOrder) { return ur2Data(buf, t, byteOrder); } @@ -515,7 +515,7 @@ namespace Exiv2 { Return the number of bytes written. */ template<> - inline long toData(char* buf, int16 t, ByteOrder byteOrder) + inline long toData(byte* buf, int16 t, ByteOrder byteOrder) { return s2Data(buf, t, byteOrder); } @@ -524,7 +524,7 @@ namespace Exiv2 { Return the number of bytes written. */ template<> - inline long toData(char* buf, int32 t, ByteOrder byteOrder) + inline long toData(byte* buf, int32 t, ByteOrder byteOrder) { return l2Data(buf, t, byteOrder); } @@ -533,7 +533,7 @@ namespace Exiv2 { Return the number of bytes written. */ template<> - inline long toData(char* buf, Rational t, ByteOrder byteOrder) + inline long toData(byte* buf, Rational t, ByteOrder byteOrder) { return r2Data(buf, t, byteOrder); } @@ -548,7 +548,7 @@ namespace Exiv2 { } template - void ValueType::read(const char* buf, long len, ByteOrder byteOrder) + void ValueType::read(const byte* buf, long len, ByteOrder byteOrder) { value_.clear(); for (long i = 0; i < len; i += TypeInfo::typeSize(typeId())) { @@ -568,7 +568,7 @@ namespace Exiv2 { } template - long ValueType::copy(char* buf, ByteOrder byteOrder) const + long ValueType::copy(byte* buf, ByteOrder byteOrder) const { long offset = 0; typename ValueList::const_iterator end = value_.end();