void XmpSidecar::writeMetadata() { if (io_->open() != 0) { throw Error(9, io_->path(), strError()); } IoCloser closer(*io_); if (writeXmpFromPacket() == false) { copyExifToXmp(exifData_, xmpData_); copyIptcToXmp(iptcData_, xmpData_); if (XmpParser::encode(xmpPacket_, xmpData_, XmpParser::omitPacketWrapper|XmpParser::useCompactFormat) > 1) { #ifndef SUPPRESS_WARNINGS EXV_ERROR << "Failed to encode XMP metadata.\n"; #endif } } if (xmpPacket_.size() > 0) { if (xmpPacket_.substr(0, 5) != "<?xml") { xmpPacket_ = xmlHeader + xmpPacket_; } BasicIo::AutoPtr tempIo(io_->temporary()); // may throw assert(tempIo.get() != 0); // Write XMP packet if ( tempIo->write(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size())) != static_cast<long>(xmpPacket_.size())) throw Error(21); if (tempIo->error()) throw Error(21); io_->close(); io_->transfer(*tempIo); // may throw } } // XmpSidecar::writeMetadata
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
void XmpSidecar::writeMetadata() { if (io_->open() != 0) { throw Error(9, io_->path(), strError()); } IoCloser closer(*io_); if (writeXmpFromPacket() == false) { copyExifToXmp(exifData_, xmpData_); copyIptcToXmp(iptcData_, xmpData_); // #1112 - restore dates if they lost their TZ info for ( Exiv2::Dictionary_i it = dates_.begin() ; it != dates_.end() ; it++) { std::string sKey = it->first; Exiv2::XmpKey key(sKey); if ( xmpData_.findKey(key) != xmpData_.end() ) { std::string value_orig(it->second); std::string value_now(xmpData_[sKey].value().toString()); // std::cout << key << " -> " << value_now << " => " << value_orig << std::endl; if ( value_orig.find(value_now.substr(0,10)) != std::string::npos ) { xmpData_[sKey] = value_orig ; } } } if (XmpParser::encode(xmpPacket_, xmpData_, XmpParser::omitPacketWrapper|XmpParser::useCompactFormat) > 1) { #ifndef SUPPRESS_WARNINGS EXV_ERROR << "Failed to encode XMP metadata.\n"; #endif } } if (xmpPacket_.size() > 0) { if (xmpPacket_.substr(0, 5) != "<?xml") { xmpPacket_ = xmlHeader + xmpPacket_ + xmlFooter; } BasicIo::AutoPtr tempIo(io_->temporary()); // may throw assert(tempIo.get() != 0); // Write XMP packet if ( tempIo->write(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size())) != static_cast<long>(xmpPacket_.size())) throw Error(21); if (tempIo->error()) throw Error(21); io_->close(); io_->transfer(*tempIo); // may throw } } // XmpSidecar::writeMetadata
void Jp2Image::doWriteMetadata(BasicIo& outIo) { if (!io_->isopen()) throw Error(20); if (!outIo.isopen()) throw Error(21); #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Writing JPEG-2000 file " << io_->path() << "\n"; std::cout << "Exiv2::Jp2Image::doWriteMetadata: tmp file created " << outIo.path() << "\n"; #endif // Ensure that this is the correct image type if (!isJp2Type(*io_, true)) { if (io_->error() || io_->eof()) throw Error(20); throw Error(22); } // Write JPEG2000 Signature. if (outIo.write(Jp2Signature, 12) != 12) throw Error(21); Jp2BoxHeader box = {0,0}; byte boxDataSize[4]; byte boxUUIDtype[4]; DataBuf bheaderBuf(8); // Box header : 4 bytes (data size) + 4 bytes (box type). // FIXME: Andreas, why the loop do not stop when EOF is taken from _io. The loop go out by an exception // generated by a zero size data read. while(io_->tell() < io_->size()) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Position: " << io_->tell() << " / " << io_->size() << "\n"; #endif // Read chunk header. std::memset(bheaderBuf.pData_, 0x00, bheaderBuf.size_); long bufRead = io_->read(bheaderBuf.pData_, bheaderBuf.size_); if (io_->error()) throw Error(14); if (bufRead != bheaderBuf.size_) throw Error(20); // Decode box header. box.boxLength = getLong(bheaderBuf.pData_, bigEndian); box.boxType = getLong(bheaderBuf.pData_ + 4, bigEndian); #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Find box type: " << bheaderBuf.pData_ + 4 << " lenght: " << box.boxLength << "\n"; #endif if (box.boxLength == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Null Box size has been found. " "This is the last box of file.\n"; #endif box.boxLength = io_->size() - io_->tell() + 8; } if (box.boxLength == 1) { // FIXME. Special case. the real box size is given in another place. } // Read whole box : Box header + Box data (not fixed size - can be null). DataBuf boxBuf(box.boxLength); // Box header (8 bytes) + box data. memcpy(boxBuf.pData_, bheaderBuf.pData_, 8); // Copy header. bufRead = io_->read(boxBuf.pData_ + 8, box.boxLength - 8); // Extract box data. if (io_->error()) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Error reading source file\n"; #endif throw Error(14); } if (bufRead != (long)(box.boxLength - 8)) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Cannot read source file data\n"; #endif throw Error(20); } switch(box.boxType) { case kJp2BoxTypeJp2Header: { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write JP2Header box (lenght: " << box.boxLength << ")\n"; #endif if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21); // Write all updated metadata here, just after JP2Header. if (exifData_.count() > 0) { // Update Exif data to a new UUID box Blob blob; ExifParser::encode(blob, littleEndian, exifData_); if (blob.size()) { const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; DataBuf rawExif(static_cast<long>(sizeof(ExifHeader) + blob.size())); memcpy(rawExif.pData_, ExifHeader, sizeof(ExifHeader)); memcpy(rawExif.pData_ + sizeof(ExifHeader), &blob[0], blob.size()); DataBuf boxData(8 + 16 + rawExif.size_); ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian); ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian); memcpy(boxData.pData_, boxDataSize, 4); memcpy(boxData.pData_ + 4, boxUUIDtype, 4); memcpy(boxData.pData_ + 8, kJp2UuidExif, 16); memcpy(boxData.pData_ + 8 + 16, rawExif.pData_, rawExif.size_); #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Exif metadata (lenght: " << boxData.size_ << ")\n"; #endif if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21); } } if (iptcData_.count() > 0) { // Update Iptc data to a new UUID box DataBuf rawIptc = IptcParser::encode(iptcData_); if (rawIptc.size_ > 0) { DataBuf boxData(8 + 16 + rawIptc.size_); ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian); ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian); memcpy(boxData.pData_, boxDataSize, 4); memcpy(boxData.pData_ + 4, boxUUIDtype, 4); memcpy(boxData.pData_ + 8, kJp2UuidIptc, 16); memcpy(boxData.pData_ + 8 + 16, rawIptc.pData_, rawIptc.size_); #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with Iptc metadata (lenght: " << boxData.size_ << ")\n"; #endif if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21); } } if (writeXmpFromPacket() == false) { if (XmpParser::encode(xmpPacket_, xmpData_) > 1) { #ifndef SUPPRESS_WARNINGS EXV_ERROR << "Failed to encode XMP metadata.\n"; #endif } } if (xmpPacket_.size() > 0) { // Update Xmp data to a new UUID box DataBuf xmp(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size())); DataBuf boxData(8 + 16 + xmp.size_); ul2Data(boxDataSize, boxData.size_, Exiv2::bigEndian); ul2Data(boxUUIDtype, kJp2BoxTypeUuid, Exiv2::bigEndian); memcpy(boxData.pData_, boxDataSize, 4); memcpy(boxData.pData_ + 4, boxUUIDtype, 4); memcpy(boxData.pData_ + 8, kJp2UuidXmp, 16); memcpy(boxData.pData_ + 8 + 16, xmp.pData_, xmp.size_); #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: Write box with XMP metadata (lenght: " << boxData.size_ << ")\n"; #endif if (outIo.write(boxData.pData_, boxData.size_) != boxData.size_) throw Error(21); } break; } case kJp2BoxTypeUuid: { if(memcmp(boxBuf.pData_ + 8, kJp2UuidExif, sizeof(16)) == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Exif Uuid box\n"; #endif } else if(memcmp(boxBuf.pData_ + 8, kJp2UuidIptc, sizeof(16)) == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Iptc Uuid box\n"; #endif } else if(memcmp(boxBuf.pData_ + 8, kJp2UuidXmp, sizeof(16)) == 0) { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: strip Xmp Uuid box\n"; #endif } else { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: write Uuid box (lenght: " << box.boxLength << ")\n"; #endif if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21); } break; } default: { #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: write box (lenght: " << box.boxLength << ")\n"; #endif if (outIo.write(boxBuf.pData_, boxBuf.size_) != boxBuf.size_) throw Error(21); break; } } } #ifdef DEBUG std::cout << "Exiv2::Jp2Image::doWriteMetadata: EOF\n"; #endif } // Jp2Image::doWriteMetadata
void JpegBase::doWriteMetadata(BasicIo& outIo) { if (!io_->isopen()) throw Error(20); if (!outIo.isopen()) throw Error(21); // Ensure that this is the correct image type if (!isThisType(*io_, true)) { if (io_->error() || io_->eof()) throw Error(20); throw Error(22); } const long bufMinSize = 36; long bufRead = 0; DataBuf buf(bufMinSize); const long seek = io_->tell(); int count = 0; int search = 0; int insertPos = 0; int comPos = 0; int skipApp1Exif = -1; int skipApp1Xmp = -1; int skipApp13Ps3 = -1; int skipCom = -1; DataBuf psData; DataBuf rawExif; // Write image header if (writeHeader(outIo)) throw Error(21); // Read section marker int marker = advanceToMarker(); if (marker < 0) throw Error(22); // 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 < 5) { // Read size and signature (ok if this hits EOF) bufRead = io_->read(buf.pData_, bufMinSize); if (io_->error()) throw Error(20); uint16_t size = getUShort(buf.pData_, bigEndian); if (marker == app0_) { if (size < 2) throw Error(22); insertPos = count + 1; if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); } else if ( skipApp1Exif == -1 && marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) { if (size < 8) throw Error(22); skipApp1Exif = count; ++search; // Seek to beginning and read the current Exif data io_->seek(8 - bufRead, BasicIo::cur); rawExif.alloc(size - 8); io_->read(rawExif.pData_, rawExif.size_); if (io_->error() || io_->eof()) throw Error(22); } else if (marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) { if (size < 31) throw Error(22); skipApp1Xmp = count; ++search; if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); } else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) { #ifdef DEBUG std::cerr << "Found APP13 Photoshop PS3 segment\n"; #endif if (size < 16) throw Error(22); skipApp13Ps3 = count; ++search; io_->seek(16 - bufRead, BasicIo::cur); psData.alloc(size - 16); // Load PS data now to allow reinsertion at any point io_->read(psData.pData_, size - 16); if (io_->error() || io_->eof()) throw Error(20); } else if (marker == com_ && skipCom == -1) { if (size < 2) throw Error(22); // Jpegs can have multiple comments, but for now only handle // the first one (most jpegs only have one anyway). skipCom = count; ++search; if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); } else { if (size < 2) throw Error(22); if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22); } // As in jpeg-6b/wrjpgcom.c: // We will insert the new comment marker just before SOFn. // This (a) causes the new comment to appear after, rather than before, // existing comments; and (b) ensures that comments come after any JFIF // or JFXX markers, as required by the JFIF specification. if ( comPos == 0 && ( marker == sof0_ || marker == sof1_ || marker == sof2_ || marker == sof3_ || marker == sof5_ || marker == sof6_ || marker == sof7_ || marker == sof9_ || marker == sof10_ || marker == sof11_ || marker == sof13_ || marker == sof14_ || marker == sof15_)) { comPos = count; ++search; } marker = advanceToMarker(); if (marker < 0) throw Error(22); ++count; } if (comPos == 0) { if (marker == eoi_) comPos = count; else comPos = insertPos; ++search; } if (exifData_.count() > 0) ++search; if (writeXmpFromPacket() == false && xmpData_.count() > 0) ++search; if (writeXmpFromPacket() == true && xmpPacket_.size() > 0) ++search; if (iptcData_.count() > 0) ++search; if (!comment_.empty()) ++search; io_->seek(seek, BasicIo::beg); count = 0; marker = advanceToMarker(); if (marker < 0) throw Error(22); // 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 = io_->read(buf.pData_, bufMinSize); if (io_->error()) throw Error(20); // Careful, this can be a meaningless number for empty // images with only an eoi_ marker uint16_t size = getUShort(buf.pData_, bigEndian); if (insertPos == count) { byte tmpBuf[64]; // Write Exif data first so that - if there is no app0 - we // create "Exif images" according to the Exif standard. if (exifData_.count() > 0) { Blob blob; ByteOrder bo = byteOrder(); if (bo == invalidByteOrder) { bo = littleEndian; setByteOrder(bo); } WriteMethod wm = ExifParser::encode(blob, rawExif.pData_, rawExif.size_, bo, exifData_); const byte* pExifData = rawExif.pData_; uint32_t exifSize = rawExif.size_; if (wm == wmIntrusive) { pExifData = blob.size() > 0 ? &blob[0] : 0; exifSize = static_cast<uint32_t>(blob.size()); } if (exifSize > 0) { // Write APP1 marker, size of APP1 field, Exif id and Exif data tmpBuf[0] = 0xff; tmpBuf[1] = app1_; if (exifSize + 8 > 0xffff) throw Error(37, "Exif"); us2Data(tmpBuf + 2, static_cast<uint16_t>(exifSize + 8), bigEndian); std::memcpy(tmpBuf + 4, exifId_, 6); if (outIo.write(tmpBuf, 10) != 10) throw Error(21); // Write new Exif data buffer if ( outIo.write(pExifData, exifSize) != static_cast<long>(exifSize)) throw Error(21); if (outIo.error()) throw Error(21); --search; } } 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) { // Write APP1 marker, size of APP1 field, XMP id and XMP packet tmpBuf[0] = 0xff; tmpBuf[1] = app1_; if (xmpPacket_.size() + 31 > 0xffff) throw Error(37, "XMP"); us2Data(tmpBuf + 2, static_cast<uint16_t>(xmpPacket_.size() + 31), bigEndian); std::memcpy(tmpBuf + 4, xmpId_, 29); if (outIo.write(tmpBuf, 33) != 33) throw Error(21); // Write new XMP packet if ( outIo.write(reinterpret_cast<const byte*>(xmpPacket_.data()), static_cast<long>(xmpPacket_.size())) != static_cast<long>(xmpPacket_.size())) throw Error(21); if (outIo.error()) throw Error(21); --search; } if (psData.size_ > 0 || iptcData_.count() > 0) { // Set the new IPTC IRB, keeps existing IRBs but removes the // IPTC block if there is no new IPTC data to write DataBuf newPsData = Photoshop::setIptcIrb(psData.pData_, psData.size_, iptcData_); if (newPsData.size_ > 0) { // Write APP13 marker, new size, and ps3Id tmpBuf[0] = 0xff; tmpBuf[1] = app13_; if (newPsData.size_ + 16 > 0xffff) throw Error(37, "IPTC"); us2Data(tmpBuf + 2, static_cast<uint16_t>(newPsData.size_ + 16), bigEndian); std::memcpy(tmpBuf + 4, Photoshop::ps3Id_, 14); if (outIo.write(tmpBuf, 18) != 18) throw Error(21); if (outIo.error()) throw Error(21); // Write new Photoshop IRB data buffer if ( outIo.write(newPsData.pData_, newPsData.size_) != newPsData.size_) throw Error(21); if (outIo.error()) throw Error(21); } if (iptcData_.count() > 0) { --search; } } } if (comPos == count) { if (!comment_.empty()) { byte tmpBuf[4]; // Write COM marker, size of comment, and string tmpBuf[0] = 0xff; tmpBuf[1] = com_; if (comment_.length() + 3 > 0xffff) throw Error(37, "JPEG comment"); us2Data(tmpBuf + 2, static_cast<uint16_t>(comment_.length() + 3), bigEndian); if (outIo.write(tmpBuf, 4) != 4) throw Error(21); if (outIo.write((byte*)comment_.data(), (long)comment_.length()) != (long)comment_.length()) throw Error(21); if (outIo.putb(0)==EOF) throw Error(21); if (outIo.error()) throw Error(21); --search; } --search; } if (marker == eoi_) { break; } else if ( skipApp1Exif == count || skipApp1Xmp == count || skipApp13Ps3 == count || skipCom == count) { --search; io_->seek(size-bufRead, BasicIo::cur); } else { if (size < 2) throw Error(22); buf.alloc(size+2); io_->seek(-bufRead-2, BasicIo::cur); io_->read(buf.pData_, size+2); if (io_->error() || io_->eof()) throw Error(20); if (outIo.write(buf.pData_, size+2) != size+2) throw Error(21); if (outIo.error()) throw Error(21); } // Next marker marker = advanceToMarker(); if (marker < 0) throw Error(22); ++count; } // Copy rest of the Io io_->seek(-2, BasicIo::cur); buf.alloc(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); } // JpegBase::doWriteMetadata
void Image::setXmpData(const XmpData& xmpData) { xmpData_ = xmpData; writeXmpFromPacket(false); }
void Image::clearXmpData() { xmpData_.clear(); writeXmpFromPacket(false); }
void Image::setXmpPacket(const std::string& xmpPacket) { xmpPacket_ = xmpPacket; writeXmpFromPacket(true); }
void Image::clearXmpPacket() { xmpPacket_.clear(); writeXmpFromPacket(true); }