#606: Added Michael Ulbrich's patch for Exif and IPTC write-support.

v0.27.3
Andreas Huggel 16 years ago
parent e57c3e5e8a
commit 0da1d88115

@ -137,21 +137,9 @@ namespace Exiv2 {
{ {
} // PsdImage::PsdImage } // PsdImage::PsdImage
void PsdImage::setExifData(const ExifData& /*exifData*/)
{
// Todo: implement me!
throw(Error(32, "Exif metadata", "Photoshop"));
}
void PsdImage::setIptcData(const IptcData& /*iptcData*/)
{
// Todo: implement me!
throw(Error(32, "IPTC metadata", "Photoshop"));
}
void PsdImage::setComment(const std::string& /*comment*/) void PsdImage::setComment(const std::string& /*comment*/)
{ {
// Todo: implement me! // not supported
throw(Error(32, "Image comment", "Photoshop")); throw(Error(32, "Image comment", "Photoshop"));
} }
@ -243,7 +231,11 @@ namespace Exiv2 {
uint32_t resourceSize = getULong(buf, bigEndian); uint32_t resourceSize = getULong(buf, bigEndian);
uint32_t curOffset = io_->tell(); uint32_t curOffset = io_->tell();
processResourceBlock(resourceId, resourceSize); #ifdef DEBUG
std::cerr << std::hex << "resourceId: " << resourceId << std::dec << " length: " << resourceSize << std::hex << "\n";
#endif
readResourceBlock(resourceId, resourceSize);
resourceSize = (resourceSize + 1) & ~1; // pad to even resourceSize = (resourceSize + 1) & ~1; // pad to even
io_->seek(curOffset + resourceSize, BasicIo::beg); io_->seek(curOffset + resourceSize, BasicIo::beg);
resourcesLength -= (12 + resourceNameLength + resourceSize); resourcesLength -= (12 + resourceNameLength + resourceSize);
@ -251,7 +243,7 @@ namespace Exiv2 {
} // PsdImage::readMetadata } // PsdImage::readMetadata
void PsdImage::processResourceBlock(uint16_t resourceId, uint32_t resourceSize) void PsdImage::readResourceBlock(uint16_t resourceId, uint32_t resourceSize)
{ {
switch(resourceId) switch(resourceId)
{ {
@ -304,7 +296,7 @@ namespace Exiv2 {
break; break;
} }
} }
} } // PsdImage::readResourceBlock
void PsdImage::writeMetadata() void PsdImage::writeMetadata()
{ {
@ -333,8 +325,7 @@ namespace Exiv2 {
#endif #endif
// Ensure that this is the correct image type // Ensure that this is the correct image type
if (!isPsdType(*io_, true)) if (!isPsdType(*io_, true)) {
{
if (io_->error() || io_->eof()) throw Error(20); if (io_->error() || io_->eof()) throw Error(20);
throw Error(22); throw Error(22);
} }
@ -346,19 +337,13 @@ namespace Exiv2 {
// Get Photoshop header from original file // Get Photoshop header from original file
byte psd_head[26]; byte psd_head[26];
if (io_->read(psd_head, 26) != 26) if (io_->read(psd_head, 26) != 26) throw Error(3, "Photoshop");
{
throw Error(3, "Photoshop");
}
// Write Photoshop header data out to new PSD file // Write Photoshop header data out to new PSD file
if (outIo.write(psd_head, 26) != 26) throw Error(21); if (outIo.write(psd_head, 26) != 26) throw Error(21);
// Read colorDataLength from original PSD // Read colorDataLength from original PSD
if (io_->read(buf, 4) != 4) if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop");
{
throw Error(3, "Photoshop");
}
uint32_t colorDataLength = getULong(buf, bigEndian); uint32_t colorDataLength = getULong(buf, bigEndian);
@ -374,10 +359,7 @@ namespace Exiv2 {
while (readTotal < colorDataLength) { while (readTotal < colorDataLength) {
toRead = static_cast<long>(colorDataLength - readTotal) < lbuf.size_ toRead = static_cast<long>(colorDataLength - readTotal) < lbuf.size_
? colorDataLength - readTotal : lbuf.size_; ? colorDataLength - readTotal : lbuf.size_;
if (io_->read(lbuf.pData_, toRead) != toRead) if (io_->read(lbuf.pData_, toRead) != toRead) throw Error(3, "Photoshop");
{
throw Error(3, "Photoshop");
}
readTotal += toRead; readTotal += toRead;
if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21); if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21);
} }
@ -386,10 +368,7 @@ namespace Exiv2 {
uint32_t resLenOffset = io_->tell(); // remember for later update uint32_t resLenOffset = io_->tell(); // remember for later update
// Read length of all resource blocks from original PSD // Read length of all resource blocks from original PSD
if (io_->read(buf, 4) != 4) if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop");
{
throw Error(3, "Photoshop");
}
uint32_t oldResLength = getULong(buf, bigEndian); uint32_t oldResLength = getULong(buf, bigEndian);
uint32_t newResLength = 0; uint32_t newResLength = 0;
@ -397,133 +376,128 @@ namespace Exiv2 {
// Write oldResLength (will be updated later) // Write oldResLength (will be updated later)
ul2Data(buf, oldResLength, bigEndian); ul2Data(buf, oldResLength, bigEndian);
if (outIo.write(buf, 4) != 4) throw Error(21); if (outIo.write(buf, 4) != 4) throw Error(21);
#ifdef DEBUG #ifdef DEBUG
std::cerr << std::dec << "oldResLength: " << oldResLength << "\n"; std::cerr << std::dec << "oldResLength: " << oldResLength << "\n";
#endif #endif
// Write metadata resource blocks: IPTC_NAA, (TODO: ExifInfo, XMPPacket)
if (iptcData_.count() > 0) {
DataBuf rawIptc = IptcParser::encode(iptcData_);
if (rawIptc.size_ > 0) {
#ifdef DEBUG
std::cerr << std::dec << "Writing IPTC_NAA: size: " << rawIptc.size_ << "\n";
#endif
ul2Data(buf, kPhotoshopResourceType, bigEndian);
if (outIo.write(buf, 4) != 4) throw Error(21);
us2Data(buf, kPhotoshopResourceID_IPTC_NAA, bigEndian);
if (outIo.write(buf, 2) != 2) throw Error(21);
us2Data(buf, 0, bigEndian); // NULL resource name
if (outIo.write(buf, 2) != 2) throw Error(21);
ul2Data(buf, rawIptc.size_, bigEndian);
if (outIo.write(buf, 4) != 4) throw Error(21);
// Write encoded Iptc data
if (outIo.write(rawIptc.pData_, rawIptc.size_) != rawIptc.size_) throw Error(21);
newResLength += rawIptc.size_ + 12;
if (rawIptc.size_ & 1) // even padding
{
buf[0] = 0;
if (outIo.write(buf, 1) != 1) throw Error(21);
newResLength++;
}
}
}
// Iterate over original resource blocks and copy those not already processed // Iterate over original resource blocks.
while (oldResLength > 0) // Replace or insert IPTC, EXIF and XMP
{ // Original resource blocks assumed to be sorted ASC
if (io_->read(buf, 8) != 8)
{ bool iptcDone = false;
throw Error(3, "Photoshop"); bool exifDone = false;
} bool xmpDone = false;
while (oldResLength > 0) {
if (io_->read(buf, 8) != 8) throw Error(3, "Photoshop");
// read resource type and ID // read resource type and ID
uint32_t resourceType = getULong(buf, bigEndian); uint32_t resourceType = getULong(buf, bigEndian);
uint16_t resourceId = getUShort(buf + 4, bigEndian);
#ifdef DEBUG if (resourceType != kPhotoshopResourceType) {
std::cerr << std::hex << "resourceId: " << resourceId << "\n";
std::cerr << std::dec;
#endif
if (resourceType != kPhotoshopResourceType)
{
break; // bad resource type break; // bad resource type
} }
uint16_t resourceId = getUShort(buf + 4, bigEndian);
uint32_t resourceNameLength = buf[6]; uint32_t resourceNameLength = buf[6];
uint32_t adjResourceNameLen = resourceNameLength & ~1; uint32_t adjResourceNameLen = resourceNameLength & ~1;
unsigned char resourceNameFirstChar = buf[7]; unsigned char resourceNameFirstChar = buf[7];
// read rest of resource name, plus any padding // read rest of resource name, plus any padding
DataBuf resName(256); DataBuf resName(256);
if (io_->read(resName.pData_, adjResourceNameLen) != adjResourceNameLen) if ( io_->read(resName.pData_, adjResourceNameLen)
{ != static_cast<long>(adjResourceNameLen)) throw Error(3, "Photoshop");
throw Error(3, "Photoshop");
}
// read resource size (actual length w/o padding!) // read resource size (actual length w/o padding!)
if (io_->read(buf, 4) != 4) if (io_->read(buf, 4) != 4) throw Error(3, "Photoshop");
{
throw Error(3, "Photoshop");
}
uint32_t resourceSize = getULong(buf, bigEndian); uint32_t resourceSize = getULong(buf, bigEndian);
uint32_t curOffset = io_->tell(); uint32_t curOffset = io_->tell();
switch(resourceId) // Write IPTC_NAA resource block
{ if ( resourceId == kPhotoshopResourceID_IPTC_NAA
case kPhotoshopResourceID_IPTC_NAA: || (resourceId > kPhotoshopResourceID_IPTC_NAA && iptcDone == false)) {
{ newResLength += writeIptcData(iptcData_, outIo);
resourceSize = (resourceSize + 1) & ~1; // adjust for padding resourceSize = (resourceSize + 1) & ~1; // adjust for padding
break; // already processed iptcDone = true;
} }
/*
case kPhotoshopResourceID_ExifInfo:
{
// TODO: skip here, if exiv2 writes it's own EXIF data
break;
}
case kPhotoshopResourceID_XMPPacket: // Write ExifInfo resource block
{ else if ( resourceId == kPhotoshopResourceID_ExifInfo
// TODO: skip here, if exiv2 writes it's own XMP data || (resourceId > kPhotoshopResourceID_ExifInfo && exifDone == false)) {
break; newResLength += writeExifData(exifData_, outIo);
} resourceSize = (resourceSize + 1) & ~1; // adjust for padding
*/ exifDone = true;
default: }
{
// Copy resource block to new PSD file // Write XMPpacket resource block
ul2Data(buf, kPhotoshopResourceType, bigEndian); else if ( resourceId == kPhotoshopResourceID_XMPPacket
if (outIo.write(buf, 4) != 4) throw Error(21); || (resourceId > kPhotoshopResourceID_XMPPacket && xmpDone == false)) {
us2Data(buf, resourceId, bigEndian); newResLength += writeXmpData(xmpData_, outIo);
if (outIo.write(buf, 2) != 2) throw Error(21); resourceSize = (resourceSize + 1) & ~1; // adjust for padding
// Write resource name as Pascal string xmpDone = true;
buf[0] = resourceNameLength & 0x000f; }
if (outIo.write(buf, 1) != 1) throw Error(21);
buf[0] = resourceNameFirstChar; // Copy all other resource blocks
if (outIo.write(buf, 1) != 1) throw Error(21); if ( resourceId != kPhotoshopResourceID_IPTC_NAA
if (outIo.write(resName.pData_, adjResourceNameLen) != adjResourceNameLen) throw Error(21); && resourceId != kPhotoshopResourceID_ExifInfo
ul2Data(buf, resourceSize, bigEndian); && resourceId != kPhotoshopResourceID_XMPPacket) {
if (outIo.write(buf, 4) != 4) throw Error(21); #ifdef DEBUG
std::cerr << std::hex << "copy : resourceId: " << resourceId << "\n";
readTotal = 0; std::cerr << std::dec;
toRead = 0; #endif
resourceSize = (resourceSize + 1) & ~1; // pad to even // Copy resource block to new PSD file
while (readTotal < resourceSize) { ul2Data(buf, kPhotoshopResourceType, bigEndian);
toRead = static_cast<long>(resourceSize - readTotal) < lbuf.size_ if (outIo.write(buf, 4) != 4) throw Error(21);
? resourceSize - readTotal : lbuf.size_; us2Data(buf, resourceId, bigEndian);
if (io_->read(lbuf.pData_, toRead) != toRead) if (outIo.write(buf, 2) != 2) throw Error(21);
{ // Write resource name as Pascal string
throw Error(3, "Photoshop"); buf[0] = resourceNameLength & 0x000f;
} if (outIo.write(buf, 1) != 1) throw Error(21);
readTotal += toRead; buf[0] = resourceNameFirstChar;
if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21); if (outIo.write(buf, 1) != 1) throw Error(21);
if ( outIo.write(resName.pData_, adjResourceNameLen)
!= static_cast<long>(adjResourceNameLen)) throw Error(21);
ul2Data(buf, resourceSize, bigEndian);
if (outIo.write(buf, 4) != 4) throw Error(21);
readTotal = 0;
toRead = 0;
resourceSize = (resourceSize + 1) & ~1; // pad to even
while (readTotal < resourceSize) {
toRead = static_cast<long>(resourceSize - readTotal) < lbuf.size_
? resourceSize - readTotal : lbuf.size_;
if (io_->read(lbuf.pData_, toRead) != toRead) {
throw Error(3, "Photoshop");
} }
if (outIo.error()) throw Error(21); readTotal += toRead;
newResLength += resourceSize + adjResourceNameLen + 12; if (outIo.write(lbuf.pData_, toRead) != toRead) throw Error(21);
break;
} }
if (outIo.error()) throw Error(21);
newResLength += resourceSize + adjResourceNameLen + 12;
} }
io_->seek(curOffset + resourceSize, BasicIo::beg); io_->seek(curOffset + resourceSize, BasicIo::beg);
oldResLength -= (12 + adjResourceNameLen + resourceSize); oldResLength -= (12 + adjResourceNameLen + resourceSize);
} }
// Append IPTC_NAA resource block, if not yet written
if (iptcDone == false) {
newResLength += writeIptcData(iptcData_, outIo);
iptcDone = true;
}
// Append ExifInfo resource block, if not yet written
if (exifDone == false) {
newResLength += writeExifData(exifData_, outIo);
exifDone = true;
}
// Append XmpPacket resource block, if not yet written
if (xmpDone == false) {
newResLength += writeXmpData(xmpData_, outIo);
xmpDone = true;
}
// Copy remaining data // Copy remaining data
long readSize = 0; long readSize = 0;
while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) { while ((readSize=io_->read(lbuf.pData_, lbuf.size_))) {
@ -541,6 +515,126 @@ namespace Exiv2 {
} // PsdImage::doWriteMetadata } // PsdImage::doWriteMetadata
uint32_t PsdImage::writeIptcData(const IptcData& iptcData, BasicIo& out) const
{
uint32_t resLength = 0;
byte buf[8];
if (iptcData.count() > 0) {
DataBuf rawIptc = IptcParser::encode(iptcData);
if (rawIptc.size_ > 0) {
#ifdef DEBUG
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_IPTC_NAA << "\n";
std::cerr << std::dec << "Writing IPTC_NAA: size: " << rawIptc.size_ << "\n";
#endif
ul2Data(buf, kPhotoshopResourceType, bigEndian);
if (out.write(buf, 4) != 4) throw Error(21);
us2Data(buf, kPhotoshopResourceID_IPTC_NAA, bigEndian);
if (out.write(buf, 2) != 2) throw Error(21);
us2Data(buf, 0, bigEndian); // NULL resource name
if (out.write(buf, 2) != 2) throw Error(21);
ul2Data(buf, rawIptc.size_, bigEndian);
if (out.write(buf, 4) != 4) throw Error(21);
// Write encoded Iptc data
if (out.write(rawIptc.pData_, rawIptc.size_) != rawIptc.size_) throw Error(21);
resLength += rawIptc.size_ + 12;
if (rawIptc.size_ & 1) // even padding
{
buf[0] = 0;
if (out.write(buf, 1) != 1) throw Error(21);
resLength++;
}
}
}
return resLength;
} // PsdImage::writeIptcData
uint32_t PsdImage::writeExifData(const ExifData& exifData, BasicIo& out)
{
uint32_t resLength = 0;
byte buf[8];
if (exifData.count() > 0) {
Blob blob;
ByteOrder bo = byteOrder();
if (bo == invalidByteOrder) {
bo = littleEndian;
setByteOrder(bo);
}
ExifParser::encode(blob, bo, exifData);
if (blob.size() > 0) {
#ifdef DEBUG
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_ExifInfo << "\n";
std::cerr << std::dec << "Writing ExifInfo: size: " << blob.size() << "\n";
#endif
ul2Data(buf, kPhotoshopResourceType, bigEndian);
if (out.write(buf, 4) != 4) throw Error(21);
us2Data(buf, kPhotoshopResourceID_ExifInfo, bigEndian);
if (out.write(buf, 2) != 2) throw Error(21);
us2Data(buf, 0, bigEndian); // NULL resource name
if (out.write(buf, 2) != 2) throw Error(21);
ul2Data(buf, blob.size(), bigEndian);
if (out.write(buf, 4) != 4) throw Error(21);
// Write encoded Exif data
if (out.write(&blob[0], blob.size()) != static_cast<long>(blob.size())) throw Error(21);
resLength += blob.size() + 12;
if (blob.size() & 1) // even padding
{
buf[0] = 0;
if (out.write(buf, 1) != 1) throw Error(21);
resLength++;
}
}
}
return resLength;
} // PsdImage::writeExifData
uint32_t PsdImage::writeXmpData(const XmpData& xmpData, BasicIo& out) const
{
std::string xmpPacket;
uint32_t resLength = 0;
byte buf[8];
#ifdef DEBUG
std::cerr << "writeXmpFromPacket(): " << writeXmpFromPacket() << "\n";
#endif
// writeXmpFromPacket(true);
if (writeXmpFromPacket() == false) {
if (XmpParser::encode(xmpPacket, xmpData) > 1) {
#ifndef SUPPRESS_WARNINGS
std::cerr << "Error: Failed to encode XMP metadata.\n";
#endif
}
}
if (xmpPacket.size() > 0) {
#ifdef DEBUG
std::cerr << std::hex << "write: resourceId: " << kPhotoshopResourceID_XMPPacket << "\n";
std::cerr << std::dec << "Writing XMPPacket: size: " << xmpPacket.size() << "\n";
#endif
ul2Data(buf, kPhotoshopResourceType, bigEndian);
if (out.write(buf, 4) != 4) throw Error(21);
us2Data(buf, kPhotoshopResourceID_XMPPacket, bigEndian);
if (out.write(buf, 2) != 2) throw Error(21);
us2Data(buf, 0, bigEndian); // NULL resource name
if (out.write(buf, 2) != 2) throw Error(21);
ul2Data(buf, xmpPacket.size(), bigEndian);
if (out.write(buf, 4) != 4) throw Error(21);
// Write XMPPacket
if (out.write(reinterpret_cast<const byte*>(xmpPacket.data()), static_cast<long>(xmpPacket.size()))
!= static_cast<long>(xmpPacket.size())) throw Error(21);
if (out.error()) throw Error(21);
resLength += xmpPacket.size() + 12;
if (xmpPacket.size() & 1) // even padding
{
buf[0] = 0;
if (out.write(buf, 1) != 1) throw Error(21);
resLength++;
}
}
return resLength;
} // PsdImage::writeXmpData
// ************************************************************************* // *************************************************************************
// free functions // free functions

@ -87,24 +87,9 @@ namespace Exiv2 {
//! @name Manipulators //! @name Manipulators
//@{ //@{
void readMetadata(); void readMetadata();
/*!
@brief Todo: Write metadata back to the image. This method is not
yet implemented. Calling it will throw an Error(31).
*/
void writeMetadata(); void writeMetadata();
/*! /*!
@brief Todo: Not supported yet. Calling this function will throw @brief Not supported. Calling this function will throw an Error(32).
an instance of Error(32).
*/
void setExifData(const ExifData& exifData);
/*!
@brief Todo: Not supported yet. Calling this function will throw
an instance of Error(32).
*/
void setIptcData(const IptcData& iptcData);
/*!
@brief Todo: Not supported yet. Calling this function will throw
an instance of Error(32).
*/ */
void setComment(const std::string& comment); void setComment(const std::string& comment);
//@} //@}
@ -128,7 +113,7 @@ namespace Exiv2 {
private: private:
//! @name Manipulators //! @name Manipulators
//@{ //@{
EXV_DLLLOCAL void processResourceBlock(uint16_t resourceId, uint32_t resourceSize); EXV_DLLLOCAL void readResourceBlock(uint16_t resourceId, uint32_t resourceSize);
/*! /*!
@brief Provides the main implementation of writeMetadata() by @brief Provides the main implementation of writeMetadata() by
writing all buffered metadata to the provided BasicIo. writing all buffered metadata to the provided BasicIo.
@ -137,6 +122,13 @@ namespace Exiv2 {
@return 4 if opening or writing to the associated BasicIo fails @return 4 if opening or writing to the associated BasicIo fails
*/ */
EXV_DLLLOCAL void doWriteMetadata(BasicIo& oIo); EXV_DLLLOCAL void doWriteMetadata(BasicIo& oIo);
EXV_DLLLOCAL uint32_t writeExifData(const ExifData& exifData, BasicIo& out);
//@}
//! @name Accessors
//@{
EXV_DLLLOCAL uint32_t writeIptcData(const IptcData& iptcData, BasicIo& out) const;
EXV_DLLLOCAL uint32_t writeXmpData(const XmpData& xmpData, BasicIo& out) const;
//@} //@}
}; // class PsdImage }; // class PsdImage

Loading…
Cancel
Save