diff --git a/src/Makefile b/src/Makefile index 173ec269..bb323d87 100644 --- a/src/Makefile +++ b/src/Makefile @@ -90,7 +90,8 @@ CCSRC = basicio.cpp \ nikonmn.cpp \ olympusmn.cpp \ orfimage.cpp \ - panasonicmn.cpp + panasonicmn.cpp \ + pgfimage.cpp ifdef HAVE_LIBZ CCSRC += pngimage.cpp \ pngchunk.cpp diff --git a/src/image.cpp b/src/image.cpp index 24056a78..61f5844b 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -60,6 +60,7 @@ EXIV2_RCSID("@(#) $Id$") #include "bmpimage.hpp" #include "jp2image.hpp" #include "rw2image.hpp" +#include "pgfimage.hpp" #include "xmpsidecar.hpp" // + standard includes @@ -117,6 +118,7 @@ namespace { { ImageType::bmp, newBmpInstance, isBmpType, amNone, amNone, amNone, amNone }, { ImageType::jp2, newJp2Instance, isJp2Type, amReadWrite, amReadWrite, amReadWrite, amNone }, { ImageType::rw2, newRw2Instance, isRw2Type, amRead, amRead, amRead, amNone }, + { ImageType::pgf, newPgfInstance, isPgfType, amReadWrite, amReadWrite, amReadWrite, amNone }, // End of list marker { ImageType::none, 0, 0, amNone, amNone, amNone, amNone } }; diff --git a/src/pgfimage.cpp b/src/pgfimage.cpp new file mode 100644 index 00000000..cefbbf32 --- /dev/null +++ b/src/pgfimage.cpp @@ -0,0 +1,306 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2009 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: pgfimage.cpp + Version: $Rev: 1750 $ + Author(s): Gilles Caulier (cgilles) + History: 16-Jun-09, gc: submitted + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id: pgfimage.cpp 1750 2009-02-16 14:30:51Z ahuggel $") + +// ***************************************************************************** + +#define DEBUG 1 + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "pgfimage.hpp" +#include "image.hpp" +#include "pngimage.hpp" +#include "basicio.hpp" +#include "error.hpp" +#include "futils.hpp" + +// + standard includes +#include +#include +#include +#include + +// Signature from front of PGF file +const unsigned char pgfSignature[3] = { 0x50, 0x47, 0x46 }; + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + PgfImage::PgfImage(BasicIo::AutoPtr io, bool /*create*/) + : Image(ImageType::pgf, mdExif | mdIptc, io) + { + } // PgfImage::PgfImage + + void PgfImage::readMetadata() + { +#ifdef DEBUG + std::cerr << "Exiv2::PgfImage::readMetadata: Reading PGF file " << io_->path() << "\n"; +#endif + if (io_->open() != 0) + { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (!isPgfType(*io_, true)) + { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "PGF"); + } + clearMetadata(); + + readPgfMagicNumber(*io_); + + uint32_t headerSize = readPgfHeaderSize(*io_); + + readPgfHeaderStructure(*io_, &pixelWidth_, &pixelHeight_); + + // And now, the most interresting, the user data byte array where metadata are stored as small image. + + long size = 8 + headerSize - io_->tell(); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::readMetadata: Found Image data (" << size << " bytes)\n"; +#endif + + if (size < 0) throw Error(20); + if (size == 0) return; + + DataBuf imgData(size); + std::memset(imgData.pData_, 0x0, imgData.size_); + long bufRead = io_->read(imgData.pData_, imgData.size_); + if (io_->error()) throw Error(14); + if (bufRead != imgData.size_) throw Error(20); + + Image::AutoPtr image = Exiv2::ImageFactory::open(imgData.pData_, imgData.size_); + image->readMetadata(); + exifData() = image->exifData(); + iptcData() = image->iptcData(); + xmpData() = image->xmpData(); + + } // PgfImage::readMetadata + + void PgfImage::writeMetadata() + { + if (io_->open() != 0) + { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + BasicIo::AutoPtr tempIo(io_->temporary()); // may throw + assert (tempIo.get() != 0); + + doWriteMetadata(*tempIo); // may throw + io_->close(); + io_->transfer(*tempIo); // may throw + + } // PgfImage::writeMetadata + + void PgfImage::doWriteMetadata(BasicIo& outIo) + { + if (!io_->isopen()) throw Error(20); + if (!outIo.isopen()) throw Error(21); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::doWriteMetadata: Writing PGF file " << io_->path() << "\n"; + std::cout << "Exiv2::PgfImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; +#endif + + // Ensure that this is the correct image type + if (!isPgfType(*io_, true)) + { + if (io_->error() || io_->eof()) throw Error(20); + throw Error(22); + } + + // Ensure PGF version. + byte mnb = readPgfMagicNumber(*io_); + + readPgfHeaderSize(*io_); + + int w, h; + DataBuf header = readPgfHeaderStructure(*io_, &w, &h); + + Image::AutoPtr img = ImageFactory::create(ImageType::png); + + img->setExifData(exifData_); + img->setIptcData(iptcData_); + img->setXmpData(xmpData_); + img->writeMetadata(); + int imgSize = img->io().size(); + DataBuf imgBuf = img->io().read(imgSize); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::doWriteMetadata: Creating image to host metadata (" << imgSize << " bytes)\n"; +#endif + + //--------------------------------------------------------------- + + // Write PGF Signature. + if (outIo.write(pgfSignature, 3) != 3) throw Error(21); + + // Write Magic number. + if (outIo.putb(mnb) == EOF) throw Error(21); + + // Write new Header size. + uint32_t newHeaderSize = header.size_ + imgSize; + DataBuf buffer(4); + memcpy (buffer.pData_, &newHeaderSize, 4); + if (outIo.write(buffer.pData_, 4) != 4) throw Error(21); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage: new PGF header size : " << newHeaderSize << " bytes\n"; + + printf("%x\n", buffer.pData_[0]); + printf("%x\n", buffer.pData_[1]); + printf("%x\n", buffer.pData_[2]); + printf("%x\n", buffer.pData_[3]); +#endif + + // Write Header data. + if (outIo.write(header.pData_, header.size_) != header.size_) throw Error(21); + + // Write new metadata byte array. + if (outIo.write(imgBuf.pData_, imgBuf.size_) != imgBuf.size_) throw Error(21); + + // Copy the rest of PGF image data. + + DataBuf buf(4096); + long readSize = 0; + while ((readSize=io_->read(buf.pData_, buf.size_))) + { + if (outIo.write(buf.pData_, readSize) != readSize) throw Error(21); + } + if (outIo.error()) throw Error(21); + + } // PgfImage::doWriteMetadata + + byte PgfImage::readPgfMagicNumber(BasicIo& iIo) + { + byte b = iIo.getb(); + if (iIo.error()) throw Error(14); + + if (b < 0x36) // 0x36 = '6'. + { + // Not right Magick version. +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::readMetadata: wrong Magick number\n"; +#endif + } + + return b; + } // PgfImage::readPgfMagicNumber + + uint32_t PgfImage::readPgfHeaderSize(BasicIo& iIo) + { + DataBuf buffer(4); + long bufRead = iIo.read(buffer.pData_, buffer.size_); + if (iIo.error()) throw Error(14); + if (bufRead != buffer.size_) throw Error(20); + + uint32_t headerSize = 0; + memcpy (&headerSize, buffer.pData_, 4); // TODO : check endianness. + if (headerSize <= 0 ) throw Error(22); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage: PGF header size : " << headerSize << " bytes\n"; +#endif + + return headerSize; + } // PgfImage::readPgfHeaderSize + + DataBuf PgfImage::readPgfHeaderStructure(BasicIo& iIo, int* width, int* height) + { + DataBuf header(16); + long bufRead = iIo.read(header.pData_, header.size_); + if (iIo.error()) throw Error(14); + if (bufRead != header.size_) throw Error(20); + + memcpy(width, &header.pData_[0], 4); // TODO : check endianness. + memcpy(height, &header.pData_[4], 4); // TODO : check endianness. + + /* NOTE: properties not yet used + byte nLevels = buffer.pData_[8]; + byte quality = buffer.pData_[9]; + byte bpp = buffer.pData_[10]; + byte channels = buffer.pData_[11]; + */ + byte mode = header.pData_[12]; + + if (mode == 2) // Indexed color image. We pass color table (256 * 3 bytes). + { + header.alloc(16 + 256*3); + + bufRead = iIo.read(&header.pData_[16], 256*3); + if (iIo.error()) throw Error(14); + if (bufRead != 256*3) throw Error(20); + } + + return header; + } // PgfImage::readPgfHeaderStructure + + // ************************************************************************* + // free functions + Image::AutoPtr newPgfInstance(BasicIo::AutoPtr io, bool create) + { + Image::AutoPtr image(new PgfImage(io, create)); + if (!image->good()) + { + image.reset(); + } + return image; + } + + bool isPgfType(BasicIo& iIo, bool advance) + { + const int32_t len = 3; + byte buf[len]; + iIo.read(buf, len); + if (iIo.error() || iIo.eof()) + { + return false; + } + int rc = memcmp(buf, pgfSignature, 3); + if (!advance || rc != 0) + { + iIo.seek(-len, BasicIo::cur); + } + + return rc == 0; + } +} // namespace Exiv2 diff --git a/src/pgfimage.hpp b/src/pgfimage.hpp new file mode 100644 index 00000000..35a71c5c --- /dev/null +++ b/src/pgfimage.hpp @@ -0,0 +1,138 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2009 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file pgfimage.hpp + @brief PGF image, implemented using the following references: + PGF specification from libpgf web site
+ @version $Rev: 1750 $ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @author Gilles Caulier (cgilles) + caulier dot gilles at gmail dot com + @date 16-Jun-09, gc: submitted + */ +#ifndef PGFIMAGE_HPP_ +#define PGFIMAGE_HPP_ + +// ***************************************************************************** +// included header files +#include "image.hpp" +#include "basicio.hpp" +#include "types.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 +{ + +// ***************************************************************************** +// class definitions + + // Add PGF to the supported image formats + namespace ImageType + { + const int pgf = 17; //!< PGF image type (see class PgfImage) + } + + /*! + @brief Class to access PGF images. Exif and IPTC metadata are supported + directly. + */ + class EXIV2API PgfImage : public Image { + public: + //! @name Creators + //@{ + /*! + @brief Constructor that can either open an existing PGF image or create + a new image from scratch. If a new image is to be created, any + existing data 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 io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + @param create Specifies if an existing image should be read (false) + or if a new file should be created (true). + This option is not yet implemented. + */ + PgfImage(BasicIo::AutoPtr io, bool create); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const { return "image/pgf"; } + //@} + + private: + //! @name NOT implemented + //@{ + //! Copy constructor + PgfImage(const PgfImage& rhs); + //! Assignment operator + PgfImage& operator=(const PgfImage& rhs); + /*! + @brief Provides the main implementation of writeMetadata() by + writing all buffered metadata to the provided BasicIo. + @param oIo BasicIo instance to write to (a temporary location). + + @return 4 if opening or writing to the associated BasicIo fails + */ + EXV_DLLLOCAL void doWriteMetadata(BasicIo& oIo); + //! Read Magick number. Only version >= 6 is supported. + byte readPgfMagicNumber(BasicIo& iIo); + //! Read PGF Header size encoded in 32 bits integer. + uint32_t readPgfHeaderSize(BasicIo& iIo); + //! Read header structure. + DataBuf readPgfHeaderStructure(BasicIo& iIo, int* width, int* height); + //@} + + }; // class PgfImage + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new PgfImage instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + EXIV2API Image::AutoPtr newPgfInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a PGF image. + EXIV2API bool isPgfType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // #ifndef PGFIMAGE_HPP_